unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

webserver.c (17087B)

      1 /*
      2  * Webserver
      3  * (C)Copyright 2016 Bram Matthys and the UnrealIRCd team
      4  * License: GPLv2 or later
      5  */
      6    
      7 #include "unrealircd.h"
      8 #include "dns.h"
      9 
     10 ModuleHeader MOD_HEADER
     11   = {
     12 	"webserver",
     13 	"1.0.0",
     14 	"Webserver",
     15 	"UnrealIRCd Team",
     16 	"unrealircd-6",
     17     };
     18 
     19 #if CHAR_MIN < 0
     20  #error "In UnrealIRCd char should always be unsigned. Check your compiler"
     21 #endif
     22 
     23 /* How many seconds to wait with closing after sending the response */
     24 #define WEB_CLOSE_TIME 1
     25 
     26 /* The "Server: xyz" in the response */
     27 #define WEB_SOFTWARE "UnrealIRCd"
     28 
     29 /* Macros */
     30 #define WEB(client)		((WebRequest *)moddata_client(client, webserver_md).ptr)
     31 #define WEBSERVER(client)	((client->local && client->local->listener) ? client->local->listener->webserver : NULL)
     32 #define reset_handshake_timeout(client, delta)  do { client->local->creationtime = TStime() - iConf.handshake_timeout + delta; } while(0)
     33 
     34 /* Forward declarations */
     35 int webserver_packet_out(Client *from, Client *to, Client *intended_to, char **msg, int *length);
     36 int webserver_packet_in(Client *client, const char *readbuf, int *length);
     37 void webserver_mdata_free(ModData *m);
     38 int webserver_handle_packet(Client *client, const char *readbuf, int length);
     39 int webserver_handle_handshake(Client *client, const char *readbuf, int *length);
     40 int webserver_handle_request_header(Client *client, const char *readbuf, int *length);
     41 void _webserver_send_response(Client *client, int status, char *msg);
     42 void _webserver_close_client(Client *client);
     43 int _webserver_handle_body(Client *client, WebRequest *web, const char *readbuf, int length);
     44 
     45 /* Global variables */
     46 ModDataInfo *webserver_md;
     47 
     48 MOD_TEST()
     49 {
     50 	MARK_AS_OFFICIAL_MODULE(modinfo);
     51 	EfunctionAddVoid(modinfo->handle, EFUNC_WEBSERVER_SEND_RESPONSE, _webserver_send_response);
     52 	EfunctionAddVoid(modinfo->handle, EFUNC_WEBSERVER_CLOSE_CLIENT, _webserver_close_client);
     53 	EfunctionAdd(modinfo->handle, EFUNC_WEBSERVER_HANDLE_BODY, _webserver_handle_body);
     54 	return MOD_SUCCESS;
     55 }
     56 
     57 MOD_INIT()
     58 {
     59 	ModDataInfo mreq;
     60 
     61 	MARK_AS_OFFICIAL_MODULE(modinfo);
     62 
     63 	//HookAdd(modinfo->handle, HOOKTYPE_PACKET, INT_MAX, webserver_packet_out);
     64 	HookAdd(modinfo->handle, HOOKTYPE_RAWPACKET_IN, INT_MIN, webserver_packet_in);
     65 
     66 	memset(&mreq, 0, sizeof(mreq));
     67 	mreq.name = "web";
     68 	mreq.serialize = NULL;
     69 	mreq.unserialize = NULL;
     70 	mreq.free = webserver_mdata_free;
     71 	mreq.sync = 0;
     72 	mreq.type = MODDATATYPE_CLIENT;
     73 	webserver_md = ModDataAdd(modinfo->handle, mreq);
     74 
     75 	return MOD_SUCCESS;
     76 }
     77 
     78 MOD_LOAD()
     79 {
     80 	return MOD_SUCCESS;
     81 }
     82 
     83 MOD_UNLOAD()
     84 {
     85 	return MOD_SUCCESS;
     86 }
     87 
     88 /** UnrealIRCd internals: free WebRequest object. */
     89 void webserver_mdata_free(ModData *m)
     90 {
     91 	WebRequest *wsu = (WebRequest *)m->ptr;
     92 	if (wsu)
     93 	{
     94 		safe_free(wsu->uri);
     95 		free_nvplist(wsu->headers);
     96 		safe_free(wsu->lefttoparse);
     97 		safe_free(wsu->request_buffer);
     98 		safe_free(m->ptr);
     99 	}
    100 }
    101 
    102 /** Outgoing packet hook.
    103  * Do we need this?
    104  */
    105 int webserver_packet_out(Client *from, Client *to, Client *intended_to, char **msg, int *length)
    106 {
    107 	static char utf8buf[510];
    108 
    109 	if (MyConnect(to) && WEB(to))
    110 	{
    111 		// TODO: Inhibit all?
    112 		// Websocket can override though?
    113 		return 0;
    114 	}
    115 	return 0;
    116 }
    117 
    118 HttpMethod webserver_get_method(const char *buf)
    119 {
    120 	if (!strncmp(buf, "HEAD ", 5))
    121 		return HTTP_METHOD_HEAD;
    122 	if (!strncmp(buf, "GET ", 4))
    123 		return HTTP_METHOD_GET;
    124 	if (!strncmp(buf, "PUT ", 4))
    125 		return HTTP_METHOD_PUT;
    126 	if (!strncmp(buf, "POST ", 5))
    127 		return HTTP_METHOD_POST;
    128 	return HTTP_METHOD_NONE; /* invalid */
    129 }
    130 
    131 void webserver_possible_request(Client *client, const char *buf, int len)
    132 {
    133 	HttpMethod method;
    134 
    135 	if (len < 8)
    136 		return;
    137 
    138 	/* Probably redundant, but just to be sure, if already tagged, then don't change it! */
    139 	if (WEB(client))
    140 		return;
    141 
    142 	method = webserver_get_method(buf);
    143 	if (method == HTTP_METHOD_NONE)
    144 		return; /* invalid */
    145 
    146 	moddata_client(client, webserver_md).ptr = safe_alloc(sizeof(WebRequest));
    147 	WEB(client)->method = method;
    148 
    149 	/* Set some default values: */
    150 	WEB(client)->content_length = -1;
    151 	WEB(client)->config_max_request_buffer_size = 4096; /* 4k */
    152 }
    153 
    154 /** Incoming packet hook. This processes web requests.
    155  * NOTE The different return values:
    156  * -1 means: don't touch this client anymore, it has or might have been killed!
    157  * 0 means: don't process this data, but you can read another packet if you want
    158  * >0 means: process this data (regular IRC data, non-web stuff)
    159  */
    160 int webserver_packet_in(Client *client, const char *readbuf, int *length)
    161 {
    162 	if ((client->local->traffic.messages_received == 0) && WEBSERVER(client))
    163 		webserver_possible_request(client, readbuf, *length);
    164 
    165 	if (!WEB(client))
    166 		return 1; /* "normal" IRC client */
    167 
    168 	if (WEB(client)->request_header_parsed)
    169 		return WEBSERVER(client)->handle_body(client, WEB(client), readbuf, *length);
    170 
    171 	/* else.. */
    172 	return webserver_handle_request_header(client, readbuf, length);
    173 }
    174 
    175 /** Helper function to parse the HTTP header consisting of multiple 'Key: value' pairs */
    176 int webserver_handshake_helper(char *buffer, int len, char **key, char **value, char **lastloc, int *end_of_request)
    177 {
    178 	static char buf[4096], *nextptr;
    179 	char *p;
    180 	char *k = NULL, *v = NULL;
    181 	int foundlf = 0;
    182 
    183 	if (buffer)
    184 	{
    185 		/* Initialize */
    186 		if (len > sizeof(buf) - 1)
    187 			len = sizeof(buf) - 1;
    188 
    189 		memcpy(buf, buffer, len);
    190 		buf[len] = '\0';
    191 		nextptr = buf;
    192 	}
    193 
    194 	*end_of_request = 0;
    195 
    196 	p = nextptr;
    197 
    198 	if (!p)
    199 	{
    200 		*key = *value = NULL;
    201 		return 0; /* done processing data */
    202 	}
    203 
    204 	if (!strncmp(p, "\n", 1) || !strncmp(p, "\r\n", 2))
    205 	{
    206 		*key = *value = NULL;
    207 		*end_of_request = 1;
    208 		return 0;
    209 	}
    210 
    211 	/* Note: p *could* point to the NUL byte ('\0') */
    212 
    213 	/* Special handling for GET line itself. */
    214 	if (webserver_get_method(p) != HTTP_METHOD_NONE)
    215 	{
    216 		k = "REQUEST";
    217 		p = strchr(p, ' ') + 1; /* space (0x20) is guaranteed to be there, see strncmp above */
    218 		v = p; /* SET VALUE */
    219 		nextptr = NULL; /* set to "we are done" in case next for loop fails */
    220 		for (; *p; p++)
    221 		{
    222 			if (*p == ' ')
    223 			{
    224 				*p = '\0'; /* terminate before "HTTP/1.X" part */
    225 			}
    226 			else if (*p == '\r')
    227 			{
    228 				*p = '\0'; /* eat silently, but don't consider EOL */
    229 			}
    230 			else if (*p == '\n')
    231 			{
    232 				*p = '\0';
    233 				nextptr = p+1; /* safe, there is data or at least a \0 there */
    234 				break;
    235 			}
    236 		}
    237 		*key = k;
    238 		*value = v;
    239 		return 1;
    240 	}
    241 
    242 	/* Header parsing starts here.
    243 	 * Example line "Host: www.unrealircd.org"
    244 	 */
    245 	k = p; /* SET KEY */
    246 
    247 	/* First check if the line contains a terminating \n. If not, don't process it
    248 	 * as it may have been a cut header.
    249 	 */
    250 	for (; *p; p++)
    251 	{
    252 		if (*p == '\n')
    253 		{
    254 			foundlf = 1;
    255 			break;
    256 		}
    257 	}
    258 
    259 	if (!foundlf)
    260 	{
    261 		*key = *value = NULL;
    262 		*lastloc = k;
    263 		return 0;
    264 	}
    265 
    266 	p = k;
    267 
    268 	for (; *p; p++)
    269 	{
    270 		if ((*p == '\n') || (*p == '\r'))
    271 		{
    272 			/* Reached EOL but 'value' not found */
    273 			*p = '\0';
    274 			break;
    275 		}
    276 		if (*p == ':')
    277 		{
    278 			*p++ = '\0';
    279 			if (*p++ != ' ')
    280 				break; /* missing mandatory space after ':' */
    281 
    282 			v = p; /* SET VALUE */
    283 			nextptr = NULL; /* set to "we are done" in case next for loop fails */
    284 			for (; *p; p++)
    285 			{
    286 				if (*p == '\r')
    287 				{
    288 					*p = '\0'; /* eat silently, but don't consider EOL */
    289 				}
    290 				else if (*p == '\n')
    291 				{
    292 					*p = '\0';
    293 					nextptr = p+1; /* safe, there is data or at least a \0 there */
    294 					break;
    295 				}
    296 			}
    297 			/* A key-value pair was succesfully parsed, return it */
    298 			*key = k;
    299 			*value = v;
    300 			return 1;
    301 		}
    302 	}
    303 
    304 	/* Fatal parse error */
    305 	*key = *value = NULL;
    306 	return 0;
    307 }
    308 
    309 /** Check if there is any data at the end of the request */
    310 char *find_end_of_request(char *header, int totalsize, int *remaining_bytes)
    311 {
    312 	char *nextframe1;
    313 	char *nextframe2;
    314 	char *nextframe = NULL;
    315 
    316 	// find first occurance, yeah this is just stupid, but it works.
    317 	nextframe1 = strstr(header, "\r\n\r\n"); // = +4
    318 	nextframe2 = strstr(header, "\n\n");     // = +2
    319 	if (nextframe1 && nextframe2)
    320 	{
    321 		if (nextframe1 < nextframe2)
    322 		{
    323 			nextframe = nextframe1 + 4;
    324 		} else {
    325 			nextframe = nextframe2 + 2;
    326 		}
    327 	} else
    328 	if (nextframe1)
    329 	{
    330 		nextframe = nextframe1 + 4;
    331 	} else
    332 	if (nextframe2)
    333 	{
    334 		nextframe = nextframe2 + 2;
    335 	}
    336 	if (nextframe)
    337 	{
    338 		*remaining_bytes = totalsize - (nextframe - header);
    339 		if (*remaining_bytes > 0)
    340 			return nextframe;
    341 	}
    342 	return NULL;
    343 }
    344 
    345 /** Handle HTTP request
    346  * Yes, I'm going to assume that the header fits in one packet and one packet only.
    347  */
    348 int webserver_handle_request_header(Client *client, const char *readbuf, int *length)
    349 {
    350 	char *key, *value;
    351 	int r, end_of_request;
    352 	static char netbuf[16384];
    353 	static char netbuf2[16384];
    354 	char *lastloc = NULL;
    355 	int n, maxcopy, nprefix=0;
    356 	int totalsize;
    357 
    358 	/* Totally paranoid: */
    359 	memset(netbuf, 0, sizeof(netbuf));
    360 	memset(netbuf2, 0, sizeof(netbuf2));
    361 
    362 	/** Frame re-assembling starts here **/
    363 	if (WEB(client)->lefttoparse)
    364 	{
    365 		strlcpy(netbuf, WEB(client)->lefttoparse, sizeof(netbuf));
    366 		nprefix = strlen(netbuf);
    367 	}
    368 	maxcopy = sizeof(netbuf) - nprefix - 1;
    369 	/* (Need to do some manual checking here as strlen() can't be safely used
    370 	 *  on readbuf. Same is true for strlncat since it uses strlen().)
    371 	 */
    372 	n = *length;
    373 	if (n > maxcopy)
    374 		n = maxcopy;
    375 	if (n <= 0)
    376 	{
    377 		webserver_close_client(client); // Oversized line
    378 		return -1;
    379 	}
    380 	memcpy(netbuf+nprefix, readbuf, n); /* SAFE: see checking above */
    381 	totalsize = n + nprefix;
    382 	netbuf[totalsize] = '\0';
    383 	memcpy(netbuf2, netbuf, totalsize+1); // copy, including the "always present \0 at the end just in case we use strstr etc".
    384 	safe_free(WEB(client)->lefttoparse);
    385 
    386 	/** Now step through the lines.. **/
    387 	for (r = webserver_handshake_helper(netbuf, strlen(netbuf), &key, &value, &lastloc, &end_of_request);
    388 	     r;
    389 	     r = webserver_handshake_helper(NULL, 0, &key, &value, &lastloc, &end_of_request))
    390 	{
    391 		if (BadPtr(value))
    392 			continue; /* skip empty values */
    393 
    394 		if (!strcasecmp(key, "REQUEST"))
    395 		{
    396 			safe_strdup(WEB(client)->uri, value);
    397 		} else
    398 		{
    399 			if (!strcasecmp(key, "Content-Length"))
    400 			{
    401 				WEB(client)->content_length = atoll(value);
    402 			} else
    403 			if (!strcasecmp(key, "Transfer-Encoding"))
    404 			{
    405 				if (!strcasecmp(value, "chunked"))
    406 					WEB(client)->transfer_encoding = TRANSFER_ENCODING_CHUNKED;
    407 			}
    408 			add_nvplist(&WEB(client)->headers, WEB(client)->num_headers, key, value);
    409 		}
    410 	}
    411 
    412 	if (end_of_request)
    413 	{
    414 		int n;
    415 		int remaining_bytes = 0;
    416 		char *nextframe;
    417 
    418 		/* Some sanity checks */
    419 		if (!WEB(client)->uri)
    420 		{
    421 			webserver_send_response(client, 400, "Malformed HTTP request");
    422 			return -1;
    423 		}
    424 
    425 		WEB(client)->request_header_parsed = 1;
    426 		n = WEBSERVER(client)->handle_request(client, WEB(client));
    427 		if ((n <= 0) || IsDead(client))
    428 			return n; /* byebye */
    429 		
    430 		/* There could be data directly after the request header (eg for
    431 		 * a POST or PUT), check for it here so it isn't lost.
    432 		 */
    433 		nextframe = find_end_of_request(netbuf2, totalsize, &remaining_bytes);
    434 		if (nextframe)
    435 			return WEBSERVER(client)->handle_body(client, WEB(client), nextframe, remaining_bytes);
    436 		return 0;
    437 	}
    438 
    439 	if (lastloc)
    440 	{
    441 		/* Last line was cut somewhere, save it for next round. */
    442 		safe_strdup(WEB(client)->lefttoparse, lastloc);
    443 	}
    444 	return 0; /* don't let UnrealIRCd process this */
    445 }
    446 
    447 /** Send a HTTP(S) response.
    448  * @param client	Client to send to
    449  * @param status	HTTP status code
    450  * @param msg		The message body.
    451  * @note if 'msgs' is NULL then don't close the connection.
    452  */
    453 void _webserver_send_response(Client *client, int status, char *msg)
    454 {
    455 	char buf[512];
    456 	char *statusmsg = "???";
    457 
    458 	if (status == 200)
    459 		statusmsg = "OK";
    460 	else if (status == 201)
    461 		statusmsg = "Created";
    462 	else if (status == 500)
    463 		statusmsg = "Internal Server Error";
    464 	else if (status == 400)
    465 		statusmsg = "Bad Request";
    466 	else if (status == 401)
    467 		statusmsg = "Unauthorized";
    468 	else if (status == 403)
    469 		statusmsg = "Forbidden";
    470 	else if (status == 404)
    471 		statusmsg = "Not Found";
    472 	else if (status == 416)
    473 		statusmsg = "Range Not Satisfiable";
    474 
    475 	snprintf(buf, sizeof(buf),
    476 		"HTTP/1.1 %d %s\r\nServer: %s\r\nConnection: close\r\n\r\n",
    477 		status, statusmsg, WEB_SOFTWARE);
    478 	if (msg)
    479 	{
    480 		strlcat(buf, msg, sizeof(buf));
    481 		strlcat(buf, "\n", sizeof(buf));
    482 	}
    483 
    484 	dbuf_put(&client->local->sendQ, buf, strlen(buf));
    485 	if (msg)
    486 		webserver_close_client(client);
    487 }
    488 
    489 /** Close a web client softly, after data has been sent. */
    490 void _webserver_close_client(Client *client)
    491 {
    492 	send_queued(client);
    493 	if (DBufLength(&client->local->sendQ) == 0)
    494 	{
    495 		exit_client(client, NULL, "End of request");
    496 		//dead_socket(client, "");
    497 	} else {
    498 		send_queued(client);
    499 		reset_handshake_timeout(client, WEB_CLOSE_TIME);
    500 	}
    501 }
    502 
    503 int webserver_handle_body_append_buffer(Client *client, const char *buf, int len)
    504 {
    505 	/* Guard.. */
    506 	if (len <= 0)
    507 	{
    508 		dead_socket(client, "HTTP request error");
    509 		return 0;
    510 	}
    511 
    512 	if (WEB(client)->request_buffer)
    513 	{
    514 		long long newsize = WEB(client)->request_buffer_size + len + 1;
    515 		if (newsize > WEB(client)->config_max_request_buffer_size)
    516 		{
    517 			/* We would overflow */
    518 			unreal_log(ULOG_WARNING, "webserver", "HTTP_BODY_TOO_LARGE", client,
    519 			           "[webserver] Client $client: request body too large ($length)",
    520 			           log_data_integer("length", newsize));
    521 			dead_socket(client, "");
    522 			return 0;
    523 		}
    524 		WEB(client)->request_buffer = realloc(WEB(client)->request_buffer, newsize);
    525 	} else
    526 	{
    527 		if (len + 1 > WEB(client)->config_max_request_buffer_size)
    528 		{
    529 			/* We would overflow */
    530 			unreal_log(ULOG_WARNING, "webserver", "HTTP_BODY_TOO_LARGE", client,
    531 			           "[webserver] Client $client: request body too large ($length)",
    532 			           log_data_integer("length", len+1));
    533 			dead_socket(client, "");
    534 			return 0;
    535 		}
    536 		WEB(client)->request_buffer = malloc(len+1);
    537 	}
    538 	memcpy(WEB(client)->request_buffer + WEB(client)->request_buffer_size, buf, len);
    539 	WEB(client)->request_buffer_size += len;
    540 	WEB(client)->request_buffer[WEB(client)->request_buffer_size] = '\0';
    541 	return 1;
    542 }
    543 
    544 /** Handle HTTP body parsing, eg for a PUT request, concatting it all together.
    545  * @param client	The client
    546  * @param web		The WEB(client)
    547  * @param readbuf	Packet in the read buffer
    548  * @param pktsize	Packet size of the read buffer
    549  * @return 1 to continue processing, 0 if client is killed.
    550  */
    551 int _webserver_handle_body(Client *client, WebRequest *web, const char *readbuf, int pktsize)
    552 {
    553 	char *buf;
    554 	long long n;
    555 	char *free_this_buffer = NULL;
    556 
    557 	if (WEB(client)->transfer_encoding == TRANSFER_ENCODING_NONE)
    558 	{
    559 		if (!webserver_handle_body_append_buffer(client, readbuf, pktsize))
    560 			return 0;
    561 
    562 		if ((WEB(client)->content_length >= 0) &&
    563 		    (WEB(client)->request_buffer_size >= WEB(client)->content_length))
    564 		{
    565 			WEB(client)->request_body_complete = 1;
    566 		}
    567 		return 1;
    568 	}
    569 
    570 	/* Fill 'buf' nd set 'buflen' with what we had + what we have now.
    571 	 * Makes things easy.
    572 	 */
    573 	if (WEB(client)->lefttoparse)
    574 	{
    575 		n = WEB(client)->lefttoparselen + pktsize;
    576 		free_this_buffer = buf = safe_alloc(n);
    577 		memcpy(buf, WEB(client)->lefttoparse, WEB(client)->lefttoparselen);
    578 		memcpy(buf+WEB(client)->lefttoparselen, readbuf, pktsize);
    579 		safe_free(WEB(client)->lefttoparse);
    580 		WEB(client)->lefttoparselen = 0;
    581 	} else {
    582 		n = pktsize;
    583 		free_this_buffer = buf = safe_alloc(n);
    584 		memcpy(buf, readbuf, n);
    585 	}
    586 
    587 	/* Chunked transfers.. yayyyy.. */
    588 	while (n > 0)
    589 	{
    590 		if (WEB(client)->chunk_remaining > 0)
    591 		{
    592 			/* Eat it */
    593 			int eat = MIN(WEB(client)->chunk_remaining, n);
    594 			if (!webserver_handle_body_append_buffer(client, buf, eat))
    595 			{
    596 				/* fatal error such as size exceeded */
    597 				safe_free(free_this_buffer);
    598 				return 0;
    599 			}
    600 			n -= eat;
    601 			buf += eat;
    602 			WEB(client)->chunk_remaining -= eat;
    603 		} else
    604 		{
    605 			int gotlf = 0;
    606 			int i;
    607 
    608 			/* First check if it is a (trailing) empty line,
    609 			 * eg from a previous chunk. Skip over.
    610 			 */
    611 			if ((n >= 2) && !strncmp(buf, "\r\n", 2))
    612 			{
    613 				buf += 2;
    614 				n -= 2;
    615 			} else
    616 			if ((n >= 1) && !strncmp(buf, "\n", 1))
    617 			{
    618 				buf++;
    619 				n--;
    620 			}
    621 
    622 			/* Now we are (possibly) at the chunk size line,
    623 			 * this is or example '7f' + newline.
    624 			 * So first, check if we have a newline at all.
    625 			 */
    626 			for (i=0; i < n; i++)
    627 			{
    628 				if (buf[i] == '\n')
    629 				{
    630 					gotlf = 1;
    631 					break;
    632 				}
    633 			}
    634 			if (!gotlf)
    635 			{
    636 				/* The line telling us the chunk size is incomplete,
    637 				 * as it does not contain an \n. Wait for more data
    638 				 * from the network socket.
    639 				 */
    640 				if (n > 0)
    641 				{
    642 					/* Store what we have first.. */
    643 					WEB(client)->lefttoparselen = n;
    644 					WEB(client)->lefttoparse = safe_alloc(n);
    645 					memcpy(WEB(client)->lefttoparse, buf, n);
    646 				}
    647 				safe_free(free_this_buffer);
    648 				return 1; /* WE WANT MORE! */
    649 			}
    650 			buf[i] = '\0'; /* cut at LF */
    651 			i++; /* point to next data */
    652 			WEB(client)->chunk_remaining = strtol(buf, NULL, 16);
    653 			if (WEB(client)->chunk_remaining < 0)
    654 			{
    655 				unreal_log(ULOG_WARNING, "webserver", "WEB_NEGATIVE_CHUNK", client,
    656 				           "Webrequest from $client: Negative chunk encountered");
    657 				safe_free(free_this_buffer);
    658 				dead_socket(client, "");
    659 				return 0;
    660 			}
    661 			if (WEB(client)->chunk_remaining == 0)
    662 			{
    663 				/* DONE! */
    664 				WEB(client)->request_body_complete = 1;
    665 				safe_free(free_this_buffer);
    666 				return 1;
    667 			}
    668 			buf += i;
    669 			n -= i;
    670 		}
    671 	}
    672 
    673 	safe_free(free_this_buffer);
    674 	return 1;
    675 }