1 /*******************************************************************************
2  *
3  * Copyright (c) 2000-2003 Intel Corporation
4  * All rights reserved.
5  * Copyright (c) 2012 France Telecom All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * - Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  * - Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  * - Neither name of Intel Corporation nor the names of its contributors
16  * may be used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  ******************************************************************************/
32 
33 /*!
34  * \file
35  *
36  * Purpose: This file defines the functionality making use of the http.
37  * It defines functions to receive messages, process messages, send messages.
38  */
39 
40 #include "config.h"
41 
42 #include "httpreadwrite.h"
43 
44 #include "UpnpExtraHeaders.h"
45 #include "UpnpFileInfo.h"
46 #include "UpnpInet.h"
47 #include "UpnpIntTypes.h"
48 #include "UpnpStdInt.h"
49 #include "membuffer.h"
50 #include "sock.h"
51 #include "statcodes.h"
52 #include "unixutil.h"
53 #include "upnp.h"
54 #include "upnpapi.h"
55 #include "uri.h"
56 #include "webserver.h"
57 
58 #include <assert.h>
59 #include <stdarg.h>
60 #include <string.h>
61 
62 #ifdef _WIN32
63 	#include <malloc.h>
64 	#define fseeko fseek
65 	#if defined(_MSC_VER) && _MSC_VER < 1900
66 		#define snprintf _snprintf
67 	#endif
68 #else /* _WIN32 */
69 	#include <arpa/inet.h>
70 	#include <sys/time.h>
71 	#include <sys/types.h>
72 	#include <sys/utsname.h>
73 	#include <sys/wait.h>
74 #endif /* _WIN32 */
75 
76 /*
77  * Please, do not change these to const int while MSVC cannot understand
78  * const int in array dimensions.
79  */
80 /*
81 const int CHUNK_HEADER_SIZE = 10;
82 const int CHUNK_TAIL_SIZE = 10;
83 */
84 #define CHUNK_HEADER_SIZE (size_t)10
85 #define CHUNK_TAIL_SIZE (size_t)10
86 
87 #ifndef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS
88 
89 	/* in seconds */
90 	#define DEFAULT_TCP_CONNECT_TIMEOUT 5
91 
92 /*!
93  * \brief Checks socket connection and wait if it is not connected.
94  * It should be called just after connect.
95  *
96  * \return 0 if successful, else -1.
97  */
Check_Connect_And_Wait_Connection(SOCKET sock,int connect_res)98 static int Check_Connect_And_Wait_Connection(
99 	/*! [in] socket. */
100 	SOCKET sock,
101 	/*! [in] result of connect. */
102 	int connect_res)
103 {
104 	struct timeval tmvTimeout = {DEFAULT_TCP_CONNECT_TIMEOUT, 0};
105 	int result;
106 	#ifdef _WIN32
107 	struct fd_set fdSet;
108 	#else
109 	fd_set fdSet;
110 	#endif
111 	FD_ZERO(&fdSet);
112 	FD_SET(sock, &fdSet);
113 
114 	if (connect_res < 0) {
115 	#ifdef _WIN32
116 		if (WSAEWOULDBLOCK == WSAGetLastError()) {
117 	#else
118 		if (EINPROGRESS == errno) {
119 	#endif
120 			result = select(
121 				sock + 1, NULL, &fdSet, NULL, &tmvTimeout);
122 			if (result < 0) {
123 	#ifdef _WIN32
124 					/* WSAGetLastError(); */
125 	#else
126 					/* errno */
127 	#endif
128 				return -1;
129 			} else if (result == 0) {
130 				/* timeout */
131 				return -1;
132 	#ifndef _WIN32
133 			} else {
134 				int valopt = 0;
135 				socklen_t len = sizeof(valopt);
136 				if (getsockopt(sock,
137 					    SOL_SOCKET,
138 					    SO_ERROR,
139 					    (void *)&valopt,
140 					    &len) < 0) {
141 					/* failed to read delayed error */
142 					return -1;
143 				} else if (valopt) {
144 					/* delayed error = valopt */
145 					return -1;
146 				}
147 	#endif
148 			}
149 		}
150 	}
151 
152 	return 0;
153 }
154 #endif /* UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */
155 
156 static int private_connect(
157 	SOCKET sockfd, const struct sockaddr *serv_addr, socklen_t addrlen)
158 {
159 #ifndef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS
160 	int ret = sock_make_no_blocking(sockfd);
161 	if (ret != -1) {
162 		ret = connect(sockfd, serv_addr, addrlen);
163 		ret = Check_Connect_And_Wait_Connection(sockfd, ret);
164 		if (ret != -1) {
165 			ret = sock_make_blocking(sockfd);
166 		}
167 	}
168 
169 	return ret;
170 #else
171 	return connect(sockfd, serv_addr, addrlen);
172 #endif /* UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */
173 }
174 
175 #ifdef _WIN32
176 struct tm *http_gmtime_r(const time_t *clock, struct tm *result)
177 {
178 	if (clock == NULL || *clock < 0 || result == NULL)
179 		return NULL;
180 
181 	/* gmtime in VC runtime is thread safe. */
182 	*result = *gmtime(clock);
183 	return result;
184 }
185 #endif
186 
187 static int get_hoststr(
188 	const char *url_str, const char **hoststr, size_t *hostlen)
189 {
190 	const char *start;
191 	const char *finish;
192 	int ret_code = UPNP_E_INVALID_URL;
193 
194 	start = strstr(url_str, "//");
195 	if (!start) {
196 		goto end_function;
197 	}
198 	start += 2;
199 	finish = strchr(start, '/');
200 	if (finish) {
201 		*hostlen = (size_t)(finish - start);
202 	} else {
203 		*hostlen = strlen(start);
204 	}
205 	*hoststr = start;
206 
207 	ret_code = UPNP_E_SUCCESS;
208 
209 end_function:
210 	return ret_code;
211 }
212 
213 static void copy_msg_headers(LinkedList *msgHeaders, UpnpString *headers)
214 {
215 	(void)msgHeaders;
216 	(void)headers;
217 	return;
218 /* TODO: */
219 #if 0
220 	ListNode *node;
221 	UpnpHttpHeader *header;
222 	http_header_t *msgHeader;
223 	if (headers) {
224 		ListInit(headers, NULL, (free_function) UpnpHttpHeader_delete);
225 		node = ListHead(msgHeaders);
226 		while(node) {
227 			msgHeader = (http_header_t*) node->item;
228 			header = UpnpHttpHeader_new();
229 			UpnpHttpHeader_strncpy_Name(
230 				header,
231 				msgHeader->name.buf,
232 				msgHeader->name.length);
233 			UpnpHttpHeader_strncpy_Value(
234 				header,
235 				msgHeader->value.buf,
236 				msgHeader->value.length);
237 			node = ListNext(msgHeaders, node);
238 		}
239 	}
240 #endif
241 }
242 
243 int http_FixUrl(uri_type *url, uri_type *fixed_url)
244 {
245 	const char *temp_path = "/";
246 
247 	*fixed_url = *url;
248 #ifdef UPNP_ENABLE_OPEN_SSL
249 	if (token_string_casecmp(&fixed_url->scheme, "http") != 0 &&
250 		token_string_casecmp(&fixed_url->scheme, "https") != 0) {
251 		return UPNP_E_INVALID_URL;
252 	}
253 #else
254 	if (token_string_casecmp(&fixed_url->scheme, "http") != 0) {
255 		return UPNP_E_INVALID_URL;
256 	}
257 #endif
258 	if (fixed_url->hostport.text.size == (size_t)0) {
259 		return UPNP_E_INVALID_URL;
260 	}
261 	/* set pathquery to "/" if it is empty */
262 	if (fixed_url->pathquery.size == (size_t)0) {
263 		fixed_url->pathquery.buff = temp_path;
264 		fixed_url->pathquery.size = (size_t)1;
265 	}
266 
267 	return UPNP_E_SUCCESS;
268 }
269 
270 int http_FixStrUrl(const char *urlstr, size_t urlstrlen, uri_type *fixed_url)
271 {
272 	uri_type url;
273 
274 	if (parse_uri(urlstr, urlstrlen, &url) != HTTP_SUCCESS) {
275 		return UPNP_E_INVALID_URL;
276 	}
277 
278 	return http_FixUrl(&url, fixed_url);
279 }
280 
281 /************************************************************************
282  * Function: http_Connect
283  *
284  * Parameters:
285  *	IN uri_type* destination_url;	URL containing destination information
286  *	OUT uri_type *url;		Fixed and corrected URL
287  *
288  * Description:
289  *	Gets destination address from URL and then connects to the remote end
290  *
291  *  Returns:
292  *	socket descriptor on success
293  *	UPNP_E_OUTOF_SOCKET
294  *	UPNP_E_SOCKET_CONNECT on error
295  ************************************************************************/
296 SOCKET http_Connect(uri_type *destination_url, uri_type *url)
297 {
298 	SOCKET connfd;
299 	socklen_t sockaddr_len;
300 	int ret_connect;
301 	char errorBuffer[ERROR_BUFFER_LEN];
302 
303 	http_FixUrl(destination_url, url);
304 
305 	connfd = socket((int)url->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
306 	if (connfd == INVALID_SOCKET) {
307 		return (SOCKET)(UPNP_E_OUTOF_SOCKET);
308 	}
309 	sockaddr_len = (socklen_t)(url->hostport.IPaddress.ss_family == AF_INET6
310 					   ? sizeof(struct sockaddr_in6)
311 					   : sizeof(struct sockaddr_in));
312 	ret_connect = private_connect(connfd,
313 		(struct sockaddr *)&url->hostport.IPaddress,
314 		sockaddr_len);
315 	if (ret_connect == -1) {
316 #ifdef _WIN32
317 		UpnpPrintf(UPNP_CRITICAL,
318 			HTTP,
319 			__FILE__,
320 			__LINE__,
321 			"connect error: %d\n",
322 			WSAGetLastError());
323 #endif
324 		if (shutdown(connfd, SD_BOTH) == -1) {
325 			strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
326 			UpnpPrintf(UPNP_INFO,
327 				HTTP,
328 				__FILE__,
329 				__LINE__,
330 				"Error in shutdown: %s\n",
331 				errorBuffer);
332 		}
333 		UpnpCloseSocket(connfd);
334 		return (SOCKET)(UPNP_E_SOCKET_CONNECT);
335 	}
336 
337 	return connfd;
338 }
339 
340 /*!
341  * \brief Get the data on the socket and take actions based on the read data to
342  * modify the parser objects buffer.
343  *
344  * If an error is reported while parsing the data, the error code is passed in
345  * the http_errr_code parameter.
346  *
347  * Parameters:
348  *	IN SOCKINFO *info;			Socket information object
349  *	OUT http_parser_t* parser;		HTTP parser object
350  *	IN http_method_t request_method;	HTTP request method
351  *	IN OUT int* timeout_secs;		time out
352  *	OUT int* http_error_code;		HTTP error code returned
353  *
354  * \return
355  * 	 UPNP_E_SUCCESS
356  *	 UPNP_E_BAD_HTTPMSG
357  */
358 int http_RecvMessage(SOCKINFO *info,
359 	http_parser_t *parser,
360 	http_method_t request_method,
361 	int *timeout_secs,
362 	int *http_error_code)
363 {
364 	int ret = UPNP_E_SUCCESS;
365 	int line = 0;
366 	parse_status_t status;
367 	int num_read;
368 	int ok_on_close = 0;
369 	char *buf;
370 	size_t buf_len = 1024;
371 
372 	*http_error_code = HTTP_INTERNAL_SERVER_ERROR;
373 	buf = malloc(buf_len);
374 	if (!buf) {
375 		ret = UPNP_E_OUTOF_MEMORY;
376 		goto ExitFunction;
377 	}
378 	if (request_method == (http_method_t)HTTPMETHOD_UNKNOWN) {
379 		parser_request_init(parser);
380 	} else {
381 		parser_response_init(parser, request_method);
382 	}
383 
384 	while (1) {
385 		/* Double the bet */
386 		free(buf);
387 		buf_len = 2 * buf_len;
388 		buf = malloc(buf_len);
389 		if (!buf) {
390 			ret = UPNP_E_OUTOF_MEMORY;
391 			goto ExitFunction;
392 		}
393 		num_read = sock_read(info, buf, buf_len, timeout_secs);
394 		if (num_read > 0) {
395 			/* got data */
396 			status = parser_append(parser, buf, (size_t)num_read);
397 			switch (status) {
398 			case PARSE_SUCCESS:
399 				UpnpPrintf(UPNP_INFO,
400 					HTTP,
401 					__FILE__,
402 					__LINE__,
403 					"<<< (RECVD) "
404 					"<<<\n%s\n-----------------\n",
405 					parser->msg.msg.buf);
406 				print_http_headers(&parser->msg);
407 				if (g_maxContentLength > 0 &&
408 					parser->content_length >
409 						(unsigned int)
410 							g_maxContentLength) {
411 					*http_error_code =
412 						HTTP_REQ_ENTITY_TOO_LARGE;
413 					line = __LINE__;
414 					ret = UPNP_E_OUTOF_BOUNDS;
415 					goto ExitFunction;
416 				}
417 				line = __LINE__;
418 				ret = 0;
419 				goto ExitFunction;
420 			case PARSE_FAILURE:
421 			case PARSE_NO_MATCH:
422 				*http_error_code = parser->http_error_code;
423 				line = __LINE__;
424 				ret = UPNP_E_BAD_HTTPMSG;
425 				goto ExitFunction;
426 			case PARSE_INCOMPLETE_ENTITY:
427 				/* read until close */
428 				ok_on_close = 1;
429 				break;
430 			case PARSE_CONTINUE_1:
431 				/* Web post request. */
432 				line = __LINE__;
433 				ret = PARSE_SUCCESS;
434 				goto ExitFunction;
435 			default:
436 				break;
437 			}
438 		} else if (num_read == 0) {
439 			if (ok_on_close) {
440 				UpnpPrintf(UPNP_INFO,
441 					HTTP,
442 					__FILE__,
443 					__LINE__,
444 					"<<< (RECVD) "
445 					"<<<\n%s\n-----------------\n",
446 					parser->msg.msg.buf);
447 				print_http_headers(&parser->msg);
448 				line = __LINE__;
449 				ret = 0;
450 				goto ExitFunction;
451 			} else {
452 				/* partial msg */
453 				*http_error_code =
454 					HTTP_BAD_REQUEST; /* or response */
455 				line = __LINE__;
456 				ret = UPNP_E_BAD_HTTPMSG;
457 				goto ExitFunction;
458 			}
459 		} else {
460 			*http_error_code = parser->http_error_code;
461 			line = __LINE__;
462 			ret = num_read;
463 			goto ExitFunction;
464 		}
465 	}
466 
467 ExitFunction:
468 	free(buf);
469 	if (ret != UPNP_E_SUCCESS) {
470 		UpnpPrintf(UPNP_ALL,
471 			HTTP,
472 			__FILE__,
473 			line,
474 			"(http_RecvMessage): Error %d, http_error_code = %d.\n",
475 			ret,
476 			*http_error_code);
477 	}
478 
479 	return ret;
480 }
481 
482 int http_SendMessage(SOCKINFO *info, int *TimeOut, const char *fmt, ...)
483 {
484 #if EXCLUDE_WEB_SERVER == 0
485 	FILE *Fp;
486 	struct SendInstruction *Instr = NULL;
487 	char *filename = NULL;
488 	char *file_buf = NULL;
489 	char *ChunkBuf = NULL;
490 	/* 10 byte allocated for chunk header. */
491 	char Chunk_Header[CHUNK_HEADER_SIZE];
492 	size_t num_read;
493 	off_t amount_to_be_read = 0;
494 	size_t Data_Buf_Size = WEB_SERVER_BUF_SIZE;
495 #endif /* EXCLUDE_WEB_SERVER */
496 	va_list argp;
497 	char *buf = NULL;
498 	char c;
499 	int nw;
500 	int RetVal = 0;
501 	size_t buf_length;
502 	size_t num_written;
503 	int I_fmt_processed = 0;
504 
505 #if EXCLUDE_WEB_SERVER == 0
506 	memset(Chunk_Header, 0, sizeof(Chunk_Header));
507 #endif /* EXCLUDE_WEB_SERVER */
508 	va_start(argp, fmt);
509 	while ((c = *fmt++)) {
510 #if EXCLUDE_WEB_SERVER == 0
511 		if (c == 'I' && !I_fmt_processed) {
512 			I_fmt_processed = 1;
513 			Instr = va_arg(argp, struct SendInstruction *);
514 			if (Instr->ReadSendSize >= 0)
515 				amount_to_be_read = Instr->ReadSendSize;
516 			else
517 				amount_to_be_read = (off_t)Data_Buf_Size;
518 			if (amount_to_be_read < (off_t)WEB_SERVER_BUF_SIZE)
519 				Data_Buf_Size = (size_t)amount_to_be_read;
520 			ChunkBuf = malloc(
521 				(size_t)(Data_Buf_Size + CHUNK_HEADER_SIZE +
522 					 CHUNK_TAIL_SIZE));
523 			if (!ChunkBuf) {
524 				RetVal = UPNP_E_OUTOF_MEMORY;
525 				goto ExitFunction;
526 			}
527 			file_buf = ChunkBuf + CHUNK_HEADER_SIZE;
528 		} else if (c == 'f') {
529 			/* file name */
530 			filename = va_arg(argp, char *);
531 			if (Instr && Instr->IsVirtualFile)
532 				Fp = (virtualDirCallback.open)(filename,
533 					UPNP_READ,
534 					Instr->Cookie,
535 					Instr->RequestCookie);
536 			else
537 				Fp = fopen(filename, "rb");
538 			if (Fp == NULL) {
539 				RetVal = UPNP_E_FILE_READ_ERROR;
540 				goto ExitFunction;
541 			}
542 			if (Instr && Instr->IsRangeActive &&
543 				Instr->IsVirtualFile) {
544 				if (virtualDirCallback.seek(Fp,
545 					    Instr->RangeOffset,
546 					    SEEK_CUR,
547 					    Instr->Cookie,
548 					    Instr->RequestCookie) != 0) {
549 					RetVal = UPNP_E_FILE_READ_ERROR;
550 					goto Cleanup_File;
551 				}
552 			} else if (Instr && Instr->IsRangeActive) {
553 				if (fseeko(Fp, Instr->RangeOffset, SEEK_CUR) !=
554 					0) {
555 					RetVal = UPNP_E_FILE_READ_ERROR;
556 					goto Cleanup_File;
557 				}
558 			}
559 			while (amount_to_be_read) {
560 				if (Instr) {
561 					int nr;
562 					size_t n =
563 						amount_to_be_read >=
564 								(off_t)Data_Buf_Size
565 							? Data_Buf_Size
566 							: (size_t)amount_to_be_read;
567 					if (Instr->IsVirtualFile) {
568 						nr = virtualDirCallback.read(Fp,
569 							file_buf,
570 							n,
571 							Instr->Cookie,
572 							Instr->RequestCookie);
573 						num_read = (size_t)nr;
574 					} else {
575 						num_read = fread(file_buf,
576 							(size_t)1,
577 							n,
578 							Fp);
579 					}
580 					amount_to_be_read -= (off_t)num_read;
581 					if (Instr->ReadSendSize < 0) {
582 						/* read until close */
583 						amount_to_be_read =
584 							(off_t)Data_Buf_Size;
585 					}
586 				} else {
587 					num_read = fread(file_buf,
588 						(size_t)1,
589 						Data_Buf_Size,
590 						Fp);
591 				}
592 				if (num_read == (size_t)0) {
593 					/* EOF so no more to send. */
594 					if (Instr && Instr->IsChunkActive) {
595 						const char *str = "0\r\n\r\n";
596 						nw = sock_write(info,
597 							str,
598 							strlen(str),
599 							TimeOut);
600 					} else {
601 						RetVal = UPNP_E_FILE_READ_ERROR;
602 					}
603 					goto Cleanup_File;
604 				}
605 				/* Create chunk for the current buffer. */
606 				if (Instr && Instr->IsChunkActive) {
607 					int rc;
608 					/* Copy CRLF at the end of the chunk */
609 					memcpy(file_buf + num_read,
610 						"\r\n",
611 						(size_t)2);
612 					/* Hex length for the chunk size. */
613 					memset(Chunk_Header,
614 						0,
615 						sizeof(Chunk_Header));
616 					rc = snprintf(Chunk_Header,
617 						sizeof(Chunk_Header),
618 						"%" PRIzx "\r\n",
619 						num_read);
620 					if (rc < 0 ||
621 						(unsigned int)rc >=
622 							sizeof(Chunk_Header)) {
623 						RetVal = UPNP_E_INTERNAL_ERROR;
624 						goto Cleanup_File;
625 					}
626 					/* Copy the chunk size header  */
627 					memcpy(file_buf - strlen(Chunk_Header),
628 						Chunk_Header,
629 						strlen(Chunk_Header));
630 					/* on the top of the buffer. */
631 					/*file_buf[num_read+strlen(Chunk_Header)]
632 					 * = NULL; */
633 					/*upnpprintf("Sending
634 					 * %s\n",file_buf-strlen(Chunk_Header));*/
635 					nw = sock_write(info,
636 						file_buf - strlen(Chunk_Header),
637 						num_read +
638 							strlen(Chunk_Header) +
639 							(size_t)2,
640 						TimeOut);
641 					num_written = (size_t)nw;
642 					if (nw <= 0 ||
643 						num_written !=
644 							num_read +
645 								strlen(Chunk_Header) +
646 								(size_t)2)
647 						/* Send error nothing we can do.
648 						 */
649 						goto Cleanup_File;
650 				} else {
651 					/* write data */
652 					nw = sock_write(info,
653 						file_buf,
654 						num_read,
655 						TimeOut);
656 					UpnpPrintf(UPNP_INFO,
657 						HTTP,
658 						__FILE__,
659 						__LINE__,
660 						">>> (SENT) "
661 						">>>\n%.*s\n------------\n",
662 						nw,
663 						file_buf);
664 					/* Send error nothing we can do */
665 					num_written = (size_t)nw;
666 					if (nw <= 0 ||
667 						num_written != num_read) {
668 						goto Cleanup_File;
669 					}
670 				}
671 			} /* while */
672 		Cleanup_File:
673 			if (Instr && Instr->IsVirtualFile) {
674 				virtualDirCallback.close(Fp,
675 					Instr->Cookie,
676 					Instr->RequestCookie);
677 			} else {
678 				fclose(Fp);
679 			}
680 			goto ExitFunction;
681 		} else
682 #endif /* EXCLUDE_WEB_SERVER */
683 			if (c == 'b') {
684 			/* memory buffer */
685 			buf = va_arg(argp, char *);
686 			buf_length = va_arg(argp, size_t);
687 			if (buf_length > (size_t)0) {
688 				nw = sock_write(info, buf, buf_length, TimeOut);
689 				num_written = (size_t)nw;
690 				UpnpPrintf(UPNP_INFO,
691 					HTTP,
692 					__FILE__,
693 					__LINE__,
694 					">>> (SENT) >>>\n"
695 					"%.*s\nbuf_length=%" PRIzd
696 					", num_written=%" PRIzd "\n"
697 					"------------\n",
698 					(int)buf_length,
699 					buf,
700 					buf_length,
701 					num_written);
702 				if (num_written != buf_length) {
703 					RetVal = 0;
704 					goto ExitFunction;
705 				}
706 			}
707 		}
708 	}
709 
710 ExitFunction:
711 	va_end(argp);
712 #if EXCLUDE_WEB_SERVER == 0
713 	free(ChunkBuf);
714 #endif /* EXCLUDE_WEB_SERVER */
715 	return RetVal;
716 }
717 
718 /************************************************************************
719  * Function: http_RequestAndResponse
720  *
721  * Parameters:
722  *	IN uri_type* destination;	Destination URI object which contains
723  *					remote IP address among other elements
724  *	IN const char* request;		Request to be sent
725  *	IN size_t request_length;	Length of the request
726  *	IN http_method_t req_method;	HTTP Request method
727  *	IN int timeout_secs;		time out value
728  *	OUT http_parser_t* response;	Parser object to receive the repsonse
729  *
730  * Description:
731  *	Initiates socket, connects to the destination, sends a
732  *	request and waits for the response from the remote end
733  *
734  * Returns:
735  *	UPNP_E_SOCKET_ERROR
736  * 	UPNP_E_SOCKET_CONNECT
737  *	Error Codes returned by http_SendMessage
738  *	Error Codes returned by http_RecvMessage
739  ************************************************************************/
740 int http_RequestAndResponse(uri_type *destination,
741 	const char *request,
742 	size_t request_length,
743 	http_method_t req_method,
744 	int timeout_secs,
745 	http_parser_t *response)
746 {
747 	SOCKET tcp_connection;
748 	int ret_code;
749 	size_t sockaddr_len;
750 	int http_error_code;
751 	SOCKINFO info;
752 
753 	tcp_connection = socket(
754 		(int)destination->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
755 	if (tcp_connection == INVALID_SOCKET) {
756 		parser_response_init(response, req_method);
757 		return UPNP_E_SOCKET_ERROR;
758 	}
759 	if (sock_init(&info, tcp_connection) != UPNP_E_SUCCESS) {
760 		parser_response_init(response, req_method);
761 		ret_code = UPNP_E_SOCKET_ERROR;
762 		goto end_function;
763 	}
764 	/* connect */
765 	sockaddr_len = destination->hostport.IPaddress.ss_family == AF_INET6
766 			       ? sizeof(struct sockaddr_in6)
767 			       : sizeof(struct sockaddr_in);
768 	ret_code = private_connect(info.socket,
769 		(struct sockaddr *)&(destination->hostport.IPaddress),
770 		(socklen_t)sockaddr_len);
771 	if (ret_code == -1) {
772 		parser_response_init(response, req_method);
773 		ret_code = UPNP_E_SOCKET_CONNECT;
774 		goto end_function;
775 	}
776 	/* send request */
777 	ret_code = http_SendMessage(
778 		&info, &timeout_secs, "b", request, request_length);
779 	if (ret_code != 0) {
780 		parser_response_init(response, req_method);
781 		goto end_function;
782 	}
783 	/* recv response */
784 	ret_code = http_RecvMessage(
785 		&info, response, req_method, &timeout_secs, &http_error_code);
786 
787 end_function:
788 	/* should shutdown completely */
789 	sock_destroy(&info, SD_BOTH);
790 
791 	return ret_code;
792 }
793 
794 /************************************************************************
795  * Function: http_Download
796  *
797  * Parameters:
798  *	IN const char* url_str;	String as a URL
799  *	IN int timeout_secs;	time out value
800  *	OUT char** document;	buffer to store the document extracted
801  *				from the donloaded message.
802  *	OUT int* doc_length;	length of the extracted document
803  *	OUT char* content_type;	Type of content
804  *
805  * Description:
806  *	Download the document message and extract the document
807  *	from the message.
808  *
809  * Return: int
810  *	UPNP_E_SUCCESS
811  *	UPNP_E_INVALID_URL
812  ************************************************************************/
813 int http_Download(const char *url_str,
814 	int timeout_secs,
815 	char **document,
816 	size_t *doc_length,
817 	char *content_type)
818 {
819 	int ret_code;
820 	uri_type url;
821 	char *msg_start;
822 	char *entity_start;
823 	const char *hoststr;
824 	http_parser_t response;
825 	size_t msg_length;
826 	size_t hostlen;
827 	memptr ctype;
828 	size_t copy_len;
829 	membuffer request;
830 	size_t url_str_len;
831 
832 	url_str_len = strlen(url_str);
833 	/*ret_code = parse_uri( (char*)url_str, url_str_len, &url ); */
834 	UpnpPrintf(UPNP_INFO,
835 		HTTP,
836 		__FILE__,
837 		__LINE__,
838 		"DOWNLOAD URL : %s\n",
839 		url_str);
840 	ret_code = http_FixStrUrl((char *)url_str, url_str_len, &url);
841 	if (ret_code != UPNP_E_SUCCESS) {
842 		return ret_code;
843 	}
844 	/* make msg */
845 	membuffer_init(&request);
846 	ret_code = get_hoststr(url_str, &hoststr, &hostlen);
847 	if (ret_code != UPNP_E_SUCCESS) {
848 		return ret_code;
849 	}
850 	UpnpPrintf(UPNP_INFO,
851 		HTTP,
852 		__FILE__,
853 		__LINE__,
854 		"HOSTNAME : %s Length : %" PRIzu "\n",
855 		hoststr,
856 		hostlen);
857 	ret_code = http_MakeMessage(&request,
858 		1,
859 		1,
860 		"Q"
861 		"s"
862 		"bcDCUc",
863 		HTTPMETHOD_GET,
864 		url.pathquery.buff,
865 		url.pathquery.size,
866 		"HOST: ",
867 		hoststr,
868 		hostlen);
869 	if (ret_code != 0) {
870 		UpnpPrintf(UPNP_INFO,
871 			HTTP,
872 			__FILE__,
873 			__LINE__,
874 			"HTTP Makemessage failed\n");
875 		membuffer_destroy(&request);
876 		return ret_code;
877 	}
878 	UpnpPrintf(UPNP_INFO,
879 		HTTP,
880 		__FILE__,
881 		__LINE__,
882 		"HTTP Buffer:\n%s\n"
883 		"----------END--------\n",
884 		request.buf);
885 	/* get doc msg */
886 	ret_code = http_RequestAndResponse(&url,
887 		request.buf,
888 		request.length,
889 		HTTPMETHOD_GET,
890 		timeout_secs,
891 		&response);
892 
893 	if (ret_code != 0) {
894 		httpmsg_destroy(&response.msg);
895 		membuffer_destroy(&request);
896 		return ret_code;
897 	}
898 	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__, "Response\n");
899 	print_http_headers(&response.msg);
900 	/* optional content-type */
901 	if (content_type) {
902 		if (httpmsg_find_hdr(&response.msg, HDR_CONTENT_TYPE, &ctype) ==
903 			NULL) {
904 			*content_type = '\0'; /* no content-type */
905 		} else {
906 			/* safety */
907 			copy_len = ctype.length < LINE_SIZE - (size_t)1
908 					   ? ctype.length
909 					   : LINE_SIZE - (size_t)1;
910 
911 			memcpy(content_type, ctype.buf, copy_len);
912 			content_type[copy_len] = '\0';
913 		}
914 	}
915 	/* extract doc from msg */
916 	if ((*doc_length = response.msg.entity.length) == (size_t)0) {
917 		/* 0-length msg */
918 		*document = NULL;
919 	} else if (response.msg.status_code == HTTP_OK) {
920 		/*LEAK_FIX_MK */
921 		/* copy entity */
922 		entity_start = response.msg.entity.buf; /* what we want */
923 		msg_length = response.msg.msg.length; /* save for posterity   */
924 		msg_start = membuffer_detach(&response.msg.msg); /* whole msg */
925 		/* move entity to the start; copy null-terminator too */
926 		memmove(msg_start, entity_start, *doc_length + (size_t)1);
927 		/* save mem for body only */
928 		*document = realloc(
929 			msg_start, *doc_length + (size_t)1); /*LEAK_FIX_MK */
930 		/* *document = Realloc( msg_start,msg_length, *doc_length + 1 );
931 		 * LEAK_FIX_MK */
932 		/* shrink can't fail */
933 		assert(msg_length > *doc_length);
934 		assert(*document != NULL);
935 		if (msg_length <= *doc_length || *document == NULL)
936 			UpnpPrintf(UPNP_INFO,
937 				HTTP,
938 				__FILE__,
939 				__LINE__,
940 				"msg_length(%" PRIzu ") <= *doc_length(%" PRIzu
941 				") or document is NULL",
942 				msg_length,
943 				*doc_length);
944 	}
945 	if (response.msg.status_code == HTTP_OK) {
946 		ret_code = 0; /* success */
947 	} else {
948 		/* server sent error msg (not requested doc) */
949 		ret_code = response.msg.status_code;
950 	}
951 	httpmsg_destroy(&response.msg);
952 	membuffer_destroy(&request);
953 
954 	return ret_code;
955 }
956 
957 /************************************************************************
958  * Function: MakeGenericMessage
959  *
960  * Parameters:
961  *  http_method_t method;   The type of HTTP method.
962  *	const char *url_str;		String as a URL
963  *	membuffer *request;		Buffer containing the request
964  *	uri_type *url; 			URI object containing the scheme,
965  *					path query token, etc.
966  *	int contentLength;		length of content
967  *	const char *contentType;	Type of content
968  *
969  * Description:
970  *	Makes the message for the HTTP POST message
971  *
972  * Returns:
973  *	UPNP_E_INVALID_URL
974  * 	UPNP_E_INVALID_PARAM
975  *	UPNP_E_SUCCESS
976  ************************************************************************/
977 int MakeGenericMessage(http_method_t method,
978 	const char *url_str,
979 	membuffer *request,
980 	uri_type *url,
981 	int contentLength,
982 	const char *contentType,
983 	const UpnpString *headers)
984 {
985 	int ret_code = 0;
986 	size_t hostlen = 0;
987 	const char *hoststr;
988 
989 	UpnpPrintf(UPNP_INFO,
990 		HTTP,
991 		__FILE__,
992 		__LINE__,
993 		"URL: %s method: %d\n",
994 		url_str,
995 		method);
996 	ret_code = http_FixStrUrl(url_str, strlen(url_str), url);
997 	if (ret_code != UPNP_E_SUCCESS)
998 		return ret_code;
999 	/* make msg */
1000 	membuffer_init(request);
1001 	ret_code = http_MakeMessage(request,
1002 		1,
1003 		1,
1004 		"Q",
1005 		method,
1006 		url->pathquery.buff,
1007 		url->pathquery.size);
1008 	/* add request headers if specified, otherwise use default headers */
1009 	if (ret_code == 0) {
1010 		if (headers) {
1011 			ret_code = http_MakeMessage(request,
1012 				1,
1013 				1,
1014 				"s",
1015 				UpnpString_get_String(headers));
1016 		} else {
1017 			ret_code = get_hoststr(url_str, &hoststr, &hostlen);
1018 			if (ret_code != UPNP_E_SUCCESS)
1019 				return ret_code;
1020 			UpnpPrintf(UPNP_INFO,
1021 				HTTP,
1022 				__FILE__,
1023 				__LINE__,
1024 				"HOSTNAME : %s Length : %" PRIzu "\n",
1025 				hoststr,
1026 				hostlen);
1027 			ret_code = http_MakeMessage(request,
1028 				1,
1029 				1,
1030 				"s"
1031 				"bcDCU",
1032 				"HOST: ",
1033 				hoststr,
1034 				hostlen);
1035 		}
1036 	}
1037 
1038 	/* add the content-type header */
1039 	if (ret_code == 0 && contentType) {
1040 		ret_code = http_MakeMessage(request, 1, 1, "T", contentType);
1041 	}
1042 	/* add content-length header. */
1043 	if (ret_code == 0) {
1044 		if (contentLength >= 0)
1045 			ret_code = http_MakeMessage(
1046 				request, 1, 1, "Nc", (off_t)contentLength);
1047 		else if (contentLength == UPNP_USING_CHUNKED)
1048 			ret_code = http_MakeMessage(request, 1, 1, "Kc");
1049 		else if (contentLength == UPNP_UNTIL_CLOSE)
1050 			ret_code = http_MakeMessage(request, 1, 1, "c");
1051 		else
1052 			ret_code = UPNP_E_INVALID_PARAM;
1053 	}
1054 	if (ret_code != 0) {
1055 		UpnpPrintf(UPNP_INFO,
1056 			HTTP,
1057 			__FILE__,
1058 			__LINE__,
1059 			"HTTP Makemessage failed\n");
1060 		membuffer_destroy(request);
1061 		return ret_code;
1062 	}
1063 	UpnpPrintf(UPNP_INFO,
1064 		HTTP,
1065 		__FILE__,
1066 		__LINE__,
1067 		"HTTP Buffer:\n%s\n"
1068 		"----------END--------\n",
1069 		request->buf);
1070 
1071 	return UPNP_E_SUCCESS;
1072 }
1073 
1074 typedef struct HTTPCONNECTIONHANDLE
1075 {
1076 	SOCKINFO sock_info;
1077 	int contentLength;
1078 	http_parser_t response;
1079 	int requestStarted;
1080 	int cancel;
1081 } http_connection_handle_t;
1082 
1083 /*!
1084  * \brief Parses already exiting data. If not complete reads more
1085  * data on the connected socket. The read data is then parsed. The
1086  * same methid is carried out for headers.
1087  *
1088  * \return integer:
1089  *	\li \c PARSE_OK - On Success
1090  *	\li \c PARSE_FAILURE - Failure to parse data correctly
1091  *	\li \c UPNP_E_BAD_HTTPMSG - Socker read() returns an error
1092  */
1093 static int ReadResponseLineAndHeaders(
1094 	/*! Socket information object. */
1095 	SOCKINFO *info,
1096 	/*! HTTP Parser object. */
1097 	http_parser_t *parser,
1098 	/*! Time out value. */
1099 	int *timeout_secs,
1100 	/*! HTTP errror code returned. */
1101 	int *http_error_code)
1102 {
1103 	parse_status_t status;
1104 	int num_read;
1105 	char buf[2 * 1024];
1106 	int done = 0;
1107 	int ret_code = 0;
1108 
1109 	/*read response line */
1110 	status = parser_parse_responseline(parser);
1111 	switch (status) {
1112 	case PARSE_OK:
1113 		done = 1;
1114 		break;
1115 	case PARSE_INCOMPLETE:
1116 		done = 0;
1117 		break;
1118 	default:
1119 		/*error */
1120 		return status;
1121 	}
1122 	while (!done) {
1123 		num_read = sock_read(info, buf, sizeof(buf), timeout_secs);
1124 		if (num_read > 0) {
1125 			/* append data to buffer */
1126 			ret_code = membuffer_append(
1127 				&parser->msg.msg, buf, (size_t)num_read);
1128 			if (ret_code != 0) {
1129 				/* set failure status */
1130 				parser->http_error_code =
1131 					HTTP_INTERNAL_SERVER_ERROR;
1132 				return PARSE_FAILURE;
1133 			}
1134 			status = parser_parse_responseline(parser);
1135 			switch (status) {
1136 			case PARSE_OK:
1137 				done = 1;
1138 				break;
1139 			case PARSE_INCOMPLETE:
1140 				done = 0;
1141 				break;
1142 			default:
1143 				/*error */
1144 				return status;
1145 			}
1146 		} else if (num_read == 0) {
1147 			/* partial msg */
1148 			*http_error_code = HTTP_BAD_REQUEST; /* or response */
1149 			return UPNP_E_BAD_HTTPMSG;
1150 		} else {
1151 			*http_error_code = parser->http_error_code;
1152 			return num_read;
1153 		}
1154 	}
1155 	status = parser_parse_headers(parser);
1156 	if ((status == (parse_status_t)PARSE_OK) &&
1157 		(parser->position == (parser_pos_t)POS_ENTITY))
1158 		done = 1;
1159 	else if (status == (parse_status_t)PARSE_INCOMPLETE)
1160 		done = 0;
1161 	else
1162 		/*error */
1163 		return status;
1164 	/*read headers */
1165 	while (!done) {
1166 		num_read = sock_read(info, buf, sizeof(buf), timeout_secs);
1167 		if (num_read > 0) {
1168 			/* append data to buffer */
1169 			ret_code = membuffer_append(
1170 				&parser->msg.msg, buf, (size_t)num_read);
1171 			if (ret_code != 0) {
1172 				/* set failure status */
1173 				parser->http_error_code =
1174 					HTTP_INTERNAL_SERVER_ERROR;
1175 				return PARSE_FAILURE;
1176 			}
1177 			status = parser_parse_headers(parser);
1178 			if (status == (parse_status_t)PARSE_OK &&
1179 				parser->position == (parser_pos_t)POS_ENTITY)
1180 				done = 1;
1181 			else if (status == (parse_status_t)PARSE_INCOMPLETE)
1182 				done = 0;
1183 			else
1184 				/*error */
1185 				return status;
1186 		} else if (num_read == 0) {
1187 			/* partial msg */
1188 			*http_error_code = HTTP_BAD_REQUEST; /* or response */
1189 			return UPNP_E_BAD_HTTPMSG;
1190 		} else {
1191 			*http_error_code = parser->http_error_code;
1192 			return num_read;
1193 		}
1194 	}
1195 
1196 	return PARSE_OK;
1197 }
1198 
1199 /************************************************************************
1200  * Function: http_HttpGetProgress
1201  *
1202  * Parameters:
1203  *	IN void *Handle;	Handle to the HTTP get object
1204  *	OUT size_t *length;	Buffer to get the read and parsed data
1205  *	OUT size_t *total;	Size of tge buffer passed
1206  *
1207  * Description:
1208  *	Extracts information from the Handle to the HTTP get object.
1209  *
1210  * Return: int
1211  *	UPNP_E_SUCCESS		- On Sucess
1212  *	UPNP_E_INVALID_PARAM	- Invalid Parameter
1213  ************************************************************************/
1214 int http_HttpGetProgress(void *Handle, size_t *length, size_t *total)
1215 {
1216 	http_connection_handle_t *handle = Handle;
1217 
1218 	if (!handle || !length || !total) {
1219 		return UPNP_E_INVALID_PARAM;
1220 	}
1221 	*length = handle->response.msg.entity.length;
1222 	*total = handle->response.content_length;
1223 
1224 	return UPNP_E_SUCCESS;
1225 }
1226 
1227 /************************************************************************
1228  * Function: http_CancelHttpGet
1229  *
1230  * Parameters:
1231  *	IN void *Handle;	Handle to HTTP get object
1232  *
1233  * Description:
1234  *	Set the cancel flag of the HttpGet handle
1235  *
1236  * Return: int
1237  *	UPNP_E_SUCCESS		- On Success
1238  *	UPNP_E_INVALID_PARAM	- Invalid Parameter
1239  ************************************************************************/
1240 int http_CancelHttpGet(void *Handle)
1241 {
1242 	http_connection_handle_t *handle = Handle;
1243 
1244 	if (!handle)
1245 		return UPNP_E_INVALID_PARAM;
1246 	handle->cancel = 1;
1247 
1248 	return UPNP_E_SUCCESS;
1249 }
1250 
1251 int http_OpenHttpConnection(const char *url_str, void **Handle, int timeout)
1252 {
1253 	int ret_code;
1254 	size_t sockaddr_len;
1255 	SOCKET tcp_connection;
1256 	http_connection_handle_t *handle = NULL;
1257 	uri_type url;
1258 	(void)timeout; /* Unused parameter */
1259 	if (!url_str || !Handle)
1260 		return UPNP_E_INVALID_PARAM;
1261 	*Handle = handle;
1262 	/* parse url_str */
1263 	ret_code = http_FixStrUrl(url_str, strlen(url_str), &url);
1264 	if (ret_code != UPNP_E_SUCCESS)
1265 		return ret_code;
1266 	/* create the handle */
1267 	handle = malloc(sizeof(http_connection_handle_t));
1268 	if (!handle) {
1269 		return UPNP_E_OUTOF_MEMORY;
1270 	}
1271 	handle->requestStarted = 0;
1272 	memset(&handle->response, 0, sizeof(handle->response));
1273 	/* connect to the server */
1274 	tcp_connection =
1275 		socket(url.hostport.IPaddress.ss_family, SOCK_STREAM, 0);
1276 	if (tcp_connection == INVALID_SOCKET) {
1277 		ret_code = UPNP_E_SOCKET_ERROR;
1278 		goto errorHandler;
1279 	}
1280 	if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
1281 		sock_destroy(&handle->sock_info, SD_BOTH);
1282 		ret_code = UPNP_E_SOCKET_ERROR;
1283 		goto errorHandler;
1284 	}
1285 	sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6
1286 			       ? sizeof(struct sockaddr_in6)
1287 			       : sizeof(struct sockaddr_in);
1288 	ret_code = private_connect(handle->sock_info.socket,
1289 		(struct sockaddr *)&(url.hostport.IPaddress),
1290 		(socklen_t)sockaddr_len);
1291 	if (ret_code == -1) {
1292 		sock_destroy(&handle->sock_info, SD_BOTH);
1293 		ret_code = UPNP_E_SOCKET_CONNECT;
1294 		goto errorHandler;
1295 	}
1296 #ifdef UPNP_ENABLE_OPEN_SSL
1297 	/* For HTTPS connections start the TLS/SSL handshake. */
1298 	if (token_string_casecmp(&url.scheme, "https") == 0) {
1299 		ret_code = sock_ssl_connect(&handle->sock_info);
1300 		if (ret_code != UPNP_E_SUCCESS) {
1301 			sock_destroy(&handle->sock_info, SD_BOTH);
1302 			goto errorHandler;
1303 		}
1304 	}
1305 #endif
1306 errorHandler:
1307 	*Handle = handle;
1308 	return ret_code;
1309 }
1310 
1311 int http_MakeHttpRequest(Upnp_HttpMethod method,
1312 	const char *url_str,
1313 	void *Handle,
1314 	UpnpString *headers,
1315 	const char *contentType,
1316 	int contentLength,
1317 	int timeout)
1318 {
1319 	int ret_code;
1320 	membuffer request;
1321 	http_connection_handle_t *handle = Handle;
1322 	uri_type url;
1323 	if (!url_str || !Handle)
1324 		return UPNP_E_INVALID_PARAM;
1325 	if (handle->requestStarted) {
1326 		/* TODO: Log an error that a previous request is already in
1327 		 * progress. */
1328 	}
1329 	handle->requestStarted = 1;
1330 	handle->cancel = 0;
1331 	ret_code = MakeGenericMessage((http_method_t)method,
1332 		url_str,
1333 		&request,
1334 		&url,
1335 		contentLength,
1336 		contentType,
1337 		headers);
1338 	if (ret_code != UPNP_E_SUCCESS)
1339 		return ret_code;
1340 	/* send request */
1341 	ret_code = http_SendMessage(
1342 		&handle->sock_info, &timeout, "b", request.buf, request.length);
1343 	membuffer_destroy(&request);
1344 	httpmsg_destroy(&handle->response.msg);
1345 	parser_response_init(&handle->response, (http_method_t)method);
1346 	return ret_code;
1347 }
1348 
1349 int http_WriteHttpRequest(void *Handle, char *buf, size_t *size, int timeout)
1350 {
1351 	http_connection_handle_t *handle = (http_connection_handle_t *)Handle;
1352 	char *tempbuf = NULL;
1353 	size_t tempbufSize = 0;
1354 	int freeTempbuf = 0;
1355 	int numWritten = 0;
1356 
1357 	if (!handle || !size || !buf) {
1358 		if (size)
1359 			*size = 0;
1360 		return UPNP_E_INVALID_PARAM;
1361 	}
1362 	if (handle->contentLength == UPNP_USING_CHUNKED) {
1363 		if (*size) {
1364 			size_t tempSize = 0;
1365 			tempbuf = malloc(
1366 				*size + CHUNK_HEADER_SIZE + CHUNK_TAIL_SIZE);
1367 			if (!tempbuf)
1368 				return UPNP_E_OUTOF_MEMORY;
1369 			/* begin chunk */
1370 			sprintf(tempbuf, "%zx\r\n", *size);
1371 			tempSize = strlen(tempbuf);
1372 			memcpy(tempbuf + tempSize, buf, *size);
1373 			memcpy(tempbuf + tempSize + *size, "\r\n", 2);
1374 			/* end of chunk */
1375 			tempbufSize = tempSize + *size + 2;
1376 			freeTempbuf = 1;
1377 		}
1378 	} else {
1379 		tempbuf = buf;
1380 		tempbufSize = *size;
1381 	}
1382 	numWritten =
1383 		sock_write(&handle->sock_info, tempbuf, tempbufSize, &timeout);
1384 	if (freeTempbuf)
1385 		free(tempbuf);
1386 	if (numWritten < 0) {
1387 		*size = 0;
1388 		return numWritten;
1389 	} else {
1390 		*size = (size_t)numWritten;
1391 		return UPNP_E_SUCCESS;
1392 	}
1393 }
1394 
1395 int http_EndHttpRequest(void *Handle, int timeout)
1396 {
1397 	int retc = 0;
1398 	const char *zcrlf = "0\r\n\r\n";
1399 	http_connection_handle_t *handle = Handle;
1400 	if (!handle)
1401 		return UPNP_E_INVALID_PARAM;
1402 	if (!handle->requestStarted) {
1403 		return UPNP_E_SUCCESS;
1404 	}
1405 	handle->requestStarted = 0;
1406 	if (handle->contentLength == UPNP_USING_CHUNKED)
1407 		/*send last chunk */
1408 		retc = sock_write(
1409 			&handle->sock_info, zcrlf, strlen(zcrlf), &timeout);
1410 
1411 	return retc >= 0 ? UPNP_E_SUCCESS : UPNP_E_SOCKET_WRITE;
1412 }
1413 
1414 int http_GetHttpResponse(void *Handle,
1415 	UpnpString *headers,
1416 	char **contentType,
1417 	int *contentLength,
1418 	int *httpStatus,
1419 	int timeout)
1420 {
1421 	int ret_code;
1422 	int http_error_code;
1423 	memptr ctype;
1424 	http_connection_handle_t *handle = Handle;
1425 	parse_status_t status;
1426 
1427 	status = ReadResponseLineAndHeaders(&handle->sock_info,
1428 		&handle->response,
1429 		&timeout,
1430 		&http_error_code);
1431 	if (status != (parse_status_t)PARSE_OK) {
1432 		ret_code = UPNP_E_BAD_RESPONSE;
1433 		goto errorHandler;
1434 	}
1435 	status = parser_get_entity_read_method(&handle->response);
1436 	switch (status) {
1437 	case PARSE_CONTINUE_1:
1438 	case PARSE_SUCCESS:
1439 		break;
1440 	default:
1441 		ret_code = UPNP_E_BAD_RESPONSE;
1442 		goto errorHandler;
1443 	}
1444 	ret_code = UPNP_E_SUCCESS;
1445 	if (httpStatus) {
1446 		*httpStatus = handle->response.msg.status_code;
1447 	}
1448 	if (contentType) {
1449 		if (!httpmsg_find_hdr(
1450 			    &handle->response.msg, HDR_CONTENT_TYPE, &ctype))
1451 			/* no content-type */
1452 			*contentType = NULL;
1453 		else
1454 			*contentType = ctype.buf;
1455 	}
1456 	if (contentLength) {
1457 		if (handle->response.position == (parser_pos_t)POS_COMPLETE)
1458 			*contentLength = 0;
1459 		else if (handle->response.ent_position == ENTREAD_USING_CHUNKED)
1460 			*contentLength = UPNP_USING_CHUNKED;
1461 		else if (handle->response.ent_position == ENTREAD_USING_CLEN)
1462 			*contentLength = (int)handle->response.content_length;
1463 		else if (handle->response.ent_position == ENTREAD_UNTIL_CLOSE)
1464 			*contentLength = UPNP_UNTIL_CLOSE;
1465 	}
1466 
1467 	if (headers) {
1468 		copy_msg_headers(&handle->response.msg.headers, headers);
1469 	}
1470 
1471 errorHandler:
1472 	if (ret_code != UPNP_E_SUCCESS)
1473 		httpmsg_destroy(&handle->response.msg);
1474 	return ret_code;
1475 }
1476 
1477 int http_ReadHttpResponse(void *Handle, char *buf, size_t *size, int timeout)
1478 {
1479 	http_connection_handle_t *handle = Handle;
1480 	parse_status_t status;
1481 	int num_read;
1482 	int ok_on_close = 0;
1483 	char tempbuf[2 * 1024];
1484 	int ret_code = 0;
1485 
1486 	if (!handle || !size || (*size > 0 && !buf)) {
1487 		if (size)
1488 			*size = 0;
1489 		return UPNP_E_INVALID_PARAM;
1490 	}
1491 	/* first parse what has already been gotten */
1492 	if (handle->response.position != POS_COMPLETE)
1493 		status = parser_parse_entity(&handle->response);
1494 	else
1495 		status = PARSE_SUCCESS;
1496 	if (status == PARSE_INCOMPLETE_ENTITY)
1497 		/* read until close */
1498 		ok_on_close = 1;
1499 	else if ((status != PARSE_SUCCESS) && (status != PARSE_CONTINUE_1) &&
1500 		 (status != PARSE_INCOMPLETE)) {
1501 		/*error */
1502 		*size = 0;
1503 		return UPNP_E_BAD_RESPONSE;
1504 	}
1505 	/* read more if necessary entity */
1506 	while (handle->response.msg.amount_discarded + *size >
1507 			handle->response.msg.entity.length &&
1508 		!handle->cancel && handle->response.position != POS_COMPLETE) {
1509 		num_read = sock_read(
1510 			&handle->sock_info, tempbuf, sizeof(tempbuf), &timeout);
1511 		if (num_read > 0) {
1512 			/* append data to buffer */
1513 			ret_code = membuffer_append(&handle->response.msg.msg,
1514 				tempbuf,
1515 				(size_t)num_read);
1516 			if (ret_code != 0) {
1517 				/* set failure status */
1518 				handle->response.http_error_code =
1519 					HTTP_INTERNAL_SERVER_ERROR;
1520 				*size = 0;
1521 				return PARSE_FAILURE;
1522 			}
1523 			status = parser_parse_entity(&handle->response);
1524 			if (status == PARSE_INCOMPLETE_ENTITY) {
1525 				/* read until close */
1526 				ok_on_close = 1;
1527 			} else if ((status != PARSE_SUCCESS) &&
1528 				   (status != PARSE_CONTINUE_1) &&
1529 				   (status != PARSE_INCOMPLETE)) {
1530 				/*error */
1531 				*size = 0;
1532 				return UPNP_E_BAD_RESPONSE;
1533 			}
1534 		} else if (num_read == 0) {
1535 			if (ok_on_close) {
1536 				UpnpPrintf(UPNP_INFO,
1537 					HTTP,
1538 					__FILE__,
1539 					__LINE__,
1540 					"<<< (RECVD) "
1541 					"<<<\n%s\n-----------------\n",
1542 					handle->response.msg.msg.buf);
1543 				handle->response.position = POS_COMPLETE;
1544 			} else {
1545 				/* partial msg */
1546 				*size = 0;
1547 				handle->response.http_error_code =
1548 					HTTP_BAD_REQUEST; /* or response */
1549 				return UPNP_E_BAD_HTTPMSG;
1550 			}
1551 		} else {
1552 			*size = 0;
1553 			return num_read;
1554 		}
1555 	}
1556 	if (handle->cancel) {
1557 		return UPNP_E_CANCELED;
1558 	}
1559 	/* truncate size to fall within available data */
1560 	if (handle->response.msg.amount_discarded + *size >
1561 		handle->response.msg.entity.length)
1562 		*size = handle->response.msg.entity.length -
1563 			handle->response.msg.amount_discarded;
1564 	/* copy data to user buffer. delete copied data */
1565 	if (*size > 0) {
1566 		memcpy(buf,
1567 			&handle->response.msg.msg
1568 				 .buf[handle->response.entity_start_position],
1569 			*size);
1570 		membuffer_delete(&handle->response.msg.msg,
1571 			handle->response.entity_start_position,
1572 			*size);
1573 		/* update scanner position. needed for chunked transfers */
1574 		handle->response.scanner.cursor -= *size;
1575 		/* update amount discarded */
1576 		handle->response.msg.amount_discarded += *size;
1577 	}
1578 
1579 	return UPNP_E_SUCCESS;
1580 }
1581 
1582 int http_CloseHttpConnection(void *Handle)
1583 {
1584 	http_connection_handle_t *handle = Handle;
1585 	if (!handle)
1586 		return UPNP_E_INVALID_PARAM;
1587 	/*should shutdown completely */
1588 	sock_destroy(&handle->sock_info, SD_BOTH);
1589 	httpmsg_destroy(&handle->response.msg);
1590 	free(handle);
1591 	return UPNP_E_SUCCESS;
1592 }
1593 
1594 /************************************************************************
1595  * Function: http_SendStatusResponse
1596  *
1597  * Parameters:
1598  *	IN SOCKINFO *info;		Socket information object
1599  *	IN int http_status_code;	error code returned while making
1600  *					or sending the response message
1601  *	IN int request_major_version;	request major version
1602  *	IN int request_minor_version;	request minor version
1603  *
1604  * Description:
1605  *	Generate a response message for the status query and send the
1606  *	status response.
1607  *
1608  * Return: int
1609  *	0 -- success
1610  *	UPNP_E_OUTOF_MEMORY
1611  *	UPNP_E_SOCKET_WRITE
1612  *	UPNP_E_TIMEDOUT
1613  ************************************************************************/
1614 int http_SendStatusResponse(SOCKINFO *info,
1615 	int http_status_code,
1616 	int request_major_version,
1617 	int request_minor_version)
1618 {
1619 	int response_major, response_minor;
1620 	membuffer membuf;
1621 	int ret;
1622 	int timeout;
1623 
1624 	http_CalcResponseVersion(request_major_version,
1625 		request_minor_version,
1626 		&response_major,
1627 		&response_minor);
1628 	membuffer_init(&membuf);
1629 	membuf.size_inc = (size_t)70;
1630 	/* response start line */
1631 	ret = http_MakeMessage(&membuf,
1632 		response_major,
1633 		response_minor,
1634 		"RSCB",
1635 		http_status_code,
1636 		http_status_code);
1637 	if (ret == 0) {
1638 		timeout = HTTP_DEFAULT_TIMEOUT;
1639 		ret = http_SendMessage(
1640 			info, &timeout, "b", membuf.buf, membuf.length);
1641 	}
1642 	membuffer_destroy(&membuf);
1643 
1644 	return ret;
1645 }
1646 
1647 int http_MakeMessage(membuffer *buf,
1648 	int http_major_version,
1649 	int http_minor_version,
1650 	const char *fmt,
1651 	...)
1652 {
1653 	char c;
1654 	char *s = NULL;
1655 	size_t num;
1656 	off_t bignum;
1657 	size_t length;
1658 	time_t *loc_time;
1659 	time_t curr_time;
1660 	struct tm date_storage;
1661 	struct tm *date;
1662 	const char *start_str;
1663 	const char *end_str;
1664 	int status_code;
1665 	const char *status_msg;
1666 	http_method_t method;
1667 	const char *method_str;
1668 	const char *url_str;
1669 	const char *temp_str;
1670 	uri_type url;
1671 	uri_type *uri_ptr;
1672 	int error_code = 0;
1673 	va_list argp;
1674 	char tempbuf[200];
1675 	const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
1676 	const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
1677 				"Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
1678 	int rc = 0;
1679 
1680 	memset(tempbuf, 0, sizeof(tempbuf));
1681 	va_start(argp, fmt);
1682 	while ((c = *fmt++)) {
1683 		if (c == 'E') {
1684 			/* list of extra headers */
1685 			UpnpListIter pos;
1686 			UpnpListHead *head;
1687 			UpnpExtraHeaders *extra;
1688 			const DOMString resp;
1689 			head = (UpnpListHead *)va_arg(argp, UpnpListHead *);
1690 			if (head) {
1691 				for (pos = UpnpListBegin(head);
1692 					pos != UpnpListEnd(head);
1693 					pos = UpnpListNext(head, pos)) {
1694 					extra = (UpnpExtraHeaders *)pos;
1695 					resp = UpnpExtraHeaders_get_resp(extra);
1696 					if (resp) {
1697 						if (membuffer_append(buf,
1698 							    resp,
1699 							    strlen(resp)))
1700 							goto error_handler;
1701 						if (membuffer_append(buf,
1702 							    "\r\n",
1703 							    (size_t)2))
1704 							goto error_handler;
1705 					}
1706 				}
1707 			}
1708 		} else if (c == 's') {
1709 			/* C string */
1710 			s = (char *)va_arg(argp, char *);
1711 			assert(s);
1712 			UpnpPrintf(UPNP_ALL,
1713 				HTTP,
1714 				__FILE__,
1715 				__LINE__,
1716 				"Adding a string : %s\n",
1717 				s);
1718 			if (membuffer_append(buf, s, strlen(s)))
1719 				goto error_handler;
1720 		} else if (c == 'K') {
1721 			/* Add Chunky header */
1722 			if (membuffer_append(buf,
1723 				    "TRANSFER-ENCODING: chunked\r\n",
1724 				    strlen("Transfer-Encoding: chunked\r\n")))
1725 				goto error_handler;
1726 		} else if (c == 'G') {
1727 			/* Add Range header */
1728 			struct SendInstruction *RespInstr;
1729 			RespInstr = (struct SendInstruction *)va_arg(
1730 				argp, struct SendInstruction *);
1731 			assert(RespInstr);
1732 			/* connection header */
1733 			if (membuffer_append(buf,
1734 				    RespInstr->RangeHeader,
1735 				    strlen(RespInstr->RangeHeader)))
1736 				goto error_handler;
1737 		} else if (c == 'b') {
1738 			/* mem buffer */
1739 			s = (char *)va_arg(argp, char *);
1740 			UpnpPrintf(UPNP_ALL,
1741 				HTTP,
1742 				__FILE__,
1743 				__LINE__,
1744 				"Adding a char Buffer starting with: %c\n",
1745 				(int)s[0]);
1746 			assert(s);
1747 			length = (size_t)va_arg(argp, size_t);
1748 			if (membuffer_append(buf, s, length))
1749 				goto error_handler;
1750 		} else if (c == 'c') {
1751 			/* crlf */
1752 			if (membuffer_append(buf, "\r\n", (size_t)2))
1753 				goto error_handler;
1754 		} else if (c == 'd') {
1755 			/* integer */
1756 			num = (size_t)va_arg(argp, int);
1757 			rc = snprintf(tempbuf, sizeof(tempbuf), "%" PRIzu, num);
1758 			if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1759 				membuffer_append(buf, tempbuf, strlen(tempbuf)))
1760 				goto error_handler;
1761 		} else if (c == 'h') {
1762 			/* off_t */
1763 			bignum = (off_t)va_arg(argp, off_t);
1764 			rc = snprintf(tempbuf,
1765 				sizeof(tempbuf),
1766 				"%" PRId64,
1767 				(int64_t)bignum);
1768 			if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1769 				membuffer_append(buf, tempbuf, strlen(tempbuf)))
1770 				goto error_handler;
1771 		} else if (c == 't' || c == 'D') {
1772 			/* date */
1773 			if (c == 'D') {
1774 				/* header */
1775 				start_str = "DATE: ";
1776 				end_str = "\r\n";
1777 				curr_time = time(NULL);
1778 				loc_time = &curr_time;
1779 			} else {
1780 				/* date value only */
1781 				start_str = end_str = "";
1782 				loc_time = (time_t *)va_arg(argp, time_t *);
1783 			}
1784 			assert(loc_time);
1785 			date = http_gmtime_r(loc_time, &date_storage);
1786 			if (date == NULL)
1787 				goto error_handler;
1788 			rc = snprintf(tempbuf,
1789 				sizeof(tempbuf),
1790 				"%s%s, %02d %s %d %02d:%02d:%02d GMT%s",
1791 				start_str,
1792 				&weekday_str[date->tm_wday * 4],
1793 				date->tm_mday,
1794 				&month_str[date->tm_mon * 4],
1795 				date->tm_year + 1900,
1796 				date->tm_hour,
1797 				date->tm_min,
1798 				date->tm_sec,
1799 				end_str);
1800 			if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1801 				membuffer_append(buf, tempbuf, strlen(tempbuf)))
1802 				goto error_handler;
1803 		} else if (c == 'L') {
1804 			/* Add CONTENT-LANGUAGE header only if
1805 			 * WEB_SERVER_CONTENT_LANGUAGE */
1806 			/* is not empty and if Accept-Language header is not
1807 			 * empty */
1808 			struct SendInstruction *RespInstr;
1809 			RespInstr = (struct SendInstruction *)va_arg(
1810 				argp, struct SendInstruction *);
1811 			assert(RespInstr);
1812 			if (strcmp(RespInstr->AcceptLanguageHeader, "") &&
1813 				strcmp(WEB_SERVER_CONTENT_LANGUAGE, "") &&
1814 				http_MakeMessage(buf,
1815 					http_major_version,
1816 					http_minor_version,
1817 					"ssc",
1818 					"CONTENT-LANGUAGE: ",
1819 					WEB_SERVER_CONTENT_LANGUAGE) != 0)
1820 				goto error_handler;
1821 		} else if (c == 'C') {
1822 			if ((http_major_version > 1) ||
1823 				(http_major_version == 1 &&
1824 					http_minor_version == 1)) {
1825 				/* connection header */
1826 				if (membuffer_append_str(
1827 					    buf, "CONNECTION: close\r\n"))
1828 					goto error_handler;
1829 			}
1830 		} else if (c == 'N') {
1831 			/* content-length header */
1832 			bignum = (off_t)va_arg(argp, off_t);
1833 			assert(bignum >= 0);
1834 			if (http_MakeMessage(buf,
1835 				    http_major_version,
1836 				    http_minor_version,
1837 				    "shc",
1838 				    "CONTENT-LENGTH: ",
1839 				    bignum) != 0)
1840 				goto error_handler;
1841 			/* Add accept ranges */
1842 			if (http_MakeMessage(buf,
1843 				    http_major_version,
1844 				    http_minor_version,
1845 				    "sc",
1846 				    "Accept-Ranges: bytes") != 0)
1847 				goto error_handler;
1848 		} else if (c == 'S' || c == 'U') {
1849 			/* SERVER or USER-AGENT header */
1850 			temp_str = (c == 'S') ? "SERVER: " : "USER-AGENT: ";
1851 			get_sdk_info(tempbuf, sizeof(tempbuf));
1852 			if (http_MakeMessage(buf,
1853 				    http_major_version,
1854 				    http_minor_version,
1855 				    "ss",
1856 				    temp_str,
1857 				    tempbuf) != 0)
1858 				goto error_handler;
1859 		} else if (c == 'X') {
1860 			/* C string */
1861 			s = (char *)va_arg(argp, char *);
1862 			assert(s);
1863 			if (membuffer_append_str(buf, "X-User-Agent: ") != 0)
1864 				goto error_handler;
1865 			if (membuffer_append(buf, s, strlen(s)) != 0)
1866 				goto error_handler;
1867 		} else if (c == 'R') {
1868 			/* response start line */
1869 			/*   e.g.: 'HTTP/1.1 200 OK' code */
1870 			status_code = (int)va_arg(argp, int);
1871 			assert(status_code > 0);
1872 			rc = snprintf(tempbuf,
1873 				sizeof(tempbuf),
1874 				"HTTP/%d.%d %d ",
1875 				http_major_version,
1876 				http_minor_version,
1877 				status_code);
1878 			/* str */
1879 			status_msg = http_get_code_text(status_code);
1880 			if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1881 				http_MakeMessage(buf,
1882 					http_major_version,
1883 					http_minor_version,
1884 					"ssc",
1885 					tempbuf,
1886 					status_msg) != 0)
1887 				goto error_handler;
1888 		} else if (c == 'B') {
1889 			/* body of a simple reply */
1890 			status_code = (int)va_arg(argp, int);
1891 			rc = snprintf(tempbuf,
1892 				sizeof(tempbuf),
1893 				"%s%d %s%s",
1894 				"<html><body><h1>",
1895 				status_code,
1896 				http_get_code_text(status_code),
1897 				"</h1></body></html>");
1898 			if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf))
1899 				goto error_handler;
1900 			bignum = (off_t)strlen(tempbuf);
1901 			if (http_MakeMessage(buf,
1902 				    http_major_version,
1903 				    http_minor_version,
1904 				    "NTcs",
1905 				    bignum,       /* content-length */
1906 				    "text/html",  /* content-type */
1907 				    tempbuf) != 0 /* body */
1908 			)
1909 				goto error_handler;
1910 		} else if (c == 'Q') {
1911 			/* request start line */
1912 			/* GET /foo/bar.html HTTP/1.1\r\n */
1913 			method = (http_method_t)va_arg(argp, http_method_t);
1914 			method_str = method_to_str(method);
1915 			url_str = (const char *)va_arg(argp, const char *);
1916 			num = (size_t)va_arg(
1917 				argp, size_t); /* length of url_str */
1918 			if (http_MakeMessage(buf,
1919 				    http_major_version,
1920 				    http_minor_version,
1921 				    "ssbsdsdc",
1922 				    method_str, /* method */
1923 				    " ",
1924 				    url_str,
1925 				    num, /* url */
1926 				    " HTTP/",
1927 				    http_major_version,
1928 				    ".",
1929 				    http_minor_version) != 0)
1930 				goto error_handler;
1931 		} else if (c == 'q') {
1932 			/* request start line and HOST header */
1933 			method = (http_method_t)va_arg(argp, http_method_t);
1934 			uri_ptr = (uri_type *)va_arg(argp, uri_type *);
1935 			assert(uri_ptr);
1936 			if (http_FixUrl(uri_ptr, &url) != 0) {
1937 				error_code = UPNP_E_INVALID_URL;
1938 				goto error_handler;
1939 			}
1940 			if (http_MakeMessage(buf,
1941 				    http_major_version,
1942 				    http_minor_version,
1943 				    "Q"
1944 				    "sbc",
1945 				    method,
1946 				    url.pathquery.buff,
1947 				    url.pathquery.size,
1948 				    "HOST: ",
1949 				    url.hostport.text.buff,
1950 				    url.hostport.text.size) != 0)
1951 				goto error_handler;
1952 		} else if (c == 'T') {
1953 			/* content type header */
1954 			temp_str = (const char *)va_arg(
1955 				argp, const char *); /* type/subtype format */
1956 			if (http_MakeMessage(buf,
1957 				    http_major_version,
1958 				    http_minor_version,
1959 				    "ssc",
1960 				    "CONTENT-TYPE: ",
1961 				    temp_str) != 0)
1962 				goto error_handler;
1963 		} else {
1964 			assert(0);
1965 		}
1966 	}
1967 	goto ExitFunction;
1968 
1969 error_handler:
1970 	/* Default is out of memory error. */
1971 	if (!error_code)
1972 		error_code = UPNP_E_OUTOF_MEMORY;
1973 	membuffer_destroy(buf);
1974 
1975 ExitFunction:
1976 	va_end(argp);
1977 	return error_code;
1978 }
1979 
1980 /************************************************************************
1981  * Function: http_CalcResponseVersion
1982  *
1983  * Parameters:
1984  *	IN int request_major_vers;	Request major version
1985  *	IN int request_minor_vers;	Request minor version
1986  *	OUT int* response_major_vers;	Response mojor version
1987  *	OUT int* response_minor_vers;	Response minor version
1988  *
1989  * Description:
1990  *	Calculate HTTP response versions based on the request versions.
1991  *
1992  * Return: void
1993  ************************************************************************/
1994 void http_CalcResponseVersion(int request_major_vers,
1995 	int request_minor_vers,
1996 	int *response_major_vers,
1997 	int *response_minor_vers)
1998 {
1999 	if ((request_major_vers > 1) ||
2000 		(request_major_vers == 1 && request_minor_vers >= 1)) {
2001 		*response_major_vers = 1;
2002 		*response_minor_vers = 1;
2003 	} else {
2004 		*response_major_vers = request_major_vers;
2005 		*response_minor_vers = request_minor_vers;
2006 	}
2007 }
2008 
2009 /************************************************************************
2010  * Function: MakeGetMessageEx
2011  *
2012  * Parameters:
2013  *	const char *url_str;	String as a URL
2014  *	membuffer *request;	Buffer containing the request
2015  *	uri_type *url; 		URI object containing the scheme, path
2016  *				query token, etc.
2017  *
2018  * Description:
2019  *	Makes the message for the HTTP GET method
2020  *
2021  * Returns:
2022  *	UPNP_E_INVALID_URL
2023  * 	Error Codes returned by http_MakeMessage
2024  *	UPNP_E_SUCCESS
2025  ************************************************************************/
2026 int MakeGetMessageEx(const char *url_str,
2027 	membuffer *request,
2028 	uri_type *url,
2029 	struct SendInstruction *pRangeSpecifier)
2030 {
2031 	size_t url_str_len;
2032 	int ret_code = UPNP_E_SUCCESS;
2033 	size_t hostlen = 0;
2034 	const char *hoststr;
2035 
2036 	url_str_len = strlen(url_str);
2037 	do {
2038 		UpnpPrintf(UPNP_INFO,
2039 			HTTP,
2040 			__FILE__,
2041 			__LINE__,
2042 			"DOWNLOAD URL : %s\n",
2043 			url_str);
2044 		ret_code = http_FixStrUrl(url_str, url_str_len, url);
2045 		if (ret_code != UPNP_E_SUCCESS) {
2046 			break;
2047 		}
2048 		/* make msg */
2049 		membuffer_init(request);
2050 		ret_code = get_hoststr(url_str, &hoststr, &hostlen);
2051 		if (ret_code != UPNP_E_SUCCESS) {
2052 			break;
2053 		}
2054 		UpnpPrintf(UPNP_INFO,
2055 			HTTP,
2056 			__FILE__,
2057 			__LINE__,
2058 			"HOSTNAME : %s Length : %" PRIzu "\n",
2059 			hoststr,
2060 			hostlen);
2061 		ret_code = http_MakeMessage(request,
2062 			1,
2063 			1,
2064 			"Q"
2065 			"s"
2066 			"bc"
2067 			"GDCUc",
2068 			HTTPMETHOD_GET,
2069 			url->pathquery.buff,
2070 			url->pathquery.size,
2071 			"HOST: ",
2072 			hoststr,
2073 			hostlen,
2074 			pRangeSpecifier);
2075 		if (ret_code != 0) {
2076 			UpnpPrintf(UPNP_INFO,
2077 				HTTP,
2078 				__FILE__,
2079 				__LINE__,
2080 				"HTTP Makemessage failed\n");
2081 			membuffer_destroy(request);
2082 			return ret_code;
2083 		}
2084 	} while (0);
2085 	UpnpPrintf(UPNP_INFO,
2086 		HTTP,
2087 		__FILE__,
2088 		__LINE__,
2089 		"HTTP Buffer:\n%s\n"
2090 		"----------END--------\n",
2091 		request->buf);
2092 
2093 	return ret_code;
2094 }
2095 
2096 #define SIZE_RANGE_BUFFER 50
2097 
2098 /************************************************************************
2099  * Function: http_OpenHttpGetEx
2100  *
2101  * Parameters:
2102  *	IN const char *url_str;		String as a URL
2103  *	IN OUT void **Handle;		Pointer to buffer to store HTTP
2104  *					post handle
2105  *	IN OUT char **contentType;	Type of content
2106  *	OUT int *contentLength;		length of content
2107  *	OUT int *httpStatus;		HTTP status returned on receiving a
2108  *					response message
2109  *	IN int timeout;			time out value
2110  *
2111  * Description:
2112  *	Makes the HTTP GET message, connects to the peer,
2113  *	sends the HTTP GET request, gets the response and parses the
2114  *	response.
2115  *
2116  * Return: int
2117  *	UPNP_E_SUCCESS		- On Success
2118  *	UPNP_E_INVALID_PARAM	- Invalid Paramters
2119  *	UPNP_E_OUTOF_MEMORY
2120  *	UPNP_E_SOCKET_ERROR
2121  *	UPNP_E_BAD_RESPONSE
2122  ************************************************************************/
2123 int http_OpenHttpGetEx(const char *url_str,
2124 	void **Handle,
2125 	char **contentType,
2126 	int *contentLength,
2127 	int *httpStatus,
2128 	int lowRange,
2129 	int highRange,
2130 	int timeout)
2131 {
2132 	int http_error_code;
2133 	memptr ctype;
2134 	SOCKET tcp_connection;
2135 	size_t sockaddr_len;
2136 	membuffer request;
2137 	http_connection_handle_t *handle = NULL;
2138 	uri_type url;
2139 	parse_status_t status;
2140 	int errCode = UPNP_E_SUCCESS;
2141 	/* char rangeBuf[SIZE_RANGE_BUFFER]; */
2142 	struct SendInstruction rangeBuf;
2143 	int rc = 0;
2144 
2145 	membuffer_init(&request);
2146 
2147 	do {
2148 		/* Checking Input parameters */
2149 		if (!url_str || !Handle || !contentType || !httpStatus) {
2150 			errCode = UPNP_E_INVALID_PARAM;
2151 			break;
2152 		}
2153 		/* Initialize output parameters */
2154 		*httpStatus = 0;
2155 		*Handle = handle;
2156 		*contentType = NULL;
2157 		*contentLength = 0;
2158 		if (lowRange > highRange) {
2159 			errCode = UPNP_E_INTERNAL_ERROR;
2160 			break;
2161 		}
2162 		memset(&rangeBuf, 0, sizeof(rangeBuf));
2163 		rc = snprintf(rangeBuf.RangeHeader,
2164 			sizeof(rangeBuf.RangeHeader),
2165 			"Range: bytes=%d-%d\r\n",
2166 			lowRange,
2167 			highRange);
2168 		if (rc < 0 || (unsigned int)rc >= sizeof(rangeBuf.RangeHeader))
2169 			break;
2170 		membuffer_init(&request);
2171 		errCode = MakeGetMessageEx(url_str, &request, &url, &rangeBuf);
2172 		if (errCode != UPNP_E_SUCCESS)
2173 			break;
2174 		handle = (http_connection_handle_t *)malloc(
2175 			sizeof(http_connection_handle_t));
2176 		if (!handle) {
2177 			errCode = UPNP_E_OUTOF_MEMORY;
2178 			break;
2179 		}
2180 		memset(handle, 0, sizeof(*handle));
2181 		parser_response_init(&handle->response, HTTPMETHOD_GET);
2182 		tcp_connection = socket(
2183 			(int)url.hostport.IPaddress.ss_family, SOCK_STREAM, 0);
2184 		if (tcp_connection == INVALID_SOCKET) {
2185 			errCode = UPNP_E_SOCKET_ERROR;
2186 			free(handle);
2187 			break;
2188 		}
2189 		if (sock_init(&handle->sock_info, tcp_connection) !=
2190 			UPNP_E_SUCCESS) {
2191 			sock_destroy(&handle->sock_info, SD_BOTH);
2192 			errCode = UPNP_E_SOCKET_ERROR;
2193 			free(handle);
2194 			break;
2195 		}
2196 		sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6
2197 				       ? sizeof(struct sockaddr_in6)
2198 				       : sizeof(struct sockaddr_in);
2199 		errCode = private_connect(handle->sock_info.socket,
2200 			(struct sockaddr *)&(url.hostport.IPaddress),
2201 			(socklen_t)sockaddr_len);
2202 		if (errCode == -1) {
2203 			sock_destroy(&handle->sock_info, SD_BOTH);
2204 			errCode = UPNP_E_SOCKET_CONNECT;
2205 			free(handle);
2206 			break;
2207 		}
2208 		/* send request */
2209 		errCode = http_SendMessage(&handle->sock_info,
2210 			&timeout,
2211 			"b",
2212 			request.buf,
2213 			request.length);
2214 		if (errCode != UPNP_E_SUCCESS) {
2215 			sock_destroy(&handle->sock_info, SD_BOTH);
2216 			free(handle);
2217 			break;
2218 		}
2219 		if (ReadResponseLineAndHeaders(&handle->sock_info,
2220 			    &handle->response,
2221 			    &timeout,
2222 			    &http_error_code) != (int)PARSE_OK) {
2223 			errCode = UPNP_E_BAD_RESPONSE;
2224 			free(handle);
2225 			break;
2226 		}
2227 		status = parser_get_entity_read_method(&handle->response);
2228 		if (status != (parse_status_t)PARSE_CONTINUE_1 &&
2229 			status != (parse_status_t)PARSE_SUCCESS) {
2230 			errCode = UPNP_E_BAD_RESPONSE;
2231 			free(handle);
2232 			break;
2233 		}
2234 		*httpStatus = handle->response.msg.status_code;
2235 		errCode = UPNP_E_SUCCESS;
2236 
2237 		if (!httpmsg_find_hdr(
2238 			    &handle->response.msg, HDR_CONTENT_TYPE, &ctype))
2239 			/* no content-type */
2240 			*contentType = NULL;
2241 		else
2242 			*contentType = ctype.buf;
2243 		if (handle->response.position == (parser_pos_t)POS_COMPLETE)
2244 			*contentLength = 0;
2245 		else if (handle->response.ent_position == ENTREAD_USING_CHUNKED)
2246 			*contentLength = UPNP_USING_CHUNKED;
2247 		else if (handle->response.ent_position == ENTREAD_USING_CLEN)
2248 			*contentLength = (int)handle->response.content_length;
2249 		else if (handle->response.ent_position == ENTREAD_UNTIL_CLOSE)
2250 			*contentLength = UPNP_UNTIL_CLOSE;
2251 		*Handle = handle;
2252 	} while (0);
2253 
2254 	membuffer_destroy(&request);
2255 
2256 	return errCode;
2257 }
2258 
2259 /************************************************************************
2260  * Function: get_sdk_info
2261  *
2262  * Parameters:
2263  *	OUT char *info;	buffer to store the operating system information
2264  *	IN size_t infoSize; size of buffer
2265  *
2266  * Description:
2267  *	Returns the server information for the operating system
2268  *
2269  * Return:
2270  *	UPNP_INLINE void
2271  ************************************************************************/
2272 /* 'info' should have a size of at least 100 bytes */
2273 void get_sdk_info(char *info, size_t infoSize)
2274 {
2275 #ifdef UPNP_ENABLE_UNSPECIFIED_SERVER
2276 	snprintf(info, infoSize, "Unspecified, UPnP/1.0, Unspecified\r\n");
2277 #else /* UPNP_ENABLE_UNSPECIFIED_SERVER */
2278 	#ifdef _WIN32
2279 	OSVERSIONINFO versioninfo;
2280 	versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
2281 
2282 	if (GetVersionEx(&versioninfo) != 0)
2283 		snprintf(info,
2284 			infoSize,
2285 			"%d.%d.%d %d/%s, UPnP/1.0, Portable SDK for UPnP "
2286 			"devices/" UPNP_VERSION_STRING "\r\n",
2287 			versioninfo.dwMajorVersion,
2288 			versioninfo.dwMinorVersion,
2289 			versioninfo.dwBuildNumber,
2290 			versioninfo.dwPlatformId,
2291 			versioninfo.szCSDVersion);
2292 	else
2293 		*info = '\0';
2294 	#else
2295 	int ret_code;
2296 	struct utsname sys_info;
2297 
2298 	ret_code = uname(&sys_info);
2299 	if (ret_code == -1)
2300 		*info = '\0';
2301 	snprintf(info,
2302 		infoSize,
2303 		"%s/%s, UPnP/1.0, Portable SDK for UPnP "
2304 		"devices/" UPNP_VERSION_STRING "\r\n",
2305 		sys_info.sysname,
2306 		sys_info.release);
2307 	#endif
2308 #endif /* UPNP_ENABLE_UNSPECIFIED_SERVER */
2309 }
2310