UPnPsdk 0.1
Universal Plug and Play +, Software Development Kit
 
Loading...
Searching...
No Matches
webserver.cpp
Go to the documentation of this file.
1/**************************************************************************
2 *
3 * Copyright (c) 2000-2003 Intel Corporation
4 * All rights reserved.
5 * Copyright (c) 2012 France Telecom All rights reserved.
6 * Copyright (C) 2022+ GPL 3 and higher by Ingo Höft, <Ingo@Hoeft-online.de>
7 * Redistribution only with this Copyright remark. Last modified: 2025-05-18
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * - Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 * - Neither name of Intel Corporation nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
29 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 **************************************************************************/
34// Last compare with ./Pupnp source file on 2024-10-26, ver 1.14.20
40#include <webserver.hpp>
41#include <UpnpExtraHeaders.hpp>
42#include <UpnpIntTypes.hpp>
43#include <httpreadwrite.hpp>
44#include <ssdp_common.hpp>
45#include <statcodes.hpp>
46#include <upnpapi.hpp>
47
48#include <UPnPsdk/synclog.hpp>
49#include <UPnPsdk/port.hpp>
50#include <UPnPsdk/webserver.hpp>
51
52#include <umock/stdlib.hpp>
53#include <umock/stdio.hpp>
54
55#ifndef COMPA_INTERNAL_CONFIG_HPP
56#error "No or wrong config.hpp header file included."
57#endif
58
59#ifndef COMPA_NET_HTTP_WEBSERVER_HPP
60#error "No or wrong webserver.hpp header file included."
61#endif
62
64#include <cassert>
65#include <sys/stat.h>
66#include <iostream>
68
69
70namespace compa {
71namespace {
72
75 RESP_UNSPEC,
76 RESP_FILEDOC,
77 RESP_XMLDOC,
78 RESP_HEADERS,
79 RESP_WEBDOC,
80 RESP_POST
81};
82
84const char* gMediaTypes[] = {
85 NULL,
86 "audio",
87 "video",
88 "image",
89 "application",
90 "text"
91};
92
94constexpr size_t APPLICATION_INDEX{4};
95
97constexpr size_t ASCTIME_R_BUFFER_SIZE{26};
98
100struct CXmlAlias {
101 public:
103 // Constructor
104 // -----------
105 CXmlAlias(){TRACE2(this, " Construct CXmlAlias()")}
106
108 // ------------------------
109 CXmlAlias(const CXmlAlias& that) {
110 TRACE2(this, " Executing CXmlAlias copy constructor()")
111
112 ::pthread_mutex_lock(&m_web_mutex);
113 this->m_name = that.m_name;
114 this->m_last_modified = that.m_last_modified;
115 if (that.m_doc.data() == nullptr) {
116 this->m_ct = that.m_ct;
117 this->m_doc = that.m_doc;
118 } else {
119 this->m_ct = 1;
120 const size_t doc_len{that.m_doc.length()};
121 char* doc = static_cast<char*>(malloc(doc_len));
122 memcpy(doc, that.m_doc.data(), doc_len);
123 this->m_doc = std::string_view(doc, doc_len);
124 }
125 ::pthread_mutex_unlock(&m_web_mutex);
126 }
127
129 // --------------------------------
130 CXmlAlias& operator=(CXmlAlias that) {
131 TRACE2(this, " Executing CXmlAlias assignment operator=")
132 // The copy constructor has alread done its work to copy the current
133 // object to the stack. No need to protect with a mutex. This swapping
134 // from the stack is thread safe.
135 std::swap(this->m_name, that.m_name);
136 std::swap(this->m_doc, that.m_doc);
137 std::swap(this->m_last_modified, that.m_last_modified);
138 std::swap(this->m_ct, that.m_ct); // Set by the copy constructor.
139
140 // by convention, always return *this
141 return *this;
142 }
143
144 // Destructor
145 // ----------
146 ~CXmlAlias() {
147 TRACE2(this, " Destruct CXmlAlias()")
148 this->clear_private();
149 if (::pthread_mutex_destroy(&m_web_mutex) != 0)
150 UPnPsdk_LOGCRIT(
151 "MSG1150") "fails with EBUSY. The mutex is currently locked.\n";
152 }
154
164 int set(const char* a_alias_name, const char*& a_alias_content,
165 size_t& a_alias_content_length,
166 time_t a_last_modified = time(nullptr)) {
167 TRACE2(this, " Executing CXmlAlias::set()")
168 ::pthread_mutex_lock(&m_web_mutex);
169
170 if (a_alias_name == nullptr) {
171 /* don't serve aliased doc anymore */
172 this->release_private();
173 ::pthread_mutex_unlock(&m_web_mutex);
174 return UPNP_E_SUCCESS;
175 }
176 if (a_alias_content == nullptr) {
177 ::pthread_mutex_unlock(&m_web_mutex);
179 }
180 this->release_private();
181
182 /* insert leading /, if missing */
183 if (*a_alias_name != '/')
184 m_name = '/';
185 m_name += a_alias_name;
186 UPnPsdk_LOGINFO(
187 "MSG1133") "attaching allocated document, take ownership.\n";
188 m_doc = std::string_view(a_alias_content, a_alias_content_length);
189 m_last_modified = a_last_modified;
190 a_alias_content = nullptr;
191 a_alias_content_length = 0;
192 m_ct = 1;
193
194 ::pthread_mutex_unlock(&m_web_mutex);
195 return UPNP_E_SUCCESS;
196 }
197
199 // -----------------------------------------------------------------
200 std::string_view name() const {
201 TRACE2(this, " Executing CXmlAlias::name()")
202 ::pthread_mutex_lock(&m_web_mutex);
203 std::string_view name = m_name;
204 ::pthread_mutex_unlock(&m_web_mutex);
205 return name;
206 }
207
209 // -----------------------------------------------------
210 std::string_view doc() const {
211 TRACE2(this, " Executing CXmlAlias::doc()")
212 ::pthread_mutex_lock(&m_web_mutex);
213 std::string_view doc = m_doc;
214 ::pthread_mutex_unlock(&m_web_mutex);
215 return doc;
216 }
217
220 // ------------------------------------------------------------------
221 time_t last_modified() const {
222 TRACE2(this, " Executing CXmlAlias::last_modified()")
223 ::pthread_mutex_lock(&m_web_mutex);
224 auto last_modified = m_last_modified;
225 ::pthread_mutex_unlock(&m_web_mutex);
226 return last_modified;
227 }
228
230 // ---------------------------------------------------------------
231 bool is_valid() const {
232 TRACE2(this, " Executing CXmlAlias::is_valid()")
233 ::pthread_mutex_lock(&m_web_mutex);
234 auto doc = m_doc.data();
235 ::pthread_mutex_unlock(&m_web_mutex);
236 return doc != nullptr;
237 }
238
240 // ---------------------------------------------------
241 void release() {
242 TRACE2(this, " Executing CXmlAlias::release()")
243 ::pthread_mutex_lock(&m_web_mutex);
244 this->release_private();
245 ::pthread_mutex_unlock(&m_web_mutex);
246 }
247
249 // ----------------------------
250 void clear() {
251 TRACE2(this, " Executing CXmlAlias::clear()")
252 ::pthread_mutex_lock(&m_web_mutex);
253 this->clear_private();
254 ::pthread_mutex_unlock(&m_web_mutex);
255 }
256
257 private:
259 mutable ::pthread_mutex_t m_web_mutex = PTHREAD_MUTEX_INITIALIZER;
260
262 std::string m_name;
263
265 std::string_view m_doc{std::string_view(nullptr, 0)};
266
268 time_t m_last_modified{};
269
271 // Counter how often the document is requested and will be decremented with
272 // this->release(). Only if m_cnt == 0 then this->clear_private() is called.
273 int m_ct{};
275
276
278 // ----------------------------------------------------------------------
279 inline void release_private() {
280 /* ignore invalid alias */
281 if (m_ct > 0) {
282 m_ct--;
283 if (m_ct <= 0)
284 this->clear_private();
285 }
286 }
287
289 // -----------------------------------------------
290 inline void clear_private() {
291 m_name.clear();
292 // std::string_view 'm_doc' was created from an (external) allocated
293 // raw pointer that ownership has been overtaken with this. To be able
294 // to free this raw pointer the const protection of the
295 // std::string_view must be removed.
296 if (m_doc.data() != nullptr) {
297 UPnPsdk_LOGINFO("MSG1118") "freeing attached allocated document.\n";
298 ::free(const_cast<char*>(m_doc.data()));
299 m_doc = std::string_view(nullptr, 0);
300 }
301 m_last_modified = 0;
302 m_ct = 0;
303 }
304};
305
308
309
314#if defined(_WIN32) || defined(DOXYGEN_RUN)
318char* web_server_asctime_r(const struct tm* tm, char* buf) {
319 if (tm == NULL || buf == NULL)
320 return NULL;
321
322 asctime_s(buf, ASCTIME_R_BUFFER_SIZE, tm);
323 return buf;
324}
325#else
326#define web_server_asctime_r asctime_r
327#endif
328
346inline int search_extension( //
347 const char* a_extension,
348 const char** a_con_type,
349 const char** a_con_subtype
350) {
351 TRACE("Executing search_extension()")
352
353 const UPnPsdk::Document_meta* filetype =
354 UPnPsdk::select_filetype(std::string_view(a_extension));
355
356 if (filetype == nullptr) {
357 TRACE(" search_extension: return with -1")
358 return -1;
359 }
360 *a_con_type = filetype->type.c_str();
361 *a_con_subtype = filetype->subtype.c_str();
362
363 return 0;
364}
365
380 const char* filename,
381 UpnpFileInfo* fileInfo
382) {
383 if (!filename)
385 if (!fileInfo)
387
388 const char* extension;
389 const char* type{};
390 const char* subtype{};
391 int ctype_found = 0;
392 char* temp = NULL;
393 size_t length = 0;
394 int rc = 0;
395
396 UpnpFileInfo_set_ContentType(fileInfo, NULL);
397 /* get ext */
398 extension = strrchr(filename, '.');
399 if (extension != NULL) {
400 if (search_extension(extension + 1, &type, &subtype) == 0)
401 ctype_found = 1;
402 }
403 if (!ctype_found) {
404 /* unknown content type */
406 subtype = "octet-stream";
407 }
408 length = strlen(type) + strlen("/") + strlen(subtype) + 1;
409 temp = (char*)malloc(length);
410 if (!temp)
411 return UPNP_E_OUTOF_MEMORY;
412 rc = snprintf(temp, length, "%s/%s", type, subtype);
413 if (rc < 0 || (unsigned int)rc >= length) {
414 umock::stdlib_h.free(temp);
415 return UPNP_E_OUTOF_MEMORY;
416 }
417 UpnpFileInfo_set_ContentType(fileInfo, temp);
418 umock::stdlib_h.free(temp);
419 if (!UpnpFileInfo_get_ContentType(fileInfo))
420 return UPNP_E_OUTOF_MEMORY;
421
422 return 0;
423}
424
432 const char* filename,
436 UpnpFileInfo* info) {
437 int code;
438 struct stat s;
439 FILE* fp;
440 int fd;
441 int rc = 0;
442 time_t aux_LastModified;
443 struct tm date;
444 char buffer[ASCTIME_R_BUFFER_SIZE];
445
447#ifdef _WIN32
448 umock::stdio_h.fopen_s(&fp, filename, "r");
449#else
450 fp = umock::stdio_h.fopen(filename, "r");
451#endif
452 /* check readable */
453 UpnpFileInfo_set_IsReadable(info, fp != NULL);
454 if (!fp) {
455 rc = -1;
456 goto exit_function;
457 }
458 fd = fileno(fp);
459 if (fd == -1) {
460 rc = -1;
461 goto exit_function;
462 }
463 code = fstat(fd, &s);
464 if (code == -1) {
465 rc = -1;
466 goto exit_function;
467 }
468 umock::stdio_h.fclose(fp);
469 fp = NULL;
470 if (S_ISDIR(s.st_mode)) {
472 } else if (S_ISREG(s.st_mode)) {
474 } else {
475 rc = -1;
476 goto exit_function;
477 }
478 UpnpFileInfo_set_FileLength(info, s.st_size);
479 UpnpFileInfo_set_LastModified(info, s.st_mtime);
480 rc = get_content_type(filename, info);
481 aux_LastModified = UpnpFileInfo_get_LastModified(info);
482 UPnPsdk_LOGINFO("MSG1108") "webserver file info=\""
483 << filename << "\", length=" << UpnpFileInfo_get_FileLength(info)
484 << ", last_mod=\""
485 << web_server_asctime_r(http_gmtime_r(&aux_LastModified, &date), buffer)
486 << "\", readable=" << UpnpFileInfo_get_IsReadable(info) << ".\n";
487
488exit_function:
489 if (fp) {
490 umock::stdio_h.fclose(fp);
491 }
492 return rc;
493}
494
505inline int get_alias(
507 const char* request_file,
509 CXmlAlias* alias,
512 UpnpFileInfo* info) {
513 int cmp = strcmp(alias->name().data(), request_file);
514 if (cmp == 0) {
516 static_cast<off_t>(alias->doc().length()));
520 }
521
522 return cmp == 0;
523}
524
533 char* filePath,
536 const void** cookie) {
537 virtualDirList* pCurVirtualDir;
538 size_t webDirLen;
539
540 pCurVirtualDir = pVirtualDirList;
541 while (pCurVirtualDir != NULL) {
542 webDirLen = strlen(pCurVirtualDir->dirName);
543 if (webDirLen) {
544 if (pCurVirtualDir->dirName[webDirLen - 1] == '/') {
545 if (strncmp(pCurVirtualDir->dirName, filePath, webDirLen) ==
546 0) {
547 if (cookie != NULL)
548 *cookie = pCurVirtualDir->cookie;
549 return 1;
550 }
551 } else {
552 if (strncmp(pCurVirtualDir->dirName, filePath, webDirLen) ==
553 0 &&
554 (filePath[webDirLen] == '/' ||
555 filePath[webDirLen] == '\0' ||
556 filePath[webDirLen] == '?')) {
557 if (cookie != NULL)
558 *cookie = pCurVirtualDir->cookie;
559 return 1;
560 }
561 }
562 }
563 pCurVirtualDir = pCurVirtualDir->next;
564 }
565
566 return 0;
567}
568
574 char* s) {
575 while (*s) {
576 *s = static_cast<char>(std::toupper(static_cast<unsigned char>(*s)));
577 ++s;
578 }
579}
580
586char* StrStr(
588 char* s1,
590 const char* s2) {
591 char* Str1;
592 char* Str2;
593 const char* Ptr;
594 char* ret = nullptr;
595
596 Str1 = strdup(s1);
597 if (!Str1)
598 goto error1;
599 Str2 = strdup(s2);
600 if (!Str2)
601 goto error2;
602 ToUpperCase(Str1);
603 ToUpperCase(Str2);
604 Ptr = strstr(Str1, Str2);
605 if (!Ptr)
606 ret = NULL;
607 else
608 ret = s1 + (Ptr - Str1);
609
610 umock::stdlib_h.free(Str2);
611error2:
612 umock::stdlib_h.free(Str1);
613error1:
614 return ret;
615}
616
622char* StrTok(
624 char** Src,
626 const char* Del) {
627 char* TmpPtr;
628 char* RetPtr;
629
630 if (*Src != NULL) {
631 RetPtr = *Src;
632 TmpPtr = strstr(*Src, Del);
633 if (TmpPtr != NULL) {
634 *TmpPtr = '\0';
635 *Src = TmpPtr + strlen(Del);
636 } else
637 *Src = NULL;
638
639 return RetPtr;
640 }
641
642 return NULL;
643}
644
652 char** SrcRangeStr,
654 off_t* FirstByte,
656 off_t* LastByte) {
657 char* Ptr;
658 char* Tok;
659 int i;
660 int64_t F = -1;
661 int64_t L = -1;
662 int Is_Suffix_byte_Range = 1;
663
664 if (*SrcRangeStr == NULL)
665 return -1;
666 Tok = StrTok(SrcRangeStr, ",");
667 if ((Ptr = strstr(Tok, "-")) == NULL)
668 return -1;
669 *Ptr = ' ';
670#ifdef _WIN32
671 sscanf_s(Tok, "%" SCNd64 "%" SCNd64, &F, &L);
672#else
673 sscanf(Tok, "%" SCNd64 "%" SCNd64, &F, &L);
674#endif
675 if (F == -1 || L == -1) {
676 *Ptr = '-';
677 for (i = 0; i < (int)strlen(Tok); i++) {
678 if (Tok[i] == '-') {
679 break;
680 } else if (isdigit(Tok[i])) {
681 Is_Suffix_byte_Range = 0;
682 break;
683 }
684 }
685 if (Is_Suffix_byte_Range) {
686 *FirstByte = (off_t)L;
687 *LastByte = (off_t)F;
688 return 1;
689 }
690 }
691 *FirstByte = (off_t)F;
692 *LastByte = (off_t)L;
693
694 return 1;
695}
696
709 char* ByteRangeSpecifier,
711 off_t FileLength,
714 struct SendInstruction* Instr) {
715 off_t FirstByte, LastByte;
716 char* RangeInput;
717 char* Ptr;
718 int rc = 0;
719
720 Instr->IsRangeActive = 1;
721 Instr->ReadSendSize = FileLength;
722 if (!ByteRangeSpecifier)
723 return HTTP_BAD_REQUEST;
724 RangeInput = strdup(ByteRangeSpecifier);
725 if (!RangeInput)
726 return HTTP_INTERNAL_SERVER_ERROR;
727 /* CONTENT-RANGE: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
728 if (StrStr(RangeInput, "bytes") == NULL ||
729 (Ptr = StrStr(RangeInput, "=")) == NULL) {
730 umock::stdlib_h.free(RangeInput);
731 Instr->IsRangeActive = 0;
732 return HTTP_BAD_REQUEST;
733 }
734 /* Jump = */
735 Ptr = Ptr + 1;
736 if (FileLength < 0) {
737 int ret = HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
738 if ((*Ptr == '0') && (*(Ptr + 1) == '-') && (*(Ptr + 2) == '\0')) {
739 Instr->IsRangeActive = 0;
740 ret = HTTP_OK;
741 }
742 umock::stdlib_h.free(RangeInput);
743 return ret;
744 }
745 if (GetNextRange(&Ptr, &FirstByte, &LastByte) != -1) {
746 if (FileLength < FirstByte) {
747 umock::stdlib_h.free(RangeInput);
748 return HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
749 }
750 if (FirstByte >= 0 && LastByte >= 0 && LastByte >= FirstByte) {
751 if (LastByte >= FileLength)
752 LastByte = FileLength - 1;
753 Instr->RangeOffset = FirstByte;
754 Instr->ReadSendSize = LastByte - FirstByte + 1;
755 /* Data between two range. */
756 rc = snprintf(
757 Instr->RangeHeader, sizeof(Instr->RangeHeader),
758 "CONTENT-RANGE: bytes %" PRId64 "-%" PRId64 "/%" PRId64 "\r\n",
759 (int64_t)FirstByte, (int64_t)LastByte, (int64_t)FileLength);
760 if (rc < 0 || (unsigned int)rc >= sizeof(Instr->RangeHeader)) {
761 umock::stdlib_h.free(RangeInput);
762 return HTTP_INTERNAL_SERVER_ERROR;
763 }
764 } else if (FirstByte >= 0 && LastByte == -1 && FirstByte < FileLength) {
765 Instr->RangeOffset = FirstByte;
766 Instr->ReadSendSize = FileLength - FirstByte;
767 rc = snprintf(Instr->RangeHeader, sizeof(Instr->RangeHeader),
768 "CONTENT-RANGE: bytes %" PRId64 "-%" PRId64
769 "/%" PRId64 "\r\n",
770 (int64_t)FirstByte, (int64_t)(FileLength - 1),
771 (int64_t)FileLength);
772 if (rc < 0 || (unsigned int)rc >= sizeof(Instr->RangeHeader)) {
773 umock::stdlib_h.free(RangeInput);
774 return HTTP_INTERNAL_SERVER_ERROR;
775 }
776 } else if (FirstByte == -1 && LastByte > 0) {
777 if (LastByte >= FileLength) {
778 Instr->RangeOffset = 0;
779 Instr->ReadSendSize = FileLength;
780 rc = snprintf(Instr->RangeHeader, sizeof(Instr->RangeHeader),
781 "CONTENT-RANGE: bytes 0-%" PRId64 "/%" PRId64
782 "\r\n",
783 (int64_t)(FileLength - 1), (int64_t)FileLength);
784 } else {
785 Instr->RangeOffset = FileLength - LastByte;
786 Instr->ReadSendSize = LastByte;
787 rc = snprintf(Instr->RangeHeader, sizeof(Instr->RangeHeader),
788 "CONTENT-RANGE: bytes %" PRId64 "-%" PRId64
789 "/%" PRId64 "\r\n",
790 (int64_t)(FileLength - LastByte),
791 (int64_t)FileLength - 1, (int64_t)FileLength);
792 }
793 if (rc < 0 || (unsigned int)rc >= sizeof(Instr->RangeHeader)) {
794 umock::stdlib_h.free(RangeInput);
795 return HTTP_INTERNAL_SERVER_ERROR;
796 }
797 } else {
798 umock::stdlib_h.free(RangeInput);
799 return HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
800 }
801 } else {
802 umock::stdlib_h.free(RangeInput);
803 return HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
804 }
805
806 umock::stdlib_h.free(RangeInput);
807 return HTTP_OK;
808}
809
824 http_message_t* Req,
826 struct SendInstruction* RespInstr,
828 off_t FileSize) {
829 http_header_t* header;
830 ListNode* node;
831 /*NNS: dlist_node* node; */
832 size_t index;
833 int RetCode = HTTP_OK;
834 char* TmpBuf;
835 size_t TmpBufSize = LINE_SIZE;
836
837 TmpBuf = (char*)malloc(TmpBufSize);
838 if (!TmpBuf)
839 return HTTP_INTERNAL_SERVER_ERROR;
840 node = ListHead(&Req->headers);
841 while (node != NULL) {
842 header = (http_header_t*)node->item;
843 /* find header type. */
844#if 0
845 index =
846 map_str_to_int((const char*)header->name.buf, header->name.length,
847 &Http_Header_Names[0], Http_Header_Names.size(), 0);
848#endif
849 UPnPsdk::CStrIntMap http_header_names_table(Http_Header_Names);
850 index = http_header_names_table.index_of(header->name.buf);
851
852 if (header->value.length >= TmpBufSize) {
853 umock::stdlib_h.free(TmpBuf);
854 TmpBufSize = header->value.length + 1;
855 TmpBuf = (char*)malloc(TmpBufSize);
856 if (!TmpBuf)
857 return HTTP_INTERNAL_SERVER_ERROR;
858 }
859 memcpy(TmpBuf, header->value.buf, header->value.length);
860 TmpBuf[header->value.length] = '\0';
861 if (index != http_header_names_table.npos) {
862 switch (Http_Header_Names[index].id) {
863 case HDR_TE: {
864 /* Request */
865 RespInstr->IsChunkActive = 1;
866
867 if (strlen(TmpBuf) > strlen("gzip")) {
868 /* means client will accept trailer. */
869 if (StrStr(TmpBuf, "trailers") != NULL) {
870 RespInstr->IsTrailers = 1;
871 }
872 }
873 break;
874 }
876 RespInstr->RecvWriteSize = atoi(TmpBuf);
877 break;
878 case HDR_RANGE:
879 RetCode =
880 CreateHTTPRangeResponseHeader(TmpBuf, FileSize, RespInstr);
881 if (RetCode != HTTP_OK) {
882 umock::stdlib_h.free(TmpBuf);
883 return RetCode;
884 }
885 break;
887 if (header->value.length + 1 >
888 sizeof(RespInstr->AcceptLanguageHeader)) {
889 size_t length = sizeof(RespInstr->AcceptLanguageHeader) - 1;
890 memcpy(RespInstr->AcceptLanguageHeader, TmpBuf, length);
891 RespInstr->AcceptLanguageHeader[length] = '\0';
892 } else {
893 memcpy(RespInstr->AcceptLanguageHeader, TmpBuf,
894 header->value.length + 1);
895 }
896 break;
897 default:
898 /*
899 TODO
900 */
901 /*
902 header.value is the value.
903 */
904 /*
905 case HDR_CONTENT_TYPE: return 1;
906 case HDR_CONTENT_LANGUAGE:return 1;
907 case HDR_LOCATION: return 1;
908 case HDR_CONTENT_LOCATION:return 1;
909 case HDR_ACCEPT: return 1;
910 case HDR_ACCEPT_CHARSET: return 1;
911 case HDR_USER_AGENT: return 1;
912 */
913
914 /*Header check for encoding */
915 /*
916 case HDR_ACCEPT_RANGE:
917 case HDR_CONTENT_RANGE:
918 case HDR_IF_RANGE:
919 */
920
921 /*Header check for encoding */
922 /*
923 case HDR_ACCEPT_ENCODING:
924 if(StrStr(TmpBuf, "identity"))
925 {
926 break;
927 }
928 else return -1;
929 case HDR_CONTENT_ENCODING:
930 case HDR_TRANSFER_ENCODING:
931 */
932 break;
933 }
934 }
935 node = ListNext(&Req->headers, node);
936 }
937 umock::stdlib_h.free(TmpBuf);
938
939 return RetCode;
940}
941
947 [[maybe_unused]] UpnpListHead* extraHeadersList) {
948 UpnpListIter pos;
949 UpnpExtraHeaders* extra;
950
951 for (pos = UpnpListBegin(extraHeadersList);
952 pos != UpnpListEnd(extraHeadersList);) {
953 extra = (UpnpExtraHeaders*)pos;
954 pos = UpnpListErase(extraHeadersList, pos);
956 }
957}
958
968 [[maybe_unused]] http_message_t* Req,
970 [[maybe_unused]] UpnpListHead* extraHeadersList) {
971 http_header_t* header;
972 ListNode* node;
973 size_t index;
974 UpnpExtraHeaders* extraHeader;
975 UpnpListHead* extraHeaderNode;
976
977 node = ListHead(&Req->headers);
978 while (node != NULL) {
979 header = (http_header_t*)node->item;
980 /* find header type. */
981#if 0
982 index = map_str_to_int((const char*)header->name.buf,
983 header->name.length, &Http_Header_Names[0],
984 Http_Header_Names.size(), 0);
985#endif
986 UPnPsdk::CStrIntMap http_header_names_table(Http_Header_Names);
987 index = http_header_names_table.index_of(header->name.buf);
988
989 if (index == http_header_names_table.npos) {
990 extraHeader = UpnpExtraHeaders_new();
991 if (!extraHeader) {
992 FreeExtraHTTPHeaders(extraHeadersList);
993 return HTTP_INTERNAL_SERVER_ERROR;
994 }
995 extraHeaderNode =
997 UpnpListInsert(extraHeadersList, UpnpListEnd(extraHeadersList),
998 extraHeaderNode);
999 UpnpExtraHeaders_strncpy_name(extraHeader, header->name.buf,
1000 header->name.length);
1001 UpnpExtraHeaders_strncpy_value(extraHeader, header->value.buf,
1002 header->value.length);
1003 }
1004 node = ListNext(&Req->headers, node);
1005 }
1006
1007 return HTTP_OK;
1008}
1009
1027 SOCKINFO* info,
1029 http_message_t* req,
1031 enum resp_type* rtype,
1033 membuffer* headers,
1035 membuffer* filename,
1037 CXmlAlias* a_alias,
1039 SendInstruction* RespInstr) {
1040 int code;
1041 int err_code;
1042
1043 char* request_doc{nullptr};
1044 UpnpFileInfo* finfo;
1045 time_t aux_LastModified;
1046 bool alias_grabbed{false};
1047 int using_alias{0};
1048 int using_virtual_dir{0};
1049 uri_type* url;
1050 const char* temp_str;
1051 int resp_major;
1052 int resp_minor;
1053 size_t dummy;
1054 memptr hdr_value;
1055
1056 url = &req->uri;
1057 assert(req->method == HTTPMETHOD_GET || req->method == HTTPMETHOD_HEAD ||
1058 req->method == HTTPMETHOD_POST ||
1059 req->method == HTTPMETHOD_SIMPLEGET);
1060 /* init */
1061 memset(&finfo, 0, sizeof(finfo));
1062 finfo = UpnpFileInfo_new();
1063 err_code = HTTP_INTERNAL_SERVER_ERROR; /* default error */
1064
1066 &resp_major, &resp_minor);
1067 /* */
1068 /* remove dots */
1069 /* */
1070 request_doc = (char*)malloc(url->pathquery.size + 1);
1071 if (request_doc == NULL) {
1072 goto error_handler; /* out of mem */
1073 }
1074 memcpy(request_doc, url->pathquery.buff, url->pathquery.size);
1075 request_doc[url->pathquery.size] = '\0';
1076 dummy = url->pathquery.size;
1077 remove_escaped_chars(request_doc, &dummy);
1078 code = remove_dots(request_doc, url->pathquery.size);
1079 if (code != 0) {
1080 err_code = HTTP_FORBIDDEN;
1081 goto error_handler;
1082 }
1083 if (*request_doc != '/') {
1084 /* no slash */
1085 err_code = HTTP_BAD_REQUEST;
1086 goto error_handler;
1087 }
1088 if (isFileInVirtualDir(request_doc, &RespInstr->Cookie)) {
1089 using_virtual_dir = 1;
1090 RespInstr->IsVirtualFile = 1;
1091 if (membuffer_assign_str(filename, request_doc) != 0) {
1092 goto error_handler;
1093 }
1094 } else {
1095 /* try using alias */
1096 if (a_alias != nullptr && gAliasDoc.is_valid()) {
1097 *a_alias = gAliasDoc;
1098 alias_grabbed = true;
1099 using_alias = get_alias(request_doc, a_alias, finfo);
1100 if (using_alias == 1) {
1102 "text/xml; charset=\"utf-8\"");
1103 if (UpnpFileInfo_get_ContentType(finfo) == NULL) {
1104 goto error_handler;
1105 }
1106 }
1107 }
1108 }
1109 if (using_virtual_dir) {
1110 if (req->method != HTTPMETHOD_POST) {
1111 if ((code = ExtraHTTPHeaders(
1113 finfo))) != HTTP_OK) {
1114 err_code = code;
1115 goto error_handler;
1116 }
1117
1119
1120 if (httpmsg_find_hdr(req, HDR_USER_AGENT, &hdr_value) != NULL) {
1121 UpnpFileInfo_strncpy_Os(finfo, hdr_value.buf, hdr_value.length);
1122 }
1123
1124 /* get file info */
1125 if (virtualDirCallback.get_info(filename->buf, finfo,
1126 RespInstr->Cookie,
1127 &RespInstr->RequestCookie) != 0) {
1128 err_code = HTTP_NOT_FOUND;
1129 goto error_handler;
1130 }
1131 /* try index.html if req is a dir */
1132 if (UpnpFileInfo_get_IsDirectory(finfo)) {
1133 if (filename->buf[filename->length - 1] == '/') {
1134 temp_str = "index.html";
1135 } else {
1136 temp_str = "/index.html";
1137 }
1138 if (membuffer_append_str(filename, temp_str) != 0) {
1139 goto error_handler;
1140 }
1141 /* get info */
1143 filename->buf, finfo, RespInstr->Cookie,
1144 &RespInstr->RequestCookie) != UPNP_E_SUCCESS ||
1146 err_code = HTTP_NOT_FOUND;
1147 goto error_handler;
1148 }
1149 }
1150 /* not readable */
1151 if (!UpnpFileInfo_get_IsReadable(finfo)) {
1152 err_code = HTTP_FORBIDDEN;
1153 goto error_handler;
1154 }
1155 /* finally, get content type */
1156 /* if ( get_content_type(filename->buf, &content_type)
1157 * != 0 ) */
1158 /*{ */
1159 /* goto error_handler; */
1160 /* } */
1161 }
1162 } else if (!using_alias) {
1163 if (gDocumentRootDir.length == 0) {
1164 goto error_handler;
1165 }
1166 /* */
1167 /* get file name */
1168 /* */
1169
1170 /* filename str */
1171 if (membuffer_assign_str(filename, gDocumentRootDir.buf) != 0 ||
1172 membuffer_append_str(filename, request_doc) != 0) {
1173 goto error_handler; /* out of mem */
1174 }
1175 /* remove trailing slashes */
1176 while (filename->length > 0 &&
1177 filename->buf[filename->length - 1] == '/') {
1178 membuffer_delete(filename, filename->length - 1, 1);
1179 }
1180 if (req->method != HTTPMETHOD_POST) {
1181 /* get info on file */
1182 if (get_file_info(filename->buf, finfo) != 0) {
1183 err_code = HTTP_NOT_FOUND;
1184 goto error_handler;
1185 }
1186 /* try index.html if req is a dir */
1187 if (UpnpFileInfo_get_IsDirectory(finfo)) {
1188 if (filename->buf[filename->length - 1] == '/') {
1189 temp_str = "index.html";
1190 } else {
1191 temp_str = "/index.html";
1192 }
1193 if (membuffer_append_str(filename, temp_str) != 0) {
1194 goto error_handler;
1195 }
1196 /* get info */
1197 if (get_file_info(filename->buf, finfo) != 0 ||
1199 err_code = HTTP_NOT_FOUND;
1200 goto error_handler;
1201 }
1202 }
1203 /* not readable */
1204 if (!UpnpFileInfo_get_IsReadable(finfo)) {
1205 err_code = HTTP_FORBIDDEN;
1206 goto error_handler;
1207 }
1208 }
1209 /* finally, get content type */
1210 /* if ( get_content_type(filename->buf, &content_type) != 0
1211 * ) */
1212 /* { */
1213 /* goto error_handler; */
1214 /* } */
1215 }
1216 RespInstr->CorsHeader =
1218 RespInstr->ReadSendSize = UpnpFileInfo_get_FileLength(finfo);
1219 /* Check other header field. */
1220 code = CheckOtherHTTPHeaders(req, RespInstr,
1222 if (code != HTTP_OK) {
1223 err_code = code;
1224 goto error_handler;
1225 }
1226 if (req->method == HTTPMETHOD_POST) {
1227 *rtype = RESP_POST;
1228 err_code = HTTP_OK;
1229 goto error_handler;
1230 }
1231
1232 /* Check if chunked encoding should be used. */
1233 if (using_virtual_dir &&
1235 /* Chunked encoding is only supported by HTTP 1.1 clients */
1236 if (resp_major == 1 && resp_minor == 1) {
1237 RespInstr->IsChunkActive = 1;
1238 } else {
1239 /* The virtual callback indicates that we should use
1240 * chunked encoding however the client doesn't support
1241 * it. Return with an internal server error. */
1242 err_code = HTTP_NOT_ACCEPTABLE;
1243 goto error_handler;
1244 }
1245 }
1246
1247 aux_LastModified = UpnpFileInfo_get_LastModified(finfo);
1248 if (RespInstr->IsRangeActive && RespInstr->IsChunkActive) {
1249 /* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
1250 /* Transfer-Encoding: chunked */
1251 if (http_MakeMessage(
1252 headers, resp_major, resp_minor,
1253 "R"
1254 "T"
1255 "GKLAD"
1256 "s"
1257 "tcS"
1258 "Xc"
1259 "ECc",
1260 HTTP_PARTIAL_CONTENT, /* status code */
1261 UpnpFileInfo_get_ContentType(finfo), /* content type */
1262 RespInstr, /* range info */
1263 RespInstr, /* language info */
1264 RespInstr, /* Access-Control-Allow-Origin */
1265 "LAST-MODIFIED: ", &aux_LastModified, X_USER_AGENT,
1266 UpnpFileInfo_get_ExtraHeadersList(finfo)) != 0) {
1267 goto error_handler;
1268 }
1269 } else if (RespInstr->IsRangeActive && !RespInstr->IsChunkActive) {
1270 /* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
1271 if (http_MakeMessage(
1272 headers, resp_major, resp_minor,
1273 "R"
1274 "N"
1275 "T"
1276 "GLAD"
1277 "s"
1278 "tcS"
1279 "Xc"
1280 "ECc",
1281 HTTP_PARTIAL_CONTENT, /* status code */
1282 RespInstr->ReadSendSize, /* content length */
1283 UpnpFileInfo_get_ContentType(finfo), /* content type */
1284 RespInstr, /* range info */
1285 RespInstr, /* language info */
1286 RespInstr, /* Access-Control-Allow-Origin */
1287 "LAST-MODIFIED: ", &aux_LastModified, X_USER_AGENT,
1288 UpnpFileInfo_get_ExtraHeadersList(finfo)) != 0) {
1289 goto error_handler;
1290 }
1291 } else if (!RespInstr->IsRangeActive && RespInstr->IsChunkActive) {
1292 /* Transfer-Encoding: chunked */
1293 if (http_MakeMessage(
1294 headers, resp_major, resp_minor,
1295 "RK"
1296 "TLAD"
1297 "s"
1298 "tcS"
1299 "Xc"
1300 "ECc",
1301 HTTP_OK, /* status code */
1302 UpnpFileInfo_get_ContentType(finfo), /* content type */
1303 RespInstr, /* language info */
1304 RespInstr, /* Access-Control-Allow-Origin */
1305 "LAST-MODIFIED: ", &aux_LastModified, X_USER_AGENT,
1306 UpnpFileInfo_get_ExtraHeadersList(finfo)) != 0) {
1307 goto error_handler;
1308 }
1309 } else {
1310 /* !RespInstr->IsRangeActive && !RespInstr->IsChunkActive */
1311 if (RespInstr->ReadSendSize >= 0) {
1312 if (http_MakeMessage(
1313 headers, resp_major, resp_minor,
1314 "R"
1315 "N"
1316 "TLAD"
1317 "s"
1318 "tcS"
1319 "Xc"
1320 "ECc",
1321 HTTP_OK, /* status code */
1322 RespInstr->ReadSendSize, /* content length */
1323 UpnpFileInfo_get_ContentType(finfo), /* content type */
1324 RespInstr, /* language info */
1325 RespInstr, /* Access-Control-Allow-Origin */
1326 "LAST-MODIFIED: ", &aux_LastModified, X_USER_AGENT,
1327 UpnpFileInfo_get_ExtraHeadersList(finfo)) != 0) {
1328 goto error_handler;
1329 }
1330 } else {
1331 if (http_MakeMessage(
1332 headers, resp_major, resp_minor,
1333 "R"
1334 "TLAD"
1335 "s"
1336 "tcS"
1337 "Xc"
1338 "ECc",
1339 HTTP_OK, /* status code */
1340 UpnpFileInfo_get_ContentType(finfo), /* content type */
1341 RespInstr, /* language info */
1342 RespInstr, /* Access-Control-Allow-Origin */
1343 "LAST-MODIFIED: ", &aux_LastModified, X_USER_AGENT,
1344 UpnpFileInfo_get_ExtraHeadersList(finfo)) != 0) {
1345 goto error_handler;
1346 }
1347 }
1348 }
1349 if (req->method == HTTPMETHOD_HEAD) {
1350 *rtype = RESP_HEADERS;
1351 } else if (using_alias) {
1352 /* GET xml */
1353 *rtype = RESP_XMLDOC;
1354 } else if (using_virtual_dir) {
1355 *rtype = RESP_WEBDOC;
1356 } else {
1357 /* GET filename */
1358 *rtype = RESP_FILEDOC;
1359 }
1360 /* simple get http 0.9 as specified in http 1.0 */
1361 /* don't send headers */
1362 if (req->method == HTTPMETHOD_SIMPLEGET) {
1363 membuffer_destroy(headers);
1364 }
1365 err_code = HTTP_OK;
1366
1367error_handler:
1368 umock::stdlib_h.free(request_doc);
1371 UpnpFileInfo_delete(finfo);
1372 if (err_code != HTTP_OK && alias_grabbed) {
1373 a_alias->release();
1374 }
1375
1376 return err_code;
1377}
1378
1392 http_parser_t* parser,
1394 SOCKINFO* info,
1396 char* filename,
1399 struct SendInstruction* Instr) {
1400 size_t Data_Buf_Size = 1024;
1401 char Buf[1024];
1402 int Timeout = -1;
1403 FILE* Fp;
1404 parse_status_t status = PARSE_OK;
1405 int ok_on_close = 0;
1406 size_t entity_offset = 0;
1407 int num_read = 0;
1408 int ret_code = HTTP_OK;
1409
1410 if (Instr && Instr->IsVirtualFile) {
1411 Fp = (FILE*)(virtualDirCallback.open)(
1412 filename, UPNP_WRITE, Instr->Cookie, Instr->RequestCookie);
1413 if (Fp == NULL)
1414 return HTTP_INTERNAL_SERVER_ERROR;
1415 } else {
1416#ifdef UPNP_ENABLE_POST_WRITE
1417 Fp = umock::stdio_h.fopen(filename, "wb");
1418 if (Fp == NULL)
1419 return HTTP_UNAUTHORIZED;
1420#else
1421 return HTTP_NOT_FOUND;
1422#endif
1423 }
1424 parser->position = POS_ENTITY;
1425 do {
1426 /* first parse what has already been gotten */
1427 if (parser->position != POS_COMPLETE)
1428 status = parser_parse_entity(parser);
1429 if (status == PARSE_INCOMPLETE_ENTITY) {
1430 /* read until close */
1431 ok_on_close = 1;
1432 } else if ((status != PARSE_SUCCESS) && (status != PARSE_CONTINUE_1) &&
1433 (status != PARSE_INCOMPLETE)) {
1434 /* error */
1435 ret_code = HTTP_BAD_REQUEST;
1436 goto ExitFunction;
1437 }
1438 /* read more if necessary entity */
1439 while (entity_offset + Data_Buf_Size > parser->msg.entity.length &&
1440 parser->position != POS_COMPLETE) {
1441 num_read = sock_read(info, Buf, sizeof(Buf), &Timeout);
1442 if (num_read > 0) {
1443 /* append data to buffer */
1444 if (membuffer_append(&parser->msg.msg, Buf, (size_t)num_read) !=
1445 0) {
1446 /* set failure status */
1447 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1448 ret_code = HTTP_INTERNAL_SERVER_ERROR;
1449 goto ExitFunction;
1450 }
1451 status = parser_parse_entity(parser);
1452 if (status == PARSE_INCOMPLETE_ENTITY) {
1453 /* read until close */
1454 ok_on_close = 1;
1455 } else if ((status != PARSE_SUCCESS) &&
1456 (status != PARSE_CONTINUE_1) &&
1457 (status != PARSE_INCOMPLETE)) {
1458 ret_code = HTTP_BAD_REQUEST;
1459 goto ExitFunction;
1460 }
1461 } else if (num_read == 0) {
1462 if (ok_on_close) {
1463 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
1464 "<<< (RECVD) "
1465 "<<<\n%s\n-----------------\n",
1466 parser->msg.msg.buf);
1467 parser->position = POS_COMPLETE;
1468 } else {
1469 /* partial msg or response */
1470 parser->http_error_code = HTTP_BAD_REQUEST;
1471 ret_code = HTTP_BAD_REQUEST;
1472 goto ExitFunction;
1473 }
1474 } else {
1475 ret_code = HTTP_SERVICE_UNAVAILABLE;
1476 goto ExitFunction;
1477 }
1478 }
1479 if ((entity_offset + Data_Buf_Size) > parser->msg.entity.length) {
1480 Data_Buf_Size = parser->msg.entity.length - entity_offset;
1481 }
1482 memcpy(
1483 Buf,
1484 &parser->msg.msg.buf[parser->entity_start_position + entity_offset],
1485 Data_Buf_Size);
1486 entity_offset += Data_Buf_Size;
1487 if (Instr && Instr->IsVirtualFile) {
1488 int n = virtualDirCallback.write(
1489 Fp, Buf, Data_Buf_Size, Instr->Cookie, Instr->RequestCookie);
1490 if (n < 0) {
1491 ret_code = HTTP_INTERNAL_SERVER_ERROR;
1492 goto ExitFunction;
1493 }
1494 } else {
1495 size_t n = umock::stdio_h.fwrite(Buf, 1, Data_Buf_Size, Fp);
1496 if (n != Data_Buf_Size) {
1497 ret_code = HTTP_INTERNAL_SERVER_ERROR;
1498 goto ExitFunction;
1499 }
1500 }
1501 } while (parser->position != POS_COMPLETE ||
1502 entity_offset != parser->msg.entity.length);
1503ExitFunction:
1504 if (Instr && Instr->IsVirtualFile) {
1505 virtualDirCallback.close(Fp, Instr->Cookie, Instr->RequestCookie);
1506 } else {
1507 umock::stdio_h.fclose(Fp);
1508 }
1509
1510 return ret_code;
1511}
1512
1514} // anonymous namespace
1515} // namespace compa
1516
1517
1519 if (bWebServerState == WEB_SERVER_DISABLED) {
1522 compa::gAliasDoc.clear();
1523 pVirtualDirList = nullptr;
1524
1525 /* Initialize callbacks */
1526 virtualDirCallback.get_info = nullptr;
1527 virtualDirCallback.open = nullptr;
1528 virtualDirCallback.read = nullptr;
1529 virtualDirCallback.write = nullptr;
1530 virtualDirCallback.seek = nullptr;
1531 virtualDirCallback.close = nullptr;
1532
1533 bWebServerState = WEB_SERVER_ENABLED;
1535 }
1536}
1537
1538int web_server_set_alias(const char* a_alias_name, const char* a_alias_content,
1539 size_t a_alias_content_length,
1540 time_t a_last_modified) {
1541 TRACE("Executing web_server_set_alias()")
1542 return compa::gAliasDoc.set(a_alias_name, a_alias_content,
1543 a_alias_content_length, a_last_modified);
1544}
1545
1546int web_server_set_root_dir(const char* root_dir) {
1547 TRACE("Executing web_server_set_root_dir()")
1548 if (root_dir == nullptr)
1550
1551 int ret = membuffer_assign_str(&gDocumentRootDir, root_dir);
1552 if (ret != 0)
1553 return ret;
1554 /* remove trailing '/', if any */
1555 if (gDocumentRootDir.length > 0) {
1556 size_t index = gDocumentRootDir.length - 1; /* last char */
1557 if (gDocumentRootDir.buf[index] == '/')
1559 }
1560
1561 return 0;
1562}
1563
1564int web_server_set_cors(const char* cors_string) {
1565 int ret;
1566
1567 ret = membuffer_assign_str(&gWebserverCorsString, cors_string);
1568 if (ret != 0)
1569 return ret;
1570
1571 return 0;
1572}
1573
1575 /* INOUT */ http_message_t* a_req, SOCKINFO* a_info) {
1576 int ret;
1577 int timeout = -1;
1578 compa::resp_type rtype{compa::RESP_UNSPEC};
1579 membuffer headers;
1580 membuffer filename;
1581 compa::CXmlAlias xmldoc;
1582 SendInstruction RespInstr;
1583
1584 /* init */
1585 memset(&RespInstr, 0, sizeof(RespInstr));
1586 membuffer_init(&headers);
1587 membuffer_init(&filename);
1588
1589 /* Process request should create the different kind of header depending on
1590 * the type of request. */
1591 ret = compa::process_request_in(a_info, a_req, &rtype, &headers, &filename,
1592 &xmldoc, &RespInstr);
1593 if (ret != HTTP_OK) {
1594 /* send error code */
1595 http_SendStatusResponse(a_info, ret, a_req->major_version,
1596 a_req->minor_version);
1597 } else {
1598 /* send response */
1599 switch (rtype) {
1600 case compa::RESP_FILEDOC:
1601 http_SendMessage(a_info, &timeout, "Ibf", &RespInstr, headers.buf,
1602 headers.length, filename.buf);
1603 break;
1604 case compa::RESP_XMLDOC:
1605 http_SendMessage(a_info, &timeout, "Ibb", &RespInstr, headers.buf,
1606 headers.length, xmldoc.doc().data(),
1607 xmldoc.doc().length());
1608 xmldoc.release();
1609 break;
1610 case compa::RESP_WEBDOC:
1611 /*http_SendVirtualDirDoc(a_info, &timeout, "Ibf",
1612 &RespInstr,
1613 headers.buf, headers.length,
1614 filename.buf);*/
1615 http_SendMessage(a_info, &timeout, "Ibf", &RespInstr, headers.buf,
1616 headers.length, filename.buf);
1617 break;
1618 case compa::RESP_HEADERS:
1619 /* headers only */
1620 http_SendMessage(a_info, &timeout, "b", headers.buf,
1621 headers.length);
1622 break;
1623 case compa::RESP_POST:
1624 /* headers only */
1625 ret = compa::http_RecvPostMessage(a_parser, a_info, filename.buf,
1626 &RespInstr);
1627 /* Send response. */
1628 http_MakeMessage(&headers, 1, 1, "RTLSXcCc", ret, "text/html",
1629 &RespInstr, X_USER_AGENT);
1630 http_SendMessage(a_info, &timeout, "b", headers.buf,
1631 headers.length);
1632 break;
1633 default:
1634 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
1635 "webserver: Invalid response type received.\n");
1636 assert(0);
1637 }
1638 }
1639 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
1640 "webserver: request processed...\n");
1641 membuffer_destroy(&headers);
1642 membuffer_destroy(&filename);
1643}
1644
1646 TRACE("Executing SetHTTPGetCallback()")
1647 gGetCallback = callback;
1648}
1649
1651 if (bWebServerState == WEB_SERVER_ENABLED) {
1654 compa::gAliasDoc.clear();
1655 bWebServerState = WEB_SERVER_DISABLED;
1656 SetHTTPGetCallback(nullptr);
1657 }
1658}
#define LINE_SIZE
Definition API.hpp:45
#define UPNP_USING_CHUNKED
Definition API.hpp:52
int http_SendMessage(SOCKINFO *info, int *TimeOut, const char *fmt,...)
Sends a message to the destination based on the format parameter.
void http_CalcResponseVersion(int request_major_vers, int request_minor_vers, int *response_major_vers, int *response_minor_vers)
Calculate HTTP response versions based on the request versions.
tm * http_gmtime_r(const time_t *clock, tm *result)
Portable gmtime_r for Microsoft Windows.
int http_SendStatusResponse(SOCKINFO *info, int http_status_code, int request_major_version, int request_minor_version)
Generate a response message for the status query and send the status response.
int http_MakeMessage(membuffer *buf, int http_major_version, int http_minor_version, const char *fmt,...)
Generate an HTTP message based on the format that is specified in the input parameters.
void web_server_callback(http_parser_t *a_parser, http_message_t *a_req, SOCKINFO *a_info)
Main entry point into web server.
int web_server_set_root_dir(const char *root_dir)
Assign the path to the global Document root directory.
void SetHTTPGetCallback(MiniServerCallback callback)
Set HTTP Get Callback.
int web_server_set_cors(const char *cors_string)
Assign the Access-Control-Allow-Origin specfied by the input const char* cors_string parameterto the ...
int web_server_set_alias(const char *a_alias_name, const char *a_alias_content, size_t a_alias_content_length, time_t a_last_modified)
Replaces current alias with the given alias.
void web_server_destroy()
Release memory allocated for the global web server root directory and the global XML document.
void web_server_init()
Initilialize root directory for web server and different documents.
http_message_t msg
entire raw message
uri_type uri
Type of a uri, e.g. absolute, relative, etc.
constexpr std::array< const UPnPsdk::str_int_entry, 33 > Http_Header_Names
Assigns header-name id to its text representation.
http_method_t method
Http method of an outgoing request.
memptr name
Header name as a string.
int major_version
Http major version.
memptr entity
message body(entity).
#define HDR_ACCEPT_LANGUAGE
Header definition.
membuffer value
Raw-value; could be multi-lined; min-length = 0.
membuffer msg
entire raw message.
int http_error_code
read-only; in case of parse error, this contains the HTTP error code (4XX or 5XX).
parser_pos_t position
Private data – don't touch.
@ POS_COMPLETE
Position complete.
@ POS_ENTITY
Position entity.
#define HDR_USER_AGENT
Type of a HTTP header.
parse_status_t
Status of parsing.
@ PARSE_INCOMPLETE
@ PARSE_INCOMPLETE_ENTITY
@ PARSE_SUCCESS
@ PARSE_OK
@ PARSE_CONTINUE_1
LinkedList headers
List of headers.
int minor_version
Http minor version.
size_t entity_start_position
Offset in the raw message buffer, which contains the message body. preceding this are the headers of ...
#define HDR_TE
Header definition.
#define HDR_RANGE
Header definition.
#define HDR_CONTENT_LENGTH
Type of a HTTP header.
Structure of an HTTP parser object.
Structure of an HTTP message.
Structure of an HTTP header object.
size_t size
Size of the buffer.
Definition uri.hpp:78
const char * buff
Buffer.
Definition uri.hpp:77
token pathquery
Member variable.
Definition uri.hpp:98
Represents a URI used in parse_uri and elsewhere.
Definition uri.hpp:92
membuffer gDocumentRootDir
Definition webserver.hpp:45
#define X_USER_AGENT
Can be overwritten by configure CFLAGS argument.
Definition webserver.hpp:87
off_t RangeOffset
member variable
Definition webserver.hpp:61
const void * CorsHeader
Definition webserver.hpp:68
long RecvWriteSize
Recv from the network and write into local file.
Definition webserver.hpp:66
off_t ReadSendSize
Read from local source and send on the network.
Definition webserver.hpp:64
const void * RequestCookie
Cookie associated with the request.
Definition webserver.hpp:72
int IsRangeActive
member variable
Definition webserver.hpp:57
const void * Cookie
Cookie associated with the virtualDir.
Definition webserver.hpp:70
char AcceptLanguageHeader[200]
member variable
Definition webserver.hpp:60
int IsTrailers
member variable
Definition webserver.hpp:58
int IsVirtualFile
member variable
Definition webserver.hpp:53
char RangeHeader[200]
member variable
Definition webserver.hpp:59
int IsChunkActive
member variable
Definition webserver.hpp:54
Send instruction.
Definition webserver.hpp:50
ListNode * ListNext(LinkedList *list, ListNode *node)
Returns the next item in the list.
ListNode * ListHead(LinkedList *list)
Returns the head of the list.
Linked list node. Stores generic item and pointers to next and prev.
Declarations to manage the builtin Webserver.
s_UpnpExtraHeaders
Header file for UpnpExtraHeaders methods.
PUPNP_Api UpnpExtraHeaders * UpnpExtraHeaders_new()
PUPNP_Api const UpnpListHead * UpnpExtraHeaders_get_node(const UpnpExtraHeaders *p)
PUPNP_Api void UpnpExtraHeaders_delete(UpnpExtraHeaders *p)
PUPNP_Api int UpnpExtraHeaders_strncpy_value(UpnpExtraHeaders *p, const char *s, size_t n)
PUPNP_Api int UpnpExtraHeaders_strncpy_name(UpnpExtraHeaders *p, const char *s, size_t n)
s_UpnpFileInfo
PUPNP_Api int UpnpFileInfo_set_LastModified(UpnpFileInfo *p, time_t n)
PUPNP_Api int UpnpFileInfo_set_CtrlPtIPAddr(UpnpFileInfo *p, const struct sockaddr_storage *buf)
PUPNP_Api time_t UpnpFileInfo_get_LastModified(const UpnpFileInfo *p)
PUPNP_Api const DOMString UpnpFileInfo_get_ContentType(const UpnpFileInfo *p)
PUPNP_Api int UpnpFileInfo_set_FileLength(UpnpFileInfo *p, off_t n)
PUPNP_Api off_t UpnpFileInfo_get_FileLength(const UpnpFileInfo *p)
PUPNP_Api int UpnpFileInfo_set_ContentType(UpnpFileInfo *p, const DOMString s)
PUPNP_Api const UpnpListHead * UpnpFileInfo_get_ExtraHeadersList(const UpnpFileInfo *p)
PUPNP_Api int UpnpFileInfo_strncpy_Os(UpnpFileInfo *p, const char *s, size_t n)
PUPNP_Api int UpnpFileInfo_get_IsReadable(const UpnpFileInfo *p)
PUPNP_Api int UpnpFileInfo_set_IsReadable(UpnpFileInfo *p, int n)
PUPNP_Api void UpnpFileInfo_delete(UpnpFileInfo *p)
PUPNP_Api int UpnpFileInfo_set_IsDirectory(UpnpFileInfo *p, int n)
PUPNP_Api int UpnpFileInfo_get_IsDirectory(const UpnpFileInfo *p)
PUPNP_Api UpnpFileInfo * UpnpFileInfo_new(void)
Printf format for integers.
VDCallback_Read read
char dirName[NAME_SIZE]
VDCallback_Open open
VDCallback_Close close
VDCallback_Write write
const void * cookie
VDCallback_GetInfo get_info
struct virtual_Dir_List * next
VDCallback_Seek seek
Virtual directory list.
Table with C-strings mapped to an integer id.
Definition strintmap.hpp:38
static constexpr size_t npos
Value returned on error or when an index on a container is not found.
Definition strintmap.hpp:46
size_t index_of(const char *a_name, bool a_case_sensitive=false)
Match the given name with names from the entries in the table.
Definition strintmap.hpp:81
http_header_t * httpmsg_find_hdr(http_message_t *msg, int header_name_id, memptr *value)
Finds header from a list, with the given 'name_id'.
parse_status_t parser_parse_entity(http_parser_t *parser)
Determines method to read entity.
PUPNP_Api UpnpListIter UpnpListInsert(UpnpListHead *list, UpnpListIter pos, UpnpListHead *elt)
Insert element before pos, returns iterator pointing to inserted element.
Definition list.cpp:68
PUPNP_Api UpnpListIter UpnpListEnd(UpnpListHead *list)
Return end of list sentinel iterator (not an element)
Definition list.cpp:58
PUPNP_Api UpnpListIter UpnpListErase(UpnpListHead *list, UpnpListIter pos)
Erase element at pos, return next one, or end()
Definition list.cpp:80
PUPNP_Api UpnpListIter UpnpListBegin(UpnpListHead *list)
Return iterator pointing to the first list element, or UpnpListEnd(list) if the list is empty.
Definition list.cpp:51
List anchor structure.
Definition list.hpp:56
void membuffer_destroy(membuffer *m)
Free's memory allocated for membuffer* m.
void membuffer_init(membuffer *m)
Wrapper to membuffer_initialize().
int membuffer_append(membuffer *m, const void *buf, size_t buf_len)
Invokes function to appends data from a constant buffer to the buffer.
int membuffer_assign_str(membuffer *m, const char *c_str)
Wrapper function for membuffer_assign().
int membuffer_append_str(membuffer *m, const char *c_str)
Invokes function to appends data from a constant string to the buffer.
void membuffer_delete(membuffer *m, size_t index, size_t num_bytes)
Shrink the size of the buffer depending on the current size of the buffer and the input parameters....
size_t length
length of buffer without terminating null byte (read-only).
Definition membuffer.hpp:65
char * buf
mem buffer; must not write beyond buf[length-1] (read/write).
Definition membuffer.hpp:63
size_t length
length of memory (read-only).
Definition membuffer.hpp:54
char * buf
start of memory (read/write).
Definition membuffer.hpp:52
pointer to a chunk of memory.
Definition membuffer.hpp:50
Maintains a block of dynamically allocated memory.
Definition membuffer.hpp:61
#define UPNP_E_INVALID_ARGUMENT
One or more of the parameters passed to a function is invalid.
Definition messages.hpp:287
#define UPNP_E_FILE_NOT_FOUND
The filename passed to one of the device registration functions was not found or was not accessible.
Definition messages.hpp:293
#define UPNP_E_SUCCESS
The operation completed successfully.
Definition messages.hpp:27
#define UPNP_E_OUTOF_MEMORY
Not enough resources are currently available to complete the operation.
Definition messages.hpp:57
void(* MiniServerCallback)(http_parser_t *parser, http_message_t *request, SOCKINFO *info)
For a miniserver callback function.
MiniServerCallback gGetCallback
HTTP server callback.
std::string subtype
file subtype
Definition webserver.hpp:21
std::string type
file type
Definition webserver.hpp:20
UPnPsdk_VIS const Document_meta * select_filetype(std::string_view a_extension)
Based on the extension, returns the content type and content subtype.
Mapping of file extension to content-type of document.
Definition webserver.hpp:18
CXmlAlias gAliasDoc
Global XML document object.
const char * gMediaTypes[]
Media types.
Definition webserver.cpp:84
char * web_server_asctime_r(const struct tm *tm, char *buf)
Multiplatform wrapper to make win32 asctime_s compatible to posix asctime_r.
constexpr size_t APPLICATION_INDEX
index to get media type of application from the media types table.
Definition webserver.cpp:94
int process_request_in(SOCKINFO *info, http_message_t *req, enum resp_type *rtype, membuffer *headers, membuffer *filename, CXmlAlias *a_alias, SendInstruction *RespInstr)
Process a remote request and return the result.
int CheckOtherHTTPHeaders(http_message_t *Req, struct SendInstruction *RespInstr, off_t FileSize)
Get header id from the request parameter.
int search_extension(const char *a_extension, const char **a_con_type, const char **a_con_subtype)
Based on the extension, returns the content type and content subtype.
int get_file_info(const char *filename, UpnpFileInfo *info)
Get file information.
int GetNextRange(char **SrcRangeStr, off_t *FirstByte, off_t *LastByte)
Returns a range of integers from a string.
constexpr size_t ASCTIME_R_BUFFER_SIZE
Number of elements for asctime_s on win32, means buffer size.
Definition webserver.cpp:97
char * StrTok(char **Src, const char *Del)
Finds next token in a string.
int isFileInVirtualDir(char *filePath, const void **cookie)
Compares filePath with paths from the list of virtual directory lists.
int http_RecvPostMessage(http_parser_t *parser, SOCKINFO *info, char *filename, struct SendInstruction *Instr)
Receives the HTTP post message.
int get_content_type(const char *filename, UpnpFileInfo *fileInfo)
Based on the extension of the filename, clones an XML string based on type and content subtype.
void FreeExtraHTTPHeaders(UpnpListHead *extraHeadersList)
Free extra HTTP headers.
char * StrStr(char *s1, const char *s2)
Finds a substring from a string in a case insensitive way.
int ExtraHTTPHeaders(http_message_t *Req, UpnpListHead *extraHeadersList)
Build an array of unrecognized headers.
int CreateHTTPRangeResponseHeader(char *ByteRangeSpecifier, off_t FileLength, struct SendInstruction *Instr)
Fills in the Offset, read size and contents to send out as an HTTP Range Response.
int get_alias(const char *request_file, CXmlAlias *alias, UpnpFileInfo *info)
Compare file names.
void ToUpperCase(char *s)
Converts C string in place to upper case.
Refactored pupnp program code that is compatible to the original pupnp code.
Specifications to be portable between different platforms.
int sock_read(SOCKINFO *info, char *buffer, size_t bufsize, int *timeoutSecs)
Reads data on socket in sockinfo.
Definition sock.cpp:525
sockaddr_storage foreign_sockaddr
Socket address of the remote node only filled in incoming requests.
Definition sock.hpp:69
Additional socket information for connections and ssl.
Definition sock.hpp:65
Manage "Step 1: Discovery" of the UPnP+™ specification with SSDP.
HTTP status codes.
Alias directory structure on the webserver for an XML document.
std::string_view doc() const
Get the root udevice description XML document.
bool is_valid() const
Returns if the XML object contains a valid XML document.
std::string_view name() const
Get the name of the root udevice description XML document.
int set(const char *a_alias_name, const char *&a_alias_content, size_t &a_alias_content_length, time_t a_last_modified=time(nullptr))
Set an XML Document.
time_t last_modified() const
Get last modified date of the root udevice description XML document.
void release()
Release an XML document from the XML object.
Define macro for synced logging to the console for detailed info and debug.
struct VirtualDirCallbacks virtualDirCallback
This structure is for virtual directory callbacks.
Definition upnpapi.cpp:247
virtualDirList * pVirtualDirList
Pointer to the virtual directory list.
Definition upnpapi.cpp:250
membuffer gWebserverCorsString
Definition webserver.hpp:47
WebServerState bWebServerState
Flag to indicate the state of web server.
Definition upnpapi.cpp:268
Inititalize the compatible library before it can be used.
UPnPsdk_VIS void UpnpPrintf(Upnp_LogLevel DLevel, Dbg_Module Module, const char *DbgFileName, int DbgLineNo, const char *FmtStr,...)
Prints the debug statement.
int remove_dots(char *buf, size_t size)
Removes ".", and ".." from a path.
Definition uri.cpp:538
int remove_escaped_chars(char *in, size_t *size)
Removes http escaped characters such as: "%20" and replaces them with their character representation.
Definition uri.cpp:526