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: 2026-03-13
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
42#include <UpnpExtraHeaders.hpp>
43#include <UpnpIntTypes.hpp>
44#include <httpreadwrite.hpp>
45#include <statcodes.hpp>
46#include <upnpapi.hpp>
47
48#include <UPnPsdk/webserver.hpp>
49
50#include <umock/stdlib.hpp>
51#include <umock/stdio.hpp>
52
53#ifndef COMPA_INTERNAL_CONFIG_HPP
54#error "No or wrong config.hpp header file included."
55#endif
56
57#ifndef COMPA_NET_HTTP_WEBSERVER_HPP
58#error "No or wrong webserver.hpp header file included."
59#endif
60
62#include <cassert>
63#include <sys/stat.h>
65
66
67namespace compa {
68namespace {
69
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 UpnpFileInfo* finfo;
1044 time_t aux_LastModified;
1045 bool alias_grabbed{false};
1046 int using_alias{0};
1047 int using_virtual_dir{0};
1048 uri_type* url;
1049 const char* temp_str;
1050 int resp_major;
1051 int resp_minor;
1052 memptr hdr_value;
1053 char* request_doc{};
1054
1055 url = &req->uri;
1056 assert(req->method == HTTPMETHOD_GET || req->method == HTTPMETHOD_HEAD ||
1057 req->method == HTTPMETHOD_POST ||
1058 req->method == HTTPMETHOD_SIMPLEGET);
1059 /* init */
1060 memset(&finfo, 0, sizeof(finfo));
1061 finfo = UpnpFileInfo_new();
1062 err_code = HTTP_INTERNAL_SERVER_ERROR; /* default error */
1063
1065 &resp_major, &resp_minor);
1066 /* */
1067 /* remove dots */
1068 /* */
1069 std::string request_docObj =
1070 std::string(url->pathquery.buff, url->pathquery.size);
1071 try {
1072 UPnPsdk::decode_esc_chars(request_docObj); // modified in place.
1073 } catch (const std::invalid_argument& ex) {
1074 UPnPsdk_LOGCATCH("MSG1157") "catched next line...\n" << ex.what();
1075 err_code = HTTP_NOT_ACCEPTABLE;
1076 goto error_handler;
1077 }
1078 UPnPsdk::remove_dot_segments(request_docObj); // modified in place.
1079
1080 request_doc = static_cast<char*>(malloc(request_docObj.size() + 1));
1081 if (request_doc == nullptr) {
1082 goto error_handler; /* out of mem */
1083 }
1084 request_docObj.copy(request_doc, std::string::npos);
1085 request_doc[request_docObj.size()] = '\0';
1086 if (*request_doc != '/') {
1087 /* no slash */
1088 err_code = HTTP_BAD_REQUEST;
1089 goto error_handler;
1090 }
1091 if (isFileInVirtualDir(request_doc, &RespInstr->Cookie)) {
1092 using_virtual_dir = 1;
1093 RespInstr->IsVirtualFile = 1;
1094 if (membuffer_assign_str(filename, request_doc) != 0) {
1095 goto error_handler;
1096 }
1097 } else {
1098 /* try using alias */
1099 if (a_alias != nullptr && gAliasDoc.is_valid()) {
1100 *a_alias = gAliasDoc;
1101 alias_grabbed = true;
1102 using_alias = get_alias(request_doc, a_alias, finfo);
1103 if (using_alias == 1) {
1105 "text/xml; charset=\"utf-8\"");
1106 if (UpnpFileInfo_get_ContentType(finfo) == NULL) {
1107 goto error_handler;
1108 }
1109 }
1110 }
1111 }
1112 if (using_virtual_dir) {
1113 if (req->method != HTTPMETHOD_POST) {
1114 if ((code = ExtraHTTPHeaders(
1116 finfo))) != HTTP_OK) {
1117 err_code = code;
1118 goto error_handler;
1119 }
1120
1122
1123 if (httpmsg_find_hdr(req, HDR_USER_AGENT, &hdr_value) != NULL) {
1124 UpnpFileInfo_strncpy_Os(finfo, hdr_value.buf, hdr_value.length);
1125 }
1126
1127 /* get file info */
1128 if (virtualDirCallback.get_info(filename->buf, finfo,
1129 RespInstr->Cookie,
1130 &RespInstr->RequestCookie) != 0) {
1131 err_code = HTTP_NOT_FOUND;
1132 goto error_handler;
1133 }
1134 /* try index.html if req is a dir */
1135 if (UpnpFileInfo_get_IsDirectory(finfo)) {
1136 if (filename->buf[filename->length - 1] == '/') {
1137 temp_str = "index.html";
1138 } else {
1139 temp_str = "/index.html";
1140 }
1141 if (membuffer_append_str(filename, temp_str) != 0) {
1142 goto error_handler;
1143 }
1144 /* get info */
1146 filename->buf, finfo, RespInstr->Cookie,
1147 &RespInstr->RequestCookie) != UPNP_E_SUCCESS ||
1149 err_code = HTTP_NOT_FOUND;
1150 goto error_handler;
1151 }
1152 }
1153 /* not readable */
1154 if (!UpnpFileInfo_get_IsReadable(finfo)) {
1155 err_code = HTTP_FORBIDDEN;
1156 goto error_handler;
1157 }
1158 /* finally, get content type */
1159 /* if ( get_content_type(filename->buf, &content_type)
1160 * != 0 ) */
1161 /*{ */
1162 /* goto error_handler; */
1163 /* } */
1164 }
1165 } else if (!using_alias) {
1166 if (gDocumentRootDir.length == 0) {
1167 goto error_handler;
1168 }
1169 /* */
1170 /* get file name */
1171 /* */
1172
1173 /* filename str */
1174 if (membuffer_assign_str(filename, gDocumentRootDir.buf) != 0 ||
1175 membuffer_append_str(filename, request_doc) != 0) {
1176 goto error_handler; /* out of mem */
1177 }
1178 /* remove trailing slashes */
1179 while (filename->length > 0 &&
1180 filename->buf[filename->length - 1] == '/') {
1181 membuffer_delete(filename, filename->length - 1, 1);
1182 }
1183 if (req->method != HTTPMETHOD_POST) {
1184 /* get info on file */
1185 if (get_file_info(filename->buf, finfo) != 0) {
1186 err_code = HTTP_NOT_FOUND;
1187 goto error_handler;
1188 }
1189 /* try index.html if req is a dir */
1190 if (UpnpFileInfo_get_IsDirectory(finfo)) {
1191 if (filename->buf[filename->length - 1] == '/') {
1192 temp_str = "index.html";
1193 } else {
1194 temp_str = "/index.html";
1195 }
1196 if (membuffer_append_str(filename, temp_str) != 0) {
1197 goto error_handler;
1198 }
1199 /* get info */
1200 if (get_file_info(filename->buf, finfo) != 0 ||
1202 err_code = HTTP_NOT_FOUND;
1203 goto error_handler;
1204 }
1205 }
1206 /* not readable */
1207 if (!UpnpFileInfo_get_IsReadable(finfo)) {
1208 err_code = HTTP_FORBIDDEN;
1209 goto error_handler;
1210 }
1211 }
1212 /* finally, get content type */
1213 /* if ( get_content_type(filename->buf, &content_type) != 0
1214 * ) */
1215 /* { */
1216 /* goto error_handler; */
1217 /* } */
1218 }
1219 RespInstr->CorsHeader =
1221 RespInstr->ReadSendSize = UpnpFileInfo_get_FileLength(finfo);
1222 /* Check other header field. */
1223 code = CheckOtherHTTPHeaders(req, RespInstr,
1225 if (code != HTTP_OK) {
1226 err_code = code;
1227 goto error_handler;
1228 }
1229 if (req->method == HTTPMETHOD_POST) {
1230 *rtype = RESP_POST;
1231 err_code = HTTP_OK;
1232 goto error_handler;
1233 }
1234
1235 /* Check if chunked encoding should be used. */
1236 if (using_virtual_dir &&
1238 /* Chunked encoding is only supported by HTTP 1.1 clients */
1239 if (resp_major == 1 && resp_minor == 1) {
1240 RespInstr->IsChunkActive = 1;
1241 } else {
1242 /* The virtual callback indicates that we should use
1243 * chunked encoding however the client doesn't support
1244 * it. Return with an internal server error. */
1245 err_code = HTTP_NOT_ACCEPTABLE;
1246 goto error_handler;
1247 }
1248 }
1249
1250 aux_LastModified = UpnpFileInfo_get_LastModified(finfo);
1251 if (RespInstr->IsRangeActive && RespInstr->IsChunkActive) {
1252 /* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
1253 /* Transfer-Encoding: chunked */
1254 if (http_MakeMessage(
1255 headers, resp_major, resp_minor,
1256 "R"
1257 "T"
1258 "GKLAD"
1259 "s"
1260 "tcS"
1261 "Xc"
1262 "ECc",
1263 HTTP_PARTIAL_CONTENT, /* status code */
1264 UpnpFileInfo_get_ContentType(finfo), /* content type */
1265 RespInstr, /* range info */
1266 RespInstr, /* language info */
1267 RespInstr, /* Access-Control-Allow-Origin */
1268 "LAST-MODIFIED: ", &aux_LastModified, X_USER_AGENT,
1269 UpnpFileInfo_get_ExtraHeadersList(finfo)) != 0) {
1270 goto error_handler;
1271 }
1272 } else if (RespInstr->IsRangeActive && !RespInstr->IsChunkActive) {
1273 /* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
1274 if (http_MakeMessage(
1275 headers, resp_major, resp_minor,
1276 "R"
1277 "N"
1278 "T"
1279 "GLAD"
1280 "s"
1281 "tcS"
1282 "Xc"
1283 "ECc",
1284 HTTP_PARTIAL_CONTENT, /* status code */
1285 RespInstr->ReadSendSize, /* content length */
1286 UpnpFileInfo_get_ContentType(finfo), /* content type */
1287 RespInstr, /* range info */
1288 RespInstr, /* language info */
1289 RespInstr, /* Access-Control-Allow-Origin */
1290 "LAST-MODIFIED: ", &aux_LastModified, X_USER_AGENT,
1291 UpnpFileInfo_get_ExtraHeadersList(finfo)) != 0) {
1292 goto error_handler;
1293 }
1294 } else if (!RespInstr->IsRangeActive && RespInstr->IsChunkActive) {
1295 /* Transfer-Encoding: chunked */
1296 if (http_MakeMessage(
1297 headers, resp_major, resp_minor,
1298 "RK"
1299 "TLAD"
1300 "s"
1301 "tcS"
1302 "Xc"
1303 "ECc",
1304 HTTP_OK, /* status code */
1305 UpnpFileInfo_get_ContentType(finfo), /* content type */
1306 RespInstr, /* language info */
1307 RespInstr, /* Access-Control-Allow-Origin */
1308 "LAST-MODIFIED: ", &aux_LastModified, X_USER_AGENT,
1309 UpnpFileInfo_get_ExtraHeadersList(finfo)) != 0) {
1310 goto error_handler;
1311 }
1312 } else {
1313 /* !RespInstr->IsRangeActive && !RespInstr->IsChunkActive */
1314 if (RespInstr->ReadSendSize >= 0) {
1315 if (http_MakeMessage(
1316 headers, resp_major, resp_minor,
1317 "R"
1318 "N"
1319 "TLAD"
1320 "s"
1321 "tcS"
1322 "Xc"
1323 "ECc",
1324 HTTP_OK, /* status code */
1325 RespInstr->ReadSendSize, /* content length */
1326 UpnpFileInfo_get_ContentType(finfo), /* content type */
1327 RespInstr, /* language info */
1328 RespInstr, /* Access-Control-Allow-Origin */
1329 "LAST-MODIFIED: ", &aux_LastModified, X_USER_AGENT,
1330 UpnpFileInfo_get_ExtraHeadersList(finfo)) != 0) {
1331 goto error_handler;
1332 }
1333 } else {
1334 if (http_MakeMessage(
1335 headers, resp_major, resp_minor,
1336 "R"
1337 "TLAD"
1338 "s"
1339 "tcS"
1340 "Xc"
1341 "ECc",
1342 HTTP_OK, /* status code */
1343 UpnpFileInfo_get_ContentType(finfo), /* content type */
1344 RespInstr, /* language info */
1345 RespInstr, /* Access-Control-Allow-Origin */
1346 "LAST-MODIFIED: ", &aux_LastModified, X_USER_AGENT,
1347 UpnpFileInfo_get_ExtraHeadersList(finfo)) != 0) {
1348 goto error_handler;
1349 }
1350 }
1351 }
1352 if (req->method == HTTPMETHOD_HEAD) {
1353 *rtype = RESP_HEADERS;
1354 } else if (using_alias) {
1355 /* GET xml */
1356 *rtype = RESP_XMLDOC;
1357 } else if (using_virtual_dir) {
1358 *rtype = RESP_WEBDOC;
1359 } else {
1360 /* GET filename */
1361 *rtype = RESP_FILEDOC;
1362 }
1363 /* simple get http 0.9 as specified in http 1.0 */
1364 /* don't send headers */
1365 if (req->method == HTTPMETHOD_SIMPLEGET) {
1366 membuffer_destroy(headers);
1367 }
1368 err_code = HTTP_OK;
1369
1370error_handler:
1371 umock::stdlib_h.free(request_doc);
1374 UpnpFileInfo_delete(finfo);
1375 if (err_code != HTTP_OK && alias_grabbed) {
1376 a_alias->release();
1377 }
1378
1379 return err_code;
1380}
1381
1395 http_parser_t* parser,
1397 SOCKINFO* info,
1399 char* filename,
1402 struct SendInstruction* Instr) {
1403 size_t Data_Buf_Size = 1024;
1404 char Buf[1024];
1405 int Timeout = -1;
1406 FILE* Fp;
1407 parse_status_t status = PARSE_OK;
1408 int ok_on_close = 0;
1409 size_t entity_offset = 0;
1410 int num_read = 0;
1411 int ret_code = HTTP_OK;
1412
1413 if (Instr && Instr->IsVirtualFile) {
1414 Fp = (FILE*)(virtualDirCallback.open)(
1415 filename, UPNP_WRITE, Instr->Cookie, Instr->RequestCookie);
1416 if (Fp == NULL)
1417 return HTTP_INTERNAL_SERVER_ERROR;
1418 } else {
1419#ifdef UPNP_ENABLE_POST_WRITE
1420 Fp = umock::stdio_h.fopen(filename, "wb");
1421 if (Fp == NULL)
1422 return HTTP_UNAUTHORIZED;
1423#else
1424 return HTTP_NOT_FOUND;
1425#endif
1426 }
1427 parser->position = POS_ENTITY;
1428 do {
1429 /* first parse what has already been gotten */
1430 if (parser->position != POS_COMPLETE)
1431 status = parser_parse_entity(parser);
1432 if (status == PARSE_INCOMPLETE_ENTITY) {
1433 /* read until close */
1434 ok_on_close = 1;
1435 } else if ((status != PARSE_SUCCESS) && (status != PARSE_CONTINUE_1) &&
1436 (status != PARSE_INCOMPLETE)) {
1437 /* error */
1438 ret_code = HTTP_BAD_REQUEST;
1439 goto ExitFunction;
1440 }
1441 /* read more if necessary entity */
1442 while (entity_offset + Data_Buf_Size > parser->msg.entity.length &&
1443 parser->position != POS_COMPLETE) {
1444 num_read = sock_read(info, Buf, sizeof(Buf), &Timeout);
1445 if (num_read > 0) {
1446 /* append data to buffer */
1447 if (membuffer_append(&parser->msg.msg, Buf, (size_t)num_read) !=
1448 0) {
1449 /* set failure status */
1450 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1451 ret_code = HTTP_INTERNAL_SERVER_ERROR;
1452 goto ExitFunction;
1453 }
1454 status = parser_parse_entity(parser);
1455 if (status == PARSE_INCOMPLETE_ENTITY) {
1456 /* read until close */
1457 ok_on_close = 1;
1458 } else if ((status != PARSE_SUCCESS) &&
1459 (status != PARSE_CONTINUE_1) &&
1460 (status != PARSE_INCOMPLETE)) {
1461 ret_code = HTTP_BAD_REQUEST;
1462 goto ExitFunction;
1463 }
1464 } else if (num_read == 0) {
1465 if (ok_on_close) {
1466 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
1467 "<<< (RECVD) "
1468 "<<<\n%s\n-----------------\n",
1469 parser->msg.msg.buf);
1470 parser->position = POS_COMPLETE;
1471 } else {
1472 /* partial msg or response */
1473 parser->http_error_code = HTTP_BAD_REQUEST;
1474 ret_code = HTTP_BAD_REQUEST;
1475 goto ExitFunction;
1476 }
1477 } else {
1478 ret_code = HTTP_SERVICE_UNAVAILABLE;
1479 goto ExitFunction;
1480 }
1481 }
1482 if ((entity_offset + Data_Buf_Size) > parser->msg.entity.length) {
1483 Data_Buf_Size = parser->msg.entity.length - entity_offset;
1484 }
1485 memcpy(
1486 Buf,
1487 &parser->msg.msg.buf[parser->entity_start_position + entity_offset],
1488 Data_Buf_Size);
1489 entity_offset += Data_Buf_Size;
1490 if (Instr && Instr->IsVirtualFile) {
1491 int n = virtualDirCallback.write(
1492 Fp, Buf, Data_Buf_Size, Instr->Cookie, Instr->RequestCookie);
1493 if (n < 0) {
1494 ret_code = HTTP_INTERNAL_SERVER_ERROR;
1495 goto ExitFunction;
1496 }
1497 } else {
1498 size_t n = umock::stdio_h.fwrite(Buf, 1, Data_Buf_Size, Fp);
1499 if (n != Data_Buf_Size) {
1500 ret_code = HTTP_INTERNAL_SERVER_ERROR;
1501 goto ExitFunction;
1502 }
1503 }
1504 } while (parser->position != POS_COMPLETE ||
1505 entity_offset != parser->msg.entity.length);
1506ExitFunction:
1507 if (Instr && Instr->IsVirtualFile) {
1508 virtualDirCallback.close(Fp, Instr->Cookie, Instr->RequestCookie);
1509 } else {
1510 umock::stdio_h.fclose(Fp);
1511 }
1512
1513 return ret_code;
1514}
1515
1517} // anonymous namespace
1518} // namespace compa
1519
1520
1522 if (bWebServerState == WEB_SERVER_DISABLED) {
1525 compa::gAliasDoc.clear();
1526 pVirtualDirList = nullptr;
1527
1528 /* Initialize callbacks */
1529 virtualDirCallback.get_info = nullptr;
1530 virtualDirCallback.open = nullptr;
1531 virtualDirCallback.read = nullptr;
1532 virtualDirCallback.write = nullptr;
1533 virtualDirCallback.seek = nullptr;
1534 virtualDirCallback.close = nullptr;
1535
1536 bWebServerState = WEB_SERVER_ENABLED;
1538 }
1539}
1540
1541int web_server_set_alias(const char* a_alias_name, const char* a_alias_content,
1542 size_t a_alias_content_length,
1543 time_t a_last_modified) {
1544 TRACE("Executing web_server_set_alias()")
1545 return compa::gAliasDoc.set(a_alias_name, a_alias_content,
1546 a_alias_content_length, a_last_modified);
1547}
1548
1549int web_server_set_root_dir(const char* root_dir) {
1550 TRACE("Executing web_server_set_root_dir()")
1551 if (root_dir == nullptr)
1553
1554 int ret = membuffer_assign_str(&gDocumentRootDir, root_dir);
1555 if (ret != 0)
1556 return ret;
1557 /* remove trailing '/', if any */
1558 if (gDocumentRootDir.length > 0) {
1559 size_t index = gDocumentRootDir.length - 1; /* last char */
1560 if (gDocumentRootDir.buf[index] == '/')
1562 }
1563
1564 return 0;
1565}
1566
1567int web_server_set_cors(const char* cors_string) {
1568 int ret;
1569
1570 ret = membuffer_assign_str(&gWebserverCorsString, cors_string);
1571 if (ret != 0)
1572 return ret;
1573
1574 return 0;
1575}
1576
1578 /* INOUT */ http_message_t* a_req, SOCKINFO* a_info) {
1579 int ret;
1580 int timeout = -1;
1581 compa::resp_type rtype{compa::RESP_UNSPEC};
1582 membuffer headers;
1583 membuffer filename;
1584 compa::CXmlAlias xmldoc;
1585 SendInstruction RespInstr;
1586
1587 /* init */
1588 memset(&RespInstr, 0, sizeof(RespInstr));
1589 membuffer_init(&headers);
1590 membuffer_init(&filename);
1591
1592 /* Process request should create the different kind of header depending on
1593 * the type of request. */
1594 ret = compa::process_request_in(a_info, a_req, &rtype, &headers, &filename,
1595 &xmldoc, &RespInstr);
1596 if (ret != HTTP_OK) {
1597 /* send error code */
1598 http_SendStatusResponse(a_info, ret, a_req->major_version,
1599 a_req->minor_version);
1600 } else {
1601 /* send response */
1602 switch (rtype) {
1603 case compa::RESP_FILEDOC:
1604 http_SendMessage(a_info, &timeout, "Ibf", &RespInstr, headers.buf,
1605 headers.length, filename.buf);
1606 break;
1607 case compa::RESP_XMLDOC:
1608 http_SendMessage(a_info, &timeout, "Ibb", &RespInstr, headers.buf,
1609 headers.length, xmldoc.doc().data(),
1610 xmldoc.doc().length());
1611 xmldoc.release();
1612 break;
1613 case compa::RESP_WEBDOC:
1614 /*http_SendVirtualDirDoc(a_info, &timeout, "Ibf",
1615 &RespInstr,
1616 headers.buf, headers.length,
1617 filename.buf);*/
1618 http_SendMessage(a_info, &timeout, "Ibf", &RespInstr, headers.buf,
1619 headers.length, filename.buf);
1620 break;
1621 case compa::RESP_HEADERS:
1622 /* headers only */
1623 http_SendMessage(a_info, &timeout, "b", headers.buf,
1624 headers.length);
1625 break;
1626 case compa::RESP_POST:
1627 /* headers only */
1628 ret = compa::http_RecvPostMessage(a_parser, a_info, filename.buf,
1629 &RespInstr);
1630 /* Send response. */
1631 http_MakeMessage(&headers, 1, 1, "RTLSXcCc", ret, "text/html",
1632 &RespInstr, X_USER_AGENT);
1633 http_SendMessage(a_info, &timeout, "b", headers.buf,
1634 headers.length);
1635 break;
1636 default:
1637 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
1638 "webserver: Invalid response type received.\n");
1639 assert(0);
1640 }
1641 }
1642 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
1643 "webserver: request processed...\n");
1644 membuffer_destroy(&headers);
1645 membuffer_destroy(&filename);
1646}
1647
1649 TRACE("Executing SetHTTPGetCallback()")
1650 gGetCallback = callback;
1651}
1652
1654 if (bWebServerState == WEB_SERVER_ENABLED) {
1657 compa::gAliasDoc.clear();
1658 bWebServerState = WEB_SERVER_DISABLED;
1659 SetHTTPGetCallback(nullptr);
1660 }
1661}
#define LINE_SIZE
Definition API.hpp:45
#define UPNP_USING_CHUNKED
Definition API.hpp:52
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.
membuffer gDocumentRootDir
Definition webserver.hpp:44
#define X_USER_AGENT
Can be overwritten by configure CFLAGS argument.
Definition webserver.hpp:86
off_t RangeOffset
member variable
Definition webserver.hpp:60
const void * CorsHeader
Definition webserver.hpp:67
long RecvWriteSize
Recv from the network and write into local file.
Definition webserver.hpp:65
off_t ReadSendSize
Read from local source and send on the network.
Definition webserver.hpp:63
const void * RequestCookie
Cookie associated with the request.
Definition webserver.hpp:71
int IsRangeActive
member variable
Definition webserver.hpp:56
const void * Cookie
Cookie associated with the virtualDir.
Definition webserver.hpp:69
char AcceptLanguageHeader[200]
member variable
Definition webserver.hpp:59
int IsTrailers
member variable
Definition webserver.hpp:57
int IsVirtualFile
member variable
Definition webserver.hpp:52
char RangeHeader[200]
member variable
Definition webserver.hpp:58
int IsChunkActive
member variable
Definition webserver.hpp:53
Send instruction.
Definition webserver.hpp:49
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.
size_t size
Size of the buffer.
Definition uri.hpp:50
const char * buff
Buffer.
Definition uri.hpp:49
token pathquery
Member variable.
Definition uri.hpp:70
Represents a URI used in parse_uri and elsewhere.
Definition uri.hpp:64
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
UPnPsdk_VIS void remove_dot_segments(std::string &a_path)
Remove dot segments from a path.
Definition uri.cpp:73
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.
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.
Performs HTTP read and write messages.
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 without terminating '\0' (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 void decode_esc_chars(std::string &a_encoded)
Replaces http percent encoded characters with their character representation.
Definition uri.cpp:133
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.
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
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.
struct VirtualDirCallbacks virtualDirCallback
This structure is for virtual directory callbacks.
Definition upnpapi.cpp:99
virtualDirList * pVirtualDirList
Pointer to the virtual directory list.
Definition upnpapi.cpp:102
membuffer gWebserverCorsString
Definition webserver.hpp:46
WebServerState bWebServerState
Flag to indicate the state of web server.
Definition upnpapi.cpp:120
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.