1 /* $Id: upnphttp.c,v 1.81 2012/10/04 22:09:34 nanard Exp $ */
3 * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * Author : Thomas Bernard
5 * Copyright (c) 2005-2012 Thomas Bernard
6 * This software is subject to the conditions detailed in the
7 * LICENCE file included in this distribution.
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <sys/param.h>
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
22 #ifdef ENABLE_HTTP_DATE
26 #include "upnpdescgen.h"
27 #include "miniupnpdpath.h"
29 #include "upnpevents.h"
30 #include "upnputils.h"
35 struct upnphttp
* ret
;
38 ret
= (struct upnphttp
*)malloc(sizeof(struct upnphttp
));
41 memset(ret
, 0, sizeof(struct upnphttp
));
43 if(!set_non_blocking(s
))
44 syslog(LOG_WARNING
, "New_upnphttp::set_non_blocking(): %m");
49 CloseSocket_upnphttp(struct upnphttp
* h
)
51 if(close(h
->socket
) < 0)
53 syslog(LOG_ERR
, "CloseSocket_upnphttp: close(%d): %m", h
->socket
);
60 Delete_upnphttp(struct upnphttp
* h
)
65 CloseSocket_upnphttp(h
);
74 /* parse HttpHeaders of the REQUEST */
76 ParseHttpHeaders(struct upnphttp
* h
)
83 /* TODO : check if req_buf, contentoff are ok */
84 while(line
< (h
->req_buf
+ h
->req_contentoff
))
86 colon
= strchr(line
, ':');
89 if(strncasecmp(line
, "Content-Length", 14)==0)
92 while(*p
< '0' || *p
> '9')
94 h
->req_contentlen
= atoi(p
);
95 /*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
96 printf(" readbufflen=%d contentoff = %d\n",
97 h->req_buflen, h->req_contentoff);*/
99 else if(strncasecmp(line
, "SOAPAction", 10)==0)
103 while(*p
== ':' || *p
== ' ' || *p
== '\t')
109 if((p
[0] == '"' && p
[n
-1] == '"')
110 || (p
[0] == '\'' && p
[n
-1] == '\''))
114 h
->req_soapActionOff
= p
- h
->req_buf
;
115 h
->req_soapActionLen
= n
;
117 else if(strncasecmp(line
, "accept-language", 15) == 0)
121 while(*p
== ':' || *p
== ' ' || *p
== '\t')
125 syslog(LOG_DEBUG
, "accept-language HTTP header : '%.*s'", n
, p
);
126 /* keep only the 1st accepted language */
128 while(p
[n
]>' ' && p
[n
] != ',')
130 if(n
>= (int)sizeof(h
->accept_language
))
131 n
= (int)sizeof(h
->accept_language
) - 1;
132 memcpy(h
->accept_language
, p
, n
);
133 h
->accept_language
[n
] = '\0';
135 else if(strncasecmp(line
, "expect", 6) == 0)
139 while(*p
== ':' || *p
== ' ' || *p
== '\t')
143 if(strncasecmp(p
, "100-continue", 12) == 0) {
144 h
->respflags
|= FLAG_CONTINUE
;
145 syslog(LOG_DEBUG
, "\"Expect: 100-Continue\" header detected");
149 else if(strncasecmp(line
, "Callback", 8)==0)
152 while(*p
!= '<' && *p
!= '\r' )
155 while(p
[n
] != '>' && p
[n
] != '\r' )
157 h
->req_CallbackOff
= p
+ 1 - h
->req_buf
;
158 h
->req_CallbackLen
= MAX(0, n
- 1);
160 else if(strncasecmp(line
, "SID", 3)==0)
166 while(!isspace(p
[n
]))
168 h
->req_SIDOff
= p
- h
->req_buf
;
171 /* Timeout: Seconds-nnnn */
173 Recommended. Requested duration until subscription expires,
174 either number of seconds or infinite. Recommendation
175 by a UPnP Forum working committee. Defined by UPnP vendor.
176 Consists of the keyword "Second-" followed (without an
177 intervening space) by either an integer or the keyword "infinite". */
178 else if(strncasecmp(line
, "Timeout", 7)==0)
183 if(strncasecmp(p
, "Second-", 7)==0) {
184 h
->req_Timeout
= atoi(p
+7);
188 else if(strncasecmp(line
, "nt", 2)==0)
194 while(!isspace(p
[n
]))
196 h
->req_NTOff
= p
- h
->req_buf
;
202 while(!(line
[0] == '\r' && line
[1] == '\n'))
208 /* very minimalistic 404 error message */
210 Send404(struct upnphttp
* h
)
212 static const char body404
[] =
213 "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
214 "<BODY><H1>Not Found</H1>The requested URL was not found"
215 " on this server.</BODY></HTML>\r\n";
217 h
->respflags
= FLAG_HTML
;
218 BuildResp2_upnphttp(h
, 404, "Not Found",
219 body404
, sizeof(body404
) - 1);
220 SendRespAndClose_upnphttp(h
);
224 Send405(struct upnphttp
* h
)
226 static const char body405
[] =
227 "<HTML><HEAD><TITLE>405 Method Not Allowed</TITLE></HEAD>"
228 "<BODY><H1>Method Not Allowed</H1>The HTTP Method "
229 "is not allowed on this resource.</BODY></HTML>\r\n";
231 h
->respflags
|= FLAG_HTML
;
232 BuildResp2_upnphttp(h
, 405, "Method Not Allowed",
233 body405
, sizeof(body405
) - 1);
234 SendRespAndClose_upnphttp(h
);
237 /* very minimalistic 501 error message */
239 Send501(struct upnphttp
* h
)
241 static const char body501
[] =
242 "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
243 "<BODY><H1>Not Implemented</H1>The HTTP Method "
244 "is not implemented by this server.</BODY></HTML>\r\n";
246 h
->respflags
= FLAG_HTML
;
247 BuildResp2_upnphttp(h
, 501, "Not Implemented",
248 body501
, sizeof(body501
) - 1);
249 SendRespAndClose_upnphttp(h
);
253 findendheaders(const char * s
, int len
)
257 if(s
[0]=='\r' && s
[1]=='\n' && s
[2]=='\r' && s
[3]=='\n')
264 #ifdef HAS_DUMMY_SERVICE
266 sendDummyDesc(struct upnphttp
* h
)
268 static const char xml_desc
[] = "<?xml version=\"1.0\"?>\r\n"
269 "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
275 " <serviceStateTable />"
277 BuildResp_upnphttp(h
, xml_desc
, sizeof(xml_desc
)-1);
278 SendRespAndClose_upnphttp(h
);
282 /* Sends the description generated by the parameter */
284 sendXMLdesc(struct upnphttp
* h
, char * (f
)(int *))
291 static const char error500
[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
292 "</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
293 syslog(LOG_ERR
, "Failed to generate XML description");
294 h
->respflags
= FLAG_HTML
;
295 BuildResp2_upnphttp(h
, 500, "Internal Server Error",
296 error500
, sizeof(error500
)-1);
300 BuildResp_upnphttp(h
, desc
, len
);
302 SendRespAndClose_upnphttp(h
);
306 /* ProcessHTTPPOST_upnphttp()
307 * executes the SOAP query if it is possible */
309 ProcessHTTPPOST_upnphttp(struct upnphttp
* h
)
311 if((h
->req_buflen
- h
->req_contentoff
) >= h
->req_contentlen
)
313 /* the request body is received */
314 if(h
->req_soapActionOff
> 0)
316 /* we can process the request */
317 syslog(LOG_INFO
, "SOAPAction: %.*s",
318 h
->req_soapActionLen
, h
->req_buf
+ h
->req_soapActionOff
);
320 h
->req_buf
+ h
->req_soapActionOff
,
321 h
->req_soapActionLen
);
325 static const char err400str
[] =
326 "<html><body>Bad request</body></html>";
327 syslog(LOG_INFO
, "No SOAPAction in HTTP headers");
328 h
->respflags
= FLAG_HTML
;
329 BuildResp2_upnphttp(h
, 400, "Bad Request",
330 err400str
, sizeof(err400str
) - 1);
331 SendRespAndClose_upnphttp(h
);
334 else if(h
->respflags
& FLAG_CONTINUE
)
336 /* Sending the 100 Continue response */
338 h
->res_buf
= malloc(256);
339 h
->res_buf_alloclen
= 256;
341 h
->res_buflen
= snprintf(h
->res_buf
, h
->res_buf_alloclen
,
342 "%s 100 Continue\r\n\r\n", h
->HttpVer
);
344 h
->state
= ESendingContinue
;
345 if(SendResp_upnphttp(h
))
346 h
->state
= EWaitingForHttpContent
;
350 /* waiting for remaining data */
351 h
->state
= EWaitingForHttpContent
;
357 * returns 0 if the callback header value is not valid
361 checkCallbackURL(struct upnphttp
* h
)
368 if(h
->req_CallbackOff
<= 0 || h
->req_CallbackLen
< 8)
370 if(memcmp(h
->req_buf
+ h
->req_CallbackOff
, "http://", 7) != 0)
374 p
= h
->req_buf
+ h
->req_CallbackOff
+ 7;
378 while(*p
!= ']' && i
< (sizeof(addrstr
)-1)
379 && p
< (h
->req_buf
+ h
->req_CallbackOff
+ h
->req_CallbackLen
))
380 addrstr
[i
++] = *(p
++);
382 while(*p
!= '/' && *p
!= ':' && i
< (sizeof(addrstr
)-1)
383 && p
< (h
->req_buf
+ h
->req_CallbackOff
+ h
->req_CallbackLen
))
384 addrstr
[i
++] = *(p
++);
388 struct in6_addr addr
;
389 if(inet_pton(AF_INET6
, addrstr
, &addr
) <= 0)
393 || (0!=memcmp(&addr
, &(h
->clientaddr_v6
), sizeof(struct in6_addr
))))
400 if(inet_pton(AF_INET
, addrstr
, &addr
) <= 0)
404 if(!IN6_IS_ADDR_V4MAPPED(&(h
->clientaddr_v6
)))
406 if(0!=memcmp(&addr
, ((const char *)&(h
->clientaddr_v6
) + 12), 4))
409 if(0!=memcmp(&addr
, &(h
->clientaddr
), sizeof(struct in_addr
)))
413 if(0!=memcmp(&addr
, &(h
->clientaddr
), sizeof(struct in_addr
)))
421 ProcessHTTPSubscribe_upnphttp(struct upnphttp
* h
, const char * path
)
424 syslog(LOG_DEBUG
, "ProcessHTTPSubscribe %s", path
);
425 syslog(LOG_DEBUG
, "Callback '%.*s' Timeout=%d",
426 h
->req_CallbackLen
, h
->req_buf
+ h
->req_CallbackOff
,
428 syslog(LOG_DEBUG
, "SID '%.*s'", h
->req_SIDLen
, h
->req_buf
+ h
->req_SIDOff
);
429 if((h
->req_CallbackOff
<= 0) && (h
->req_SIDOff
<= 0)) {
430 /* Missing or invalid CALLBACK : 412 Precondition Failed.
431 * If CALLBACK header is missing or does not contain a valid HTTP URL,
432 * the publisher must respond with HTTP error 412 Precondition Failed*/
433 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
434 SendRespAndClose_upnphttp(h
);
436 /* - add to the subscriber list
437 * - respond HTTP/x.x 200 OK
438 * - Send the initial event message */
439 /* Server:, SID:; Timeout: Second-(xx|infinite) */
440 /* Check that the callback URL is on the same IP as
441 * the request, and not on the internet, nor on ourself (DOS attack ?) */
442 if(h
->req_CallbackOff
> 0) {
444 /* SID: and Callback: are incompatible */
445 if(h
->req_SIDOff
> 0) {
446 syslog(LOG_WARNING
, "Both Callback: and SID: in SUBSCRIBE");
447 BuildResp2_upnphttp(h
, 400, "Incompatible header fields", 0, 0);
448 /* "NT: upnp:event" header is mandatory */
449 } else if(h
->req_NTOff
<= 0 || h
->req_NTLen
!= 10 ||
450 0 != memcmp("upnp:event", h
->req_buf
+ h
->req_NTOff
, 10)) {
451 syslog(LOG_WARNING
, "Invalid NT in SUBSCRIBE %.*s",
452 h
->req_NTLen
, h
->req_buf
+ h
->req_NTOff
);
453 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
456 if(checkCallbackURL(h
)) {
457 sid
= upnpevents_addSubscriber(path
, h
->req_buf
+ h
->req_CallbackOff
,
458 h
->req_CallbackLen
, h
->req_Timeout
);
459 h
->respflags
= FLAG_TIMEOUT
;
461 syslog(LOG_DEBUG
, "generated sid=%s", sid
);
462 h
->respflags
|= FLAG_SID
;
465 BuildResp_upnphttp(h
, 0, 0);
467 syslog(LOG_WARNING
, "Invalid Callback in SUBSCRIBE %.*s",
468 h
->req_CallbackLen
, h
->req_buf
+ h
->req_CallbackOff
);
469 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
472 /* subscription renew */
474 412 Precondition Failed. If a SID does not correspond to a known,
475 un-expired subscription, the publisher must respond
476 with HTTP error 412 Precondition Failed. */
478 /* SID: and NT: headers are incompatibles */
479 if(h
->req_NTOff
> 0) {
480 syslog(LOG_WARNING
, "Both NT: and SID: in SUBSCRIBE");
481 BuildResp2_upnphttp(h
, 400, "Incompatible header fields", 0, 0);
484 if(renewSubscription(h
->req_buf
+ h
->req_SIDOff
, h
->req_SIDLen
,
485 h
->req_Timeout
) < 0) {
486 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
488 h
->respflags
= FLAG_TIMEOUT
;
489 BuildResp_upnphttp(h
, 0, 0);
492 SendRespAndClose_upnphttp(h
);
497 ProcessHTTPUnSubscribe_upnphttp(struct upnphttp
* h
, const char * path
)
499 syslog(LOG_DEBUG
, "ProcessHTTPUnSubscribe %s", path
);
500 syslog(LOG_DEBUG
, "SID '%.*s'", h
->req_SIDLen
, h
->req_buf
+ h
->req_SIDOff
);
501 /* Remove from the list */
503 if(h
->req_SIDOff
<= 0 || h
->req_SIDLen
== 0) {
504 /* SID: header missing or empty */
505 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
506 } else if(h
->req_CallbackOff
> 0 || h
->req_NTOff
> 0) {
507 /* no NT: or Callback: header must be present */
508 BuildResp2_upnphttp(h
, 400, "Incompatible header fields", 0, 0);
511 if(upnpevents_removeSubscriber(h
->req_buf
+ h
->req_SIDOff
, h
->req_SIDLen
) < 0) {
512 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
514 BuildResp_upnphttp(h
, 0, 0);
516 SendRespAndClose_upnphttp(h
);
520 /* Parse and process Http Query
521 * called once all the HTTP headers have been received. */
523 ProcessHttpQuery_upnphttp(struct upnphttp
* h
)
525 static const struct {
529 { ROOTDESC_PATH
, genRootDesc
},
530 { WANIPC_PATH
, genWANIPCn
},
531 { WANCFG_PATH
, genWANCfg
},
532 #ifdef HAS_DUMMY_SERVICE
535 #ifdef ENABLE_L3F_SERVICE
538 #ifdef ENABLE_6FC_SERVICE
539 { WANIP6FC_PATH
, gen6FC
},
541 #ifdef ENABLE_DP_SERVICE
546 char HttpCommand
[16];
554 for(i
= 0; i
<15 && *p
!= ' ' && *p
!= '\r'; i
++)
555 HttpCommand
[i
] = *(p
++);
556 HttpCommand
[i
] = '\0';
559 for(i
= 0; i
<127 && *p
!= ' ' && *p
!= '\r'; i
++)
564 HttpVer
= h
->HttpVer
;
565 for(i
= 0; i
<15 && *p
!= '\r'; i
++)
568 syslog(LOG_INFO
, "HTTP REQUEST : %s %s (%s)",
569 HttpCommand
, HttpUrl
, HttpVer
);
571 if(strcmp("POST", HttpCommand
) == 0)
573 h
->req_command
= EPost
;
574 ProcessHTTPPOST_upnphttp(h
);
576 else if(strcmp("GET", HttpCommand
) == 0)
578 h
->req_command
= EGet
;
579 for(i
=0; path_desc
[i
].path
; i
++) {
580 if(strcasecmp(path_desc
[i
].path
, HttpUrl
) == 0) {
582 sendXMLdesc(h
, path_desc
[i
].f
);
584 #ifdef HAS_DUMMY_SERVICE
592 if(0 == memcmp(HttpUrl
, "/ctl/", 5)) {
593 /* 405 Method Not Allowed
595 h
->respflags
= FLAG_ALLOW_POST
;
600 if(0 == memcmp(HttpUrl
, "/evt/", 5)) {
601 /* 405 Method Not Allowed
602 * Allow: SUBSCRIBE, UNSUBSCRIBE */
603 h
->respflags
= FLAG_ALLOW_SUB_UNSUB
;
608 syslog(LOG_NOTICE
, "%s not found, responding ERROR 404", HttpUrl
);
612 else if(strcmp("SUBSCRIBE", HttpCommand
) == 0)
614 h
->req_command
= ESubscribe
;
615 ProcessHTTPSubscribe_upnphttp(h
, HttpUrl
);
617 else if(strcmp("UNSUBSCRIBE", HttpCommand
) == 0)
619 h
->req_command
= EUnSubscribe
;
620 ProcessHTTPUnSubscribe_upnphttp(h
, HttpUrl
);
623 else if(strcmp("SUBSCRIBE", HttpCommand
) == 0)
625 syslog(LOG_NOTICE
, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
631 syslog(LOG_NOTICE
, "Unsupported HTTP Command %s", HttpCommand
);
638 Process_upnphttp(struct upnphttp
* h
)
646 case EWaitingForHttpRequest
:
647 n
= recv(h
->socket
, buf
, sizeof(buf
), 0);
650 if(errno
!= EAGAIN
&&
651 errno
!= EWOULDBLOCK
&&
654 syslog(LOG_ERR
, "recv (state0): %m");
655 h
->state
= EToDelete
;
657 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
661 syslog(LOG_WARNING
, "HTTP Connection from %s closed unexpectedly",
662 inet_ntoa(h
->clientaddr
)); //!!TB - added client address
663 h
->state
= EToDelete
;
667 const char * endheaders
;
668 /* if 1st arg of realloc() is null,
669 * realloc behaves the same as malloc() */
670 h
->req_buf
= (char *)realloc(h
->req_buf
, n
+ h
->req_buflen
+ 1);
671 memcpy(h
->req_buf
+ h
->req_buflen
, buf
, n
);
673 h
->req_buf
[h
->req_buflen
] = '\0';
674 /* search for the string "\r\n\r\n" */
675 endheaders
= findendheaders(h
->req_buf
, h
->req_buflen
);
678 h
->req_contentoff
= endheaders
- h
->req_buf
+ 4;
679 ProcessHttpQuery_upnphttp(h
);
683 case EWaitingForHttpContent
:
684 n
= recv(h
->socket
, buf
, sizeof(buf
), 0);
687 if(errno
!= EAGAIN
&&
688 errno
!= EWOULDBLOCK
&&
691 syslog(LOG_ERR
, "recv (state1): %m");
692 h
->state
= EToDelete
;
694 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
698 syslog(LOG_WARNING
, "HTTP Connection from %s closed unexpectedly",
699 inet_ntoa(h
->clientaddr
)); //!!TB - added client address
700 h
->state
= EToDelete
;
704 void * tmp
= realloc(h
->req_buf
, n
+ h
->req_buflen
);
707 syslog(LOG_ERR
, "memory allocation error %m");
708 h
->state
= EToDelete
;
713 memcpy(h
->req_buf
+ h
->req_buflen
, buf
, n
);
715 if((h
->req_buflen
- h
->req_contentoff
) >= h
->req_contentlen
)
717 ProcessHTTPPOST_upnphttp(h
);
722 case ESendingContinue
:
723 if(SendResp_upnphttp(h
))
724 h
->state
= EWaitingForHttpContent
;
726 case ESendingAndClosing
:
727 SendRespAndClose_upnphttp(h
);
730 syslog(LOG_WARNING
, "Unexpected state: %d", h
->state
);
734 static const char httpresphead
[] =
736 "Content-Type: %s\r\n"
737 "Connection: close\r\n"
738 "Content-Length: %d\r\n"
739 "Server: " MINIUPNPD_SERVER_STRING
"\r\n"
742 "<?xml version=\"1.0\"?>\n"
743 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
744 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
750 /* with response code and response message
751 * also allocate enough memory */
754 BuildHeader_upnphttp(struct upnphttp
* h
, int respcode
,
755 const char * respmsg
,
760 h
->res_buf_alloclen
< ((int)sizeof(httpresphead
) + 256 + bodylen
)) {
763 templen
= sizeof(httpresphead
) + 256 + bodylen
;
764 h
->res_buf
= (char *)malloc(templen
);
766 syslog(LOG_ERR
, "malloc error in BuildHeader_upnphttp()");
769 h
->res_buf_alloclen
= templen
;
772 h
->res_buflen
= snprintf(h
->res_buf
, h
->res_buf_alloclen
,
773 httpresphead
, h
->HttpVer
,
775 (h
->respflags
&FLAG_HTML
)?"text/html":"text/xml; charset=\"utf-8\"",
777 /* Content-Type MUST be 'text/xml; charset="utf-8"' according to UDA v1.1 */
778 /* Content-Type MUST be 'text/xml' according to UDA v1.0 */
779 /* Additional headers */
780 #ifdef ENABLE_HTTP_DATE
787 /* %a and %b depend on locale */
788 strftime(http_date
, sizeof(http_date
),
789 "%a, %d %b %Y %H:%M:%S GMT", &tm
);
790 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
791 h
->res_buf_alloclen
- h
->res_buflen
,
792 "Date: %s\r\n", http_date
);
796 if(h
->respflags
& FLAG_TIMEOUT
) {
797 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
798 h
->res_buf_alloclen
- h
->res_buflen
,
801 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
802 h
->res_buf_alloclen
- h
->res_buflen
,
803 "%d\r\n", h
->req_Timeout
);
805 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
806 h
->res_buf_alloclen
- h
->res_buflen
,
810 if(h
->respflags
& FLAG_SID
) {
811 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
812 h
->res_buf_alloclen
- h
->res_buflen
,
813 "SID: %s\r\n", h
->res_SID
);
816 if(h
->respflags
& FLAG_ALLOW_POST
) {
817 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
818 h
->res_buf_alloclen
- h
->res_buflen
,
819 "Allow: %s\r\n", "POST");
820 } else if(h
->respflags
& FLAG_ALLOW_SUB_UNSUB
) {
821 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
822 h
->res_buf_alloclen
- h
->res_buflen
,
823 "Allow: %s\r\n", "SUBSCRIBE, UNSUBSCRIBE");
825 if(h
->accept_language
[0] != '\0') {
826 /* defaulting to "en" */
827 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
828 h
->res_buf_alloclen
- h
->res_buflen
,
829 "Content-Language: %s\r\n",
830 h
->accept_language
[0] == '*' ? "en" : h
->accept_language
);
832 h
->res_buf
[h
->res_buflen
++] = '\r';
833 h
->res_buf
[h
->res_buflen
++] = '\n';
834 if(h
->res_buf_alloclen
< (h
->res_buflen
+ bodylen
))
837 tmp
= (char *)realloc(h
->res_buf
, (h
->res_buflen
+ bodylen
));
841 h
->res_buf_alloclen
= h
->res_buflen
+ bodylen
;
845 syslog(LOG_ERR
, "realloc error in BuildHeader_upnphttp()");
851 BuildResp2_upnphttp(struct upnphttp
* h
, int respcode
,
852 const char * respmsg
,
853 const char * body
, int bodylen
)
855 BuildHeader_upnphttp(h
, respcode
, respmsg
, bodylen
);
857 memcpy(h
->res_buf
+ h
->res_buflen
, body
, bodylen
);
858 h
->res_buflen
+= bodylen
;
861 /* responding 200 OK ! */
863 BuildResp_upnphttp(struct upnphttp
* h
,
864 const char * body
, int bodylen
)
866 BuildResp2_upnphttp(h
, 200, "OK", body
, bodylen
);
870 SendResp_upnphttp(struct upnphttp
* h
)
874 while (h
->res_sent
< h
->res_buflen
)
876 n
= send(h
->socket
, h
->res_buf
+ h
->res_sent
,
877 h
->res_buflen
- h
->res_sent
, 0);
881 continue; /* try again immediatly */
882 if(errno
== EAGAIN
|| errno
== EWOULDBLOCK
)
884 /* try again later */
887 syslog(LOG_ERR
, "send(res_buf): %m");
888 break; /* avoid infinite loop */
892 syslog(LOG_ERR
, "send(res_buf): %d bytes sent (out of %d)",
893 h
->res_sent
, h
->res_buflen
);
901 return 1; /* finished */
905 SendRespAndClose_upnphttp(struct upnphttp
* h
)
909 while (h
->res_sent
< h
->res_buflen
)
911 n
= send(h
->socket
, h
->res_buf
+ h
->res_sent
,
912 h
->res_buflen
- h
->res_sent
, 0);
916 continue; /* try again immediatly */
917 if(errno
== EAGAIN
|| errno
== EWOULDBLOCK
)
919 /* try again later */
920 h
->state
= ESendingAndClosing
;
923 syslog(LOG_ERR
, "send(res_buf): %m");
924 break; /* avoid infinite loop */
928 syslog(LOG_ERR
, "send(res_buf): %d bytes sent (out of %d)",
929 h
->res_sent
, h
->res_buflen
);
937 CloseSocket_upnphttp(h
);