1 /* nl-web.c --- HTTP network protocol routines for newLISPD
2
3 Copyright (C) 2016 Lutz Mueller
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 */
19
20 #include "newlisp.h"
21 #include <errno.h>
22 #include "protos.h"
23
24 #ifdef WINDOWS
25 #include <winsock2.h>
26 #else
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/socket.h>
30 #include <sys/ioctl.h>
31 #include <sys/wait.h>
32 #include <netdb.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #endif
36
37 #define BUFFSIZE 10240
38
39 #ifndef WINDOWS
40 #define SOCKET_ERROR -1
41 #else
42 #define fgets win_fgets
43 #define close closesocket /* for file operations on Windows use _close */
44 #endif
45
46 /* from nl-sock.c */
47 extern UINT netErrorIdx;
48 extern char * netErrorMsg[];
49
50 /* from newlisp.c */
51 extern int httpSafe;
52
53 #define OK_FILE_DELETED "file deleted"
54
55 #define MAX_PROTOCOL 8
56 #define NO_FLAGS_SET 0
57
58 char * requestMethod[] = {"GET", "HEAD", "PUT", "PUT", "POST", "DELETE"};
59
60 /* with MinGW gcc 3.4.5 not needed
61 #ifdef WINDOWS
62 struct timezone {
63 int tz_minuteswest;
64 int tz_dsttime;
65 };
66
67 int gettimeofday( struct timeval *tp, struct timezone *tzp );
68 #endif
69 */
70
71 #ifdef WINDOWS
72 extern int IOchannelIsSocketStream;
73 #endif
74
75 extern SYMBOL * transferEvent;
76
77 ssize_t readFile(char * fileName, char * * buffer);
78 int writeFile(char * fileName, char * buffer, size_t size, char * type);
79 int parseUrl(char *url, char * protocol, char * host, int * port, char * path, size_t bufflen);
80 void parsePath(char * url, char * path, size_t bufflen);
81 size_t parseValue(char * str);
82 void trimTrailing(char * ptr);
83 CELL * webError(int no, int sockno);
84 CELL * base64(CELL * params, int type);
85
86 #ifndef EMSCRIPTEN
87 jmp_buf socketTimeoutJump;
88 INT socketTimeout = 0;
89 struct timeval socketStart;
90
91 /* socket send and receive routines with timeout */
recvc_tm(int sock)92 int recvc_tm(int sock)
93 {
94 struct timeval tm;
95 unsigned char chr;
96 ssize_t bytes;
97
98 while(wait_ready(sock, 1000, 0) <= 0)
99 {
100 if(socketTimeout)
101 {
102 gettimeofday(&tm, NULL);
103 if(timediff_ms(tm, socketStart) > socketTimeout)
104 longjmp(socketTimeoutJump, 1);
105 }
106 }
107
108 bytes = recv(sock, (void *)&chr, 1, NO_FLAGS_SET);
109 if(bytes <= 0) return(-1);
110
111 return(chr);
112 }
113
114
recvs_tm(char * buffer,size_t size,int sock)115 char * recvs_tm(char * buffer, size_t size, int sock)
116 {
117 ssize_t bytesReceived = 0;
118 int chr;
119
120 while(bytesReceived < size)
121 {
122 if((chr = recvc_tm(sock)) < 0)
123 {
124 if(bytesReceived == 0)
125 return(NULL);
126 else break;
127 }
128
129 *(buffer + bytesReceived++) = chr;
130 if(chr == '\n') break;
131 }
132
133 *(buffer + bytesReceived) = 0;
134 return(buffer);
135 }
136
wait_until_read_ready(int sock)137 void wait_until_read_ready(int sock)
138 {
139 struct timeval tm;
140
141 while(wait_ready(sock, 1000, 0) <= 0)
142 {
143 if(socketTimeout)
144 {
145 gettimeofday(&tm, NULL);
146 if(timediff_ms(tm, socketStart) > socketTimeout)
147 longjmp(socketTimeoutJump, 1);
148 }
149 }
150 }
151
recvsize_tm(char * buffer,size_t size,int sock,int flag)152 size_t recvsize_tm(char * buffer, size_t size, int sock, int flag)
153 {
154 ssize_t sizeRead = 0;
155 size_t resultSize = 0;
156
157 wait_until_read_ready(sock);
158 memset(buffer, 0, size);
159 while(size)
160 {
161 sizeRead = recv(sock, buffer + resultSize, size, NO_FLAGS_SET);
162 if(sizeRead <= 0)
163 {
164 sizeRead = 0;
165 break;
166 }
167 resultSize += sizeRead;
168 size -= sizeRead;
169 if(flag && transferEvent != nilSymbol)
170 executeSymbol(transferEvent, stuffInteger(sizeRead), NULL);
171 wait_until_read_ready(sock);
172 }
173
174 return(resultSize);
175 }
176
sendf(int sock,int debug,char * format,...)177 ssize_t sendf(int sock, int debug, char * format, ...)
178 {
179 char * buffer;
180 va_list argptr;
181 int result;
182
183 va_start(argptr,format);
184 /* new in 7201 , defined in nl-filesys.c if not in libc */
185 vasprintf(&buffer, format, argptr);
186
187 result = send(sock, buffer, strlen(buffer), NO_FLAGS_SET);
188 if(debug) varPrintf(OUT_CONSOLE, "%s", buffer);
189
190 freeMemory(buffer);
191 va_end(argptr);
192
193 return(result);
194 }
195
p_getUrl(CELL * params)196 CELL * p_getUrl(CELL * params)
197 {
198 return(getPutPostDeleteUrl(NULL, params, HTTP_GET, CONNECT_TIMEOUT));
199 }
200
201
p_putUrl(CELL * params)202 CELL * p_putUrl(CELL * params)
203 {
204 return(getPutPostDeleteUrl(NULL, params, HTTP_PUT, CONNECT_TIMEOUT));
205 }
206
207
p_postUrl(CELL * params)208 CELL * p_postUrl(CELL * params)
209 {
210 return(getPutPostDeleteUrl(NULL, params, HTTP_POST, CONNECT_TIMEOUT));
211 }
212
p_deleteUrl(CELL * params)213 CELL * p_deleteUrl(CELL * params)
214 {
215 return(getPutPostDeleteUrl(NULL, params, HTTP_DELETE, CONNECT_TIMEOUT));
216 }
217
transfer(int sock,char * buff,int len)218 int transfer(int sock, char * buff, int len)
219 {
220 int bytesSend = 0, n;
221
222 while(bytesSend < len)
223 {
224 if((n = sendall(sock, buff + bytesSend,
225 (len - bytesSend) > BUFFSIZE ? BUFFSIZE : len - bytesSend) )
226 == SOCKET_ERROR)
227 return(SOCKET_ERROR);
228 bytesSend += n;
229 if(transferEvent != nilSymbol)
230 executeSymbol(transferEvent, stuffInteger(bytesSend), NULL);
231 }
232
233 return(bytesSend);
234 }
235
getPutPostDeleteUrl(char * url,CELL * params,int type,int timeout)236 CELL * getPutPostDeleteUrl(char * url, CELL * params, int type, int timeout)
237 {
238 char * proxyUrl, * putPostStr = NULL, *contentType;
239 char * protocol;
240 char * host;
241 char * pHost;
242 char * path;
243 char * customHeader = NULL;
244 size_t bufflen;
245 int port, pPort, sock = 0;
246 char * option, * method = NULL;
247 char * buff;
248 char resultTxt[128];
249 char * buffPtr;
250 char * resultPtr = NULL;
251 int haveContentLength = FALSE, headRequest = FALSE, listFlag = FALSE, debugFlag = FALSE, rawFlag = FALSE;
252 int chunked = FALSE;
253 ssize_t sizeRead = 0;
254 size_t resultSize = 0, fSize = 0, size = 0;
255 CELL * result, * cell;
256 CELL * headerCell = NULL;
257 int ch, len;
258 int responseLoop;
259 int statusCode;
260
261 buff = alloca(BUFFSIZE);
262
263 /* reset net-error */
264 netErrorIdx = 0;
265
266 /* get parameters */
267
268 if(url == NULL)
269 params = getString(params, &url);
270
271 if(type == HTTP_PUT || type == HTTP_PUT_APPEND || type == HTTP_POST)
272 params = getStringSize(params, &putPostStr, &size, TRUE);
273
274
275 if(my_strnicmp(url, "file://", 7) == 0)
276 {
277 if(type == HTTP_GET)
278 {
279 if((size = readFile(url, &buffPtr)) == -1)
280 return(webError(ERROR_FILE_OP, sock));
281 return(makeStringCell(buffPtr, size));
282 }
283 if(type == HTTP_PUT)
284 {
285 if(writeFile(url, putPostStr, size, "w") == -1)
286 return(webError(ERROR_FILE_OP, sock));
287 snprintf(resultTxt, 64, "%u bytes written", (unsigned int)size);
288 return(stuffString(resultTxt)); /* not an error */
289 }
290 if(type == HTTP_DELETE)
291 {
292 url = getLocalPath(url);
293 return(unlink(url) == 0 ? stuffString(OK_FILE_DELETED)
294 : webError(ERROR_FILE_OP, sock));
295 }
296
297 return(webError(ERROR_BAD_URL, sock));
298 }
299
300
301 if(type == HTTP_POST)
302 {
303 if(params->type != CELL_NIL)
304 params = getString(params, &contentType);
305 else
306 contentType = "application/x-www-form-urlencoded";
307 }
308
309 result = evaluateExpression(params);
310 params = params->next;
311
312 if(isNumber(result->type))
313 {
314 getIntegerExt(result, (UINT*)&socketTimeout, FALSE);
315 /* set connection timeout to total-timeout specified by user */
316 timeout = socketTimeout;
317 }
318
319 else if(result->type == CELL_STRING)
320 {
321 option = (char *)result->contents;
322 len = result->aux - 1;
323 if(searchBuffer(option, len, "header", 6, 0) != -1)
324 headRequest = TRUE;
325 if(searchBuffer(option, len, "list", 4, 0) != -1)
326 listFlag = TRUE;
327 if(searchBuffer(option, len, "debug", 5, 0) != -1)
328 debugFlag = TRUE;
329 if(searchBuffer(option, len, "raw", 3, 0) != -1)
330 rawFlag = TRUE;
331
332 if(params != nilCell)
333 params = getInteger(params, (UINT*)&socketTimeout);
334 }
335 else if(result != nilCell)
336 return(errorProcExt(ERR_NUMBER_OR_STRING_EXPECTED, result));
337
338 /* if total timeout is specified, custom-header can be specified too */
339 if(socketTimeout && params != nilCell)
340 getString(params, &customHeader);
341
342 bufflen = strlen(url) + 1;
343 if(bufflen < MAX_URL_LEN + 1) bufflen = MAX_URL_LEN + 1;
344
345 protocol = alloca(8);
346 host = alloca(bufflen);
347 pHost = alloca(bufflen);
348 path = alloca(bufflen);
349
350 /* parse URL for parameters */
351 if(parseUrl(url, protocol, host, &port, path, bufflen) == FALSE)
352 return(webError(ERROR_BAD_URL, sock));
353
354 /* printf("protocol: %s host:%s port %d path:%s\n", protocol, host, port, path); */
355
356 proxyUrl = getenv("HTTP_PROXY");
357
358 if(proxyUrl == NULL)
359 {
360 strncpy(pHost, host, bufflen);
361 pPort = port;
362 }
363 else
364 {
365 if(parseUrl(proxyUrl, protocol, pHost, &pPort, NULL, bufflen) == FALSE)
366 return(webError(ERROR_BAD_URL, sock));
367 }
368
369 /* start timer */
370 gettimeofday(&socketStart, NULL);
371 /* connect to host */
372 CONNECT_TO_HOST:
373 if(sock)
374 close(sock);
375
376
377 if((sock = netConnect(pHost, pPort, SOCK_STREAM, 0, timeout)) == SOCKET_ERROR)
378 return(webError(netErrorIdx, sock));
379
380 if(type == HTTP_GET)
381 if(headRequest == TRUE) type = HTTP_HEAD;
382 method = requestMethod[type];
383
384 /* send header */
385 if(proxyUrl != NULL)
386 sendf(sock, debugFlag, "%s %s://%s:%d/%s HTTP/1.1\r\n", method, protocol, host, port, path);
387 else
388 sendf(sock, debugFlag, "%s /%s HTTP/1.1\r\n", method, path);
389
390 /* obligatory host spec */
391 sendf(sock, debugFlag, "Host: %s\r\n", host);
392
393 /* send optional custom header entries */
394 if (customHeader != NULL)
395 sendf(sock, debugFlag, "%s", customHeader);
396 else
397 sendf(sock, debugFlag, "User-Agent: newLISP v%d\r\n", version);
398
399 sendf(sock, debugFlag, "%s", "Connection: close\r\n");
400
401 /* expanded header for PUT, POST and body */
402 if(type == HTTP_PUT || type == HTTP_PUT_APPEND)
403 {
404 if(type == HTTP_PUT_APPEND) sendf(sock, debugFlag, "%s", "Pragma: append\r\n");
405 if(customHeader == NULL)
406 sendf(sock, debugFlag, "Content-type: text/html\r\nContent-length: %d\r\n\r\n", size);
407 else
408 sendf(sock, debugFlag, "Content-length: %d\r\n\r\n", size);
409
410 if(transfer(sock, putPostStr, size) == SOCKET_ERROR)
411 return(webError(ERROR_TRANSFER, sock));
412 if(debugFlag) varPrintf(OUT_CONSOLE, "%s", putPostStr);
413 }
414 else if(type == HTTP_POST)
415 {
416 sendf(sock, debugFlag, "Content-type: %s\r\nContent-length: %d\r\n\r\n", contentType, size);
417 if(transfer(sock, putPostStr, size) == SOCKET_ERROR)
418 return(webError(ERROR_TRANSFER, sock));
419 if(debugFlag) varPrintf(OUT_CONSOLE, "%s", putPostStr);
420 }
421 else /* HTTP_GET, HTTP_DELETE */
422 sendf(sock, debugFlag, "%s", "\r\n");
423
424 if(setjmp(socketTimeoutJump) != 0)
425 {
426 if(sock) close(sock);
427 if(resultPtr != NULL) free(resultPtr);
428 return(webError(ERR_INET_TIMEOUT, sock));
429 }
430
431 /* Retrieve HTTP response and check for status code. */
432 responseLoop = 0;
433 READ_RESPONSE:
434 if(++responseLoop == 4)
435 return(webError(ERROR_INVALID_RESPONSE, sock));
436
437
438 if (recvs_tm(buff, BUFFSIZE, sock) == NULL)
439 return(webError(ERROR_NO_RESPONSE, sock));
440
441 if(debugFlag) varPrintf(OUT_CONSOLE, "\n%s\n", buff);
442
443 /* go past first token */
444 for (buffPtr = buff; *buffPtr != '\0' && !isspace((int)*buffPtr); ++buffPtr) {;}
445
446 /* trim leading spaces */
447 while(isspace((int)*buffPtr)) ++buffPtr;
448
449 /* get status code */
450 statusCode = atoi(buffPtr);
451 if(statusCode >= 400)
452 snprintf(resultTxt, 128, "ERR: %s", buff);
453 else
454 snprintf(resultTxt, 128, "%s", buff);
455
456 switch (statusCode)
457 {
458 case 0:
459 case 100:
460 /* drain and continue */
461 while (ch = recvc_tm(sock), ch != EOF && ch != '\n') {;}
462 goto READ_RESPONSE;
463 case 200:
464 case 201:
465 case 202:
466 case 203:
467 case 204:
468 case 205:
469 case 206:
470 case 300:
471 case 301:
472 case 302:
473 case 303:
474 case 307:
475 break;
476 default:
477 break;
478 }
479
480 /* Retrieve HTTP headers. */
481 memset(buff, 0, BUFFSIZE);
482 if(listFlag || headRequest)
483 headerCell = stuffString("");
484
485 /* Retrieve header */
486 while(strcmp(buff, "\r\n") != 0 && strcmp(buff, "\n") != 0)
487 {
488 if(recvs_tm(buff, BUFFSIZE, sock) == NULL)
489 return(webError(ERROR_HEADER, sock));
490
491 if(listFlag || headRequest) appendCellString(headerCell, buff, strlen(buff));
492
493 if(my_strnicmp(buff, "content-length:", 15) == 0)
494 {
495 fSize = parseValue(buff + 15);
496 haveContentLength = TRUE;
497 }
498 /* 302 contradicts standard for redirection but is common practice */
499 if(my_strnicmp(buff, "location:", 9) == 0 && !rawFlag &&
500 (statusCode == 301 || statusCode == 302 || statusCode == 303))
501 {
502 buffPtr = buff + 9;
503 while(isspace((int)*buffPtr)) ++buffPtr;
504 if(*buffPtr == '/')
505 strncpy(path, buffPtr + 1, bufflen);
506 else /* its a url or path */
507 {
508 if(parseUrl(buffPtr, protocol, host, &port, path, bufflen) == FALSE)
509 /* path only */
510 parsePath(buffPtr, path, buffPtr - buff);
511
512 if(proxyUrl == NULL)
513 {
514 strncpy(pHost, host, bufflen);
515 pPort = port;
516 }
517 }
518
519 if(headerCell) deleteList(headerCell);
520 goto CONNECT_TO_HOST;
521 }
522
523 if(my_strnicmp(buff, "Transfer-Encoding:", 18) == 0
524 && searchBuffer(buff, strlen(buff), "chunked", 7, 0) != -1)
525 chunked = TRUE;
526 }
527
528 if(headRequest)
529 return(headerCell);
530
531 /* changed in 10.6.4 should allow 0-length contents and 204 handled earlier */
532 /* if((haveContentLength == TRUE && fSize == 0) || statusCode == 204) */
533 if(statusCode == 204)
534 return(webError(ERROR_NO_CONTENT, sock));
535
536 /* Retrieve HTTP body. */
537 else if(chunked == TRUE)
538 {
539 resultPtr = NULL;
540 if(recvs_tm(buff, BUFFSIZE, sock) == NULL)
541 return(webError(ERROR_NO_CONTENT, sock));
542 while((size = strtoul(buff, NULL, 16)) > 0)
543 {
544 if(resultSize == 0)
545 resultPtr = allocMemory(size + 1);
546 else
547 resultPtr = reallocMemory(resultPtr, resultSize + size + 1);
548 if(recvsize_tm(resultPtr + resultSize, size, sock, TRUE) != size)
549 {
550 free(resultPtr);
551 return(webError(ERROR_CHUNKED_FORMAT, sock));
552 }
553 resultSize += size;
554 recvs_tm(buff, BUFFSIZE, sock); /* empty line */
555 recvs_tm(buff, BUFFSIZE, sock); /* chunck size */
556 }
557 }
558
559 else if(haveContentLength == TRUE && fSize)
560 {
561 resultPtr = allocMemory(fSize + 1);
562 if((resultSize = recvsize_tm(resultPtr, fSize, sock, TRUE)) == 0)
563 {
564 free(resultPtr);
565 return(webError(ERROR_NO_CONTENT, sock));
566 }
567 }
568
569 else /* no content length given, relies on host closing the connection */
570 {
571 resultPtr = allocMemory(BUFFSIZE + 1);
572 resultSize = 0;
573 size = BUFFSIZE;
574 while ((sizeRead = recvsize_tm(buff, BUFFSIZE, sock, FALSE)) > 0)
575 {
576 if((resultSize + sizeRead) > size)
577 {
578 size = resultSize + BUFFSIZE;
579 resultPtr = reallocMemory(resultPtr, size + 1);
580 }
581 memcpy(resultPtr + resultSize, buff, sizeRead);
582 resultSize += sizeRead;
583 if(transferEvent != nilSymbol)
584 executeSymbol(transferEvent, stuffInteger(sizeRead), NULL);
585 }
586 }
587
588
589 if(resultPtr == NULL)
590 {
591 if(statusCode < 400)
592 result = stuffString("");
593 else
594 result = stuffString(resultTxt);
595 }
596 else
597 {
598 result = getCell(CELL_STRING);
599 if(statusCode >= 400 && listFlag == FALSE)
600 {
601 bufflen = strlen(resultTxt);
602 buffPtr = allocMemory(bufflen + resultSize + 1);
603 memcpy(buffPtr, resultTxt, bufflen);
604 memcpy(buffPtr + bufflen, resultPtr, resultSize);
605 free(resultPtr);
606 resultPtr = buffPtr;
607 resultSize += bufflen;
608 }
609 *(resultPtr + resultSize) = 0;
610 result->contents = (UINT)resultPtr;
611 result->aux = resultSize + 1;
612 }
613
614 close(sock);
615
616 if(listFlag)
617 {
618 cell = getCell(CELL_EXPRESSION);
619 addList(cell, headerCell);
620 addList(cell, result);
621 addList(cell, stuffString(statusCode >= 400 ? resultTxt + 5 : resultTxt));
622 addList(cell, stuffInteger(statusCode));
623 return(cell);
624 }
625
626 return(result);
627 }
628
629
parseUrl(char * url,char * protocol,char * host,int * port,char * path,size_t bufflen)630 int parseUrl(char * url, char * protocol, char * host, int * port, char * path, size_t bufflen)
631 {
632 char * bracketPtr = NULL;
633 char * colonPtr = NULL;
634 char * slashPtr;
635 int len;
636
637 /* trim trailing whitespace like '/r/n' from url */
638 len = strlen(url);
639 while(*(url + len) <= ' ' && len > 0)
640 {
641 *(url + len) = 0;
642 len--;
643 }
644
645 *port = 80;
646
647 if(my_strnicmp(url, "http://", 7) == 0)
648 {
649 strncpy(protocol,"http", MAX_PROTOCOL );
650 if( (ADDR_FAMILY == AF_INET6) && (*(url + 7) == '[') )
651 strncpy(host, url+8, bufflen);
652 else
653 strncpy(host, url+7, bufflen);
654 }
655 else if( my_strnicmp(url, "https://", 8) == 0)
656 {
657 strncpy(protocol, "https", MAX_PROTOCOL);
658 if( (ADDR_FAMILY == AF_INET6) && (*(url + 8) == '[') )
659 strncpy(host, url+9, bufflen);
660 else
661 strncpy(host, url+8, bufflen);
662 }
663 else
664 return(FALSE);
665
666 if(ADDR_FAMILY == AF_INET6)
667 bracketPtr = strchr(host, ']');
668 else
669 colonPtr = strchr(host, ':');
670
671 slashPtr = strchr(host, '/');
672
673 if(ADDR_FAMILY == AF_INET6)
674 {
675 if (bracketPtr != NULL && (slashPtr == NULL || bracketPtr < slashPtr))
676 {
677 *bracketPtr = '\0';
678 if(*(bracketPtr + 1) == ':')
679 *port = atoi(bracketPtr + 2);
680 }
681 else
682 {
683 colonPtr = strchr(host, ':');
684 if (colonPtr != NULL && (slashPtr == NULL || colonPtr < slashPtr))
685 {
686 *colonPtr++ = '\0';
687 *port = atoi(colonPtr);
688 }
689 }
690 }
691 else
692 {
693 if (colonPtr != NULL && (slashPtr == NULL || colonPtr < slashPtr))
694 {
695 *colonPtr++ = '\0';
696 *port = atoi(colonPtr);
697 }
698 }
699
700 if(path == NULL) return(TRUE);
701
702 if (slashPtr != NULL)
703 {
704 *slashPtr++ = '\0';
705 strncpy(path, slashPtr, bufflen);
706 }
707 else
708 strncpy(path, "", bufflen);
709
710 /* printf("protocol:%s host:%s port:%d path:%s\n", protocol, host, *port, path); */
711
712 return(TRUE);
713 }
714
parsePath(char * url,char * path,size_t bufflen)715 void parsePath(char * url, char * path, size_t bufflen)
716 {
717 int len;
718
719 /* trim trailing whitespace like '/r/n' from url */
720 len = strlen(url);
721 while(*(url + len) <= ' ' && len > 0)
722 {
723 *(url + len) = 0;
724 len--;
725 }
726
727 /* trim leading whitespace */
728 while(*url <= ' ') url++;
729 strncpy(path, url, bufflen);
730 }
731
parseValue(char * str)732 size_t parseValue(char * str)
733 {
734 while(!isDigit((unsigned char)*str) && *str != 0) ++str;
735 return atol(str);
736 }
737
webError(int errorNo,int sockno)738 CELL * webError(int errorNo, int sockno)
739 {
740 char msg[64];
741
742
743 if(sockno) close(sockno);
744 netErrorIdx = errorNo;
745 snprintf(msg, 64, "ERR: %s", netErrorMsg[errorNo]);
746
747 return(stuffString(msg));
748 }
749 #endif /* ifndef EMSCRIPTEN */
750
751 /***************************************************************************
752 * _ _ ____ _
753 * Project ___| | | | _ \| |
754 * / __| | | | |_) | |
755 * | (__| |_| | _ <| |___
756 * \___|\___/|_| \_\_____|
757 *
758 * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
759 *
760 * This software is licensed as described in the file COPYING, which
761 * you should have received as part of this distribution. The terms
762 * are also available at http://curl.haxx.se/docs/copyright.html.
763 *
764 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
765 * copies of the Software, and permit persons to whom the Software is
766 * furnished to do so, under the terms of the COPYING file.
767 *
768 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
769 * KIND, either express or implied.
770 *
771 * $Id: base64.c,v 1.32 2004/12/15 01:38:25 danf Exp $
772 ***************************************************************************/
773
774 /* Base64 encoding/decoding
775
776 this file from the cURL project is included in nl-web.c for the
777 newLISP functions 'base64-enc' and 'base64-dec'
778
779 all #include statements have and the test harness rootines have
780 been stripped. 2005-1-6 Lutz Mueller
781 */
782
783
decodeQuantum(unsigned char * dest,const char * src)784 static void decodeQuantum(unsigned char *dest, const char *src)
785 {
786 unsigned int x = 0;
787 int i;
788 for(i = 0; i < 4; i++) {
789 if(src[i] >= 'A' && src[i] <= 'Z')
790 x = (x << 6) + (unsigned int)(src[i] - 'A' + 0);
791 else if(src[i] >= 'a' && src[i] <= 'z')
792 x = (x << 6) + (unsigned int)(src[i] - 'a' + 26);
793 else if(src[i] >= '0' && src[i] <= '9')
794 x = (x << 6) + (unsigned int)(src[i] - '0' + 52);
795 else if(src[i] == '+')
796 x = (x << 6) + 62;
797 else if(src[i] == '/')
798 x = (x << 6) + 63;
799 else if(src[i] == '=')
800 x = (x << 6);
801 }
802
803 dest[2] = (unsigned char)(x & 255);
804 x >>= 8;
805 dest[1] = (unsigned char)(x & 255);
806 x >>= 8;
807 dest[0] = (unsigned char)(x & 255);
808 }
809
810 /*
811 * Curl_base64_decode()
812 *
813 * Given a base64 string at src, decode it into the memory pointed to by
814 * dest. Returns the length of the decoded data.
815 */
Curl_base64_decode(const char * src,char * dest)816 size_t Curl_base64_decode(const char *src, char *dest)
817 {
818 int length = 0;
819 int equalsTerm = 0;
820 int i;
821 int numQuantums;
822 unsigned char lastQuantum[3];
823 size_t rawlen=0;
824
825 while((src[length] != '=') && src[length])
826 length++;
827 while(src[length+equalsTerm] == '=')
828 equalsTerm++;
829
830 if(equalsTerm > 3) equalsTerm = 3; /* LM added 2006-09-08 */
831
832 numQuantums = (length + equalsTerm) / 4;
833
834 if(numQuantums == 0) return(0);
835
836 rawlen = (numQuantums * 3) - equalsTerm;
837
838 for(i = 0; i < numQuantums - 1; i++) {
839 decodeQuantum((unsigned char *)dest, src);
840 dest += 3; src += 4;
841 }
842
843 decodeQuantum(lastQuantum, src);
844 for(i = 0; i < 3 - equalsTerm; i++)
845 dest[i] = lastQuantum[i];
846
847 return rawlen;
848 }
849
850 #define BASE64_ENC 0
851 #define BASE64_DEC 1
852
p_base64Enc(CELL * params)853 CELL * p_base64Enc(CELL * params) { return(base64(params, BASE64_ENC)); }
p_base64Dec(CELL * params)854 CELL * p_base64Dec(CELL * params) { return(base64(params, BASE64_DEC)); }
855
base64(CELL * params,int type)856 CELL * base64(CELL * params, int type)
857 {
858 char * inPtr;
859 char * outPtr;
860 size_t sizein, sizeout;
861 int emptyFlag = 0;
862
863 params = getStringSize(params, &inPtr, &sizein, TRUE);
864 emptyFlag = getFlag(params);
865
866 if(type == BASE64_ENC)
867 {
868 if(sizein == 0)
869 return(emptyFlag ? stuffString("") : stuffString("===="));
870 if((sizeout = Curl_base64_encode(inPtr, sizein, &outPtr)) == 0)
871 return(stuffString(""));
872 }
873 else /* BASE64_DEC */
874 {
875 outPtr = allocMemory((sizein * 3) / 4 + 9);
876 sizeout = Curl_base64_decode(inPtr, outPtr);
877 *(outPtr + sizeout) = 0;
878 }
879
880 /*
881 strCell = getCell(CELL_STRING);
882 strCell->contents = (UINT)outPtr;
883 strCell->aux = sizeout + 1;
884 return(strCell);
885 */
886
887 return(makeStringCell(outPtr, sizeout));
888 }
889
890 /* ---- Base64 Encoding --- */
891 static const char table64[]=
892 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
893
894 /*
895 * Curl_base64_encode()
896 *
897 * Returns the length of the newly created base64 string. The third argument
898 * is a pointer to an allocated area holding the base64 data. If something
899 * went wrong, -1 is returned.
900 *
901 */
Curl_base64_encode(const char * inp,size_t insize,char ** outptr)902 size_t Curl_base64_encode(const char *inp, size_t insize, char **outptr)
903 {
904 unsigned char ibuf[3];
905 unsigned char obuf[4];
906 int i;
907 int inputparts;
908 char *output;
909 char *base64data;
910
911 char *indata = (char *)inp;
912
913 *outptr = NULL; /* set to NULL in case of failure before we reach the end */
914
915 if(0 == insize)
916 insize = strlen(indata);
917
918 base64data = output = (char*)malloc(insize*4/3+4);
919 if(NULL == output)
920 return 0;
921
922 while(insize > 0) {
923 for (i = inputparts = 0; i < 3; i++) {
924 if(insize > 0) {
925 inputparts++;
926 ibuf[i] = *indata;
927 indata++;
928 insize--;
929 }
930 else
931 ibuf[i] = 0;
932 }
933
934 obuf [0] = (ibuf [0] & 0xFC) >> 2;
935 obuf [1] = ((ibuf [0] & 0x03) << 4) | ((ibuf [1] & 0xF0) >> 4);
936 obuf [2] = ((ibuf [1] & 0x0F) << 2) | ((ibuf [2] & 0xC0) >> 6);
937 obuf [3] = ibuf [2] & 0x3F;
938
939 switch(inputparts) {
940 case 1: /* only one byte read */
941 snprintf(output, 5, "%c%c==",
942 table64[obuf[0]],
943 table64[obuf[1]]);
944 break;
945 case 2: /* two bytes read */
946 snprintf(output, 5, "%c%c%c=",
947 table64[obuf[0]],
948 table64[obuf[1]],
949 table64[obuf[2]]);
950 break;
951 default:
952 snprintf(output, 5, "%c%c%c%c",
953 table64[obuf[0]],
954 table64[obuf[1]],
955 table64[obuf[2]],
956 table64[obuf[3]] );
957 break;
958 }
959 output += 4;
960 }
961 *output=0;
962 *outptr = base64data; /* make it return the actual data memory */
963
964 return strlen(base64data); /* return the length of the new data */
965 }
966 /* ---- End of Base64 Encoding ---- */
967
968
969 #ifndef EMSCRIPTEN
970 /* --------------------------- HTTP server mode -----------------------------
971 Handles GET, POST, PUT and DELETE requests
972 handles queries in GET requests and sets environment variables
973 DOCUMENT_ROOT, REQUEST_METHOD, SERVER_SOFTWARE and QUERY_STRING,
974 and when present in client request header HTTP_HOST, HTTP_USER_AGENT
975 and HTTP_COOKIE. REMOTE_ADDR is set when the client connects.
976 Subset HTTP/1.0 compliant.
977 */
978 #ifndef LIBRARY
979 /* #define DEBUGHTTP */
980 #define SERVER_SOFTWARE "newLISP/10.7.5"
981
982 int sendHTTPmessage(int status, char * description, char * request);
983 void handleHTTPcgi(char * command, char * query, ssize_t querySize);
984 size_t readHeader(char * buff, int * pragmaFlag);
985 ssize_t readPayLoad(ssize_t size, char * content, int outFile, char * request);
986 int endsWith(char * str, char * ext);
987 char * getMediaType(char * request);
988 void url_decode(char *dest, char *src);
989
sendHTTPpage(char * content,size_t size,char * media)990 void sendHTTPpage(char * content, size_t size, char * media)
991 {
992 int pos = 0;
993 char status[128];
994
995 memset(status, 0, 128);
996 if(strncmp(content, "Status:", 7) == 0)
997 {
998 /* get content after */
999 while(*(content + pos) >= 32) pos++;
1000 memcpy(status, content + 7, pos - 7);
1001 content = content + pos;
1002 if(size) size -= pos;
1003 while(*content == '\r' || *content == '\n') { content++; size--; }
1004 }
1005 else
1006 strncpy(status, "200 OK", 6);
1007
1008 varPrintf(OUT_CONSOLE, "HTTP/1.0 %s\r\n", status);
1009 varPrintf(OUT_CONSOLE, "Server: newLISP v.%d (%s)\r\n", version, OSTYPE);
1010 #ifdef DEBUGHTTP
1011 puts("# Header sent:");
1012 printf("HTTP/1.0 %s\r\n", status);
1013 printf("Server: newLISP v.%d (%s)\r\n", version, OSTYPE);
1014 #endif
1015
1016 if(media != NULL)
1017 {
1018 varPrintf(OUT_CONSOLE, "Content-length: %d\r\nContent-type: %s\r\n\r\n", size, media);
1019 #ifdef DEBUGHTTP
1020 printf("Content-length: %d\r\nContent-type: %s\r\n\r\n", (int)size, media);
1021 #endif
1022 }
1023 #ifndef WINDOWS
1024 size = write(fileno(IOchannel), content, size);
1025 fflush(IOchannel);
1026 fclose(IOchannel);
1027 IOchannel = NULL;
1028 #else /* it is WINDOWS */
1029 if(IOchannel != NULL && IOchannelIsSocketStream)
1030 {
1031 sendall(getSocket(IOchannel), content, size);
1032 close(getSocket(IOchannel));
1033 }
1034 else
1035 varPrintf(OUT_CONSOLE, "%s", content);
1036 return;
1037 #endif
1038 #ifdef DEBUGHTTP
1039 printf("# content:%s:\r\n", content);
1040 fflush(stdout);
1041 #endif
1042 }
1043
1044 #define MAX_BUFF 1024
1045 #define ERROR_404 "File or Directory not found"
1046 #define ERROR_411 "Length required for"
1047 #define ERROR_500 "Server error"
1048 #define DEFAULT_PAGE_1 "index.html"
1049 #define DEFAULT_PAGE_2 "index.cgi"
1050 #define CGI_EXTENSION ".cgi"
1051 #define MEDIA_TEXT "text/plain"
1052
1053
executeHTTPrequest(char * request,int type)1054 int executeHTTPrequest(char * request, int type)
1055 {
1056 char * sptr;
1057 char * query;
1058 char * content = NULL;
1059 char * decoded;
1060 char buff[MAX_BUFF];
1061 ssize_t transferred, size;
1062 char * mediaType;
1063 CELL * result = NULL;
1064 int outFile;
1065 int pragmaFlag;
1066 int len;
1067 char * fileMode = "w";
1068
1069 if(chdir(startupDir) < 0)
1070 fatalError(ERR_IO_ERROR, 0, 0);
1071 query = sptr = request;
1072
1073 setenv("DOCUMENT_ROOT", startupDir, 1);
1074 setenv("SERVER_SOFTWARE", SERVER_SOFTWARE, 1);
1075 setenv("REQUEST_METHOD", requestMethod[type], 1);
1076
1077 #ifdef DEBUGHTTP
1078 printf("# HTTP request:%s:%s:\r\n", request, requestMethod[type]);
1079 #endif
1080
1081 /* stuff after request */
1082 while(*sptr > ' ') ++sptr;
1083 *sptr = 0;
1084 while(*query != 0 && *query != '?') ++query;
1085 if(*query == '?')
1086 {
1087 *query = 0;
1088 query++;
1089 }
1090
1091 setenv("QUERY_STRING", query, 1);
1092
1093 /* do url_decode */
1094 decoded = alloca(strlen(request) + 1);
1095 url_decode(decoded, request);
1096 request = decoded;
1097
1098 setenv("REQUEST_URI", request, 1); /* 10.7.4 */
1099
1100 /* change to base dir of request file */
1101 sptr = request + strlen(request);
1102 while(*sptr != '/' && sptr != request) --sptr;
1103 if(*sptr == '/')
1104 {
1105 *sptr = 0;
1106 sptr++;
1107 if(chdir(request))
1108 {
1109 sendHTTPmessage(404, ERROR_404, request);
1110 return(TRUE);
1111 }
1112 request = sptr;
1113 }
1114
1115 if((len = strlen(request)) == 0)
1116 {
1117 if(isFile(DEFAULT_PAGE_2, 0) == 0)
1118 request = DEFAULT_PAGE_2;
1119 else
1120 request = DEFAULT_PAGE_1;
1121 len = strlen(request);
1122 }
1123
1124 size = readHeader(buff, &pragmaFlag);
1125 switch(type)
1126 {
1127 case HTTP_GET:
1128 case HTTP_HEAD:
1129 if(endsWith(request, CGI_EXTENSION))
1130 handleHTTPcgi(request, query, strlen(query));
1131 else
1132 {
1133 mediaType = getMediaType(request);
1134
1135 if(type == HTTP_HEAD)
1136 {
1137 snprintf(buff, MAX_BUFF - 1,
1138 "Content-length: %"PRId64"\r\nContent-type: %s\r\n\r\n",
1139 fileSize(request), mediaType);
1140 sendHTTPpage(buff, strlen(buff), NULL);
1141 }
1142 else
1143 {
1144 if((size = readFile(request, &content)) == -1)
1145 sendHTTPmessage(404, ERROR_404, request);
1146 else
1147 sendHTTPpage(content, size, mediaType);
1148 if(content) free(content);
1149 }
1150 }
1151 break;
1152
1153 case HTTP_DELETE:
1154 if(httpSafe)
1155 {
1156 sendHTTPpage("Server in safe mode", 19, MEDIA_TEXT);
1157 break;
1158 }
1159
1160 if(unlink(request) != 0)
1161 sendHTTPmessage(500, "Could not delete", request);
1162 else
1163 sendHTTPpage("File deleted", 12, MEDIA_TEXT);
1164 break;
1165
1166 case HTTP_POST:
1167 if(!size)
1168 {
1169 sendHTTPmessage(411, ERROR_411, request);
1170 break;
1171 }
1172
1173 query = callocMemory(size + 1);
1174
1175 if(readPayLoad(size, query, 0, request) == -1)
1176 {
1177 free(query);
1178 break;
1179 }
1180
1181 handleHTTPcgi(request, query, size);
1182 free(query);
1183 break;
1184
1185 case HTTP_PUT:
1186 if(httpSafe)
1187 {
1188 sendHTTPpage("Server in safe mode", 19, request);
1189 break;
1190 }
1191 if(pragmaFlag) fileMode = "a";
1192
1193 if(!size)
1194 {
1195 sendHTTPmessage(411, ERROR_411, request);
1196 break;
1197 }
1198
1199 if( (outFile = openFile(request, fileMode, NULL)) == (int)-1)
1200 {
1201 sendHTTPmessage(500, "cannot create file", request);
1202 break;
1203 }
1204
1205 transferred = readPayLoad(size, buff, outFile, request);
1206 #ifdef WINDOWS
1207 _close(outFile);
1208 #else
1209 close(outFile);
1210 #endif
1211 if(transferred != -1)
1212 {
1213 snprintf(buff, 255, "%d bytes transferred for %s\r\n", (int)transferred, request);
1214 sendHTTPpage(buff, strlen(buff), MEDIA_TEXT);
1215 }
1216 break;
1217
1218 default:
1219 break;
1220 }
1221
1222 if(chdir(startupDir) < 0) fatalError(ERR_IO_ERROR, 0, 0);
1223 if(result != NULL) deleteList(result);
1224 return(TRUE);
1225 }
1226
1227
sendHTTPmessage(int status,char * desc,char * req)1228 int sendHTTPmessage(int status, char * desc, char * req)
1229 {
1230 char msg[256];
1231
1232 snprintf(msg, 256, "Status:%d %s\r\nERR:%d %s: %s\r\n", status, desc, status, desc, req);
1233 sendHTTPpage(msg, strlen(msg), MEDIA_TEXT);
1234 return(0);
1235 }
1236
1237
1238 /* remove leading white space */
trim(char * buff)1239 char * trim(char * buff)
1240 {
1241 char * ptr = buff;
1242 while(*ptr <= ' ') ptr++;
1243 return(ptr);
1244 }
1245
1246 /* retrieve rest of header */
readHeader(char * buff,int * pragmaFlag)1247 size_t readHeader(char * buff, int * pragmaFlag)
1248 {
1249 size_t size = 0;
1250 int offset;
1251 char numStr[16];
1252
1253 *pragmaFlag = 0;
1254
1255 memset(buff, 0, MAX_LINE);
1256
1257 setenv("HTTP_HOST", "", 1);
1258 setenv("HTTP_USER_AGENT", "", 1);
1259 setenv("HTTP_COOKIE", "", 1);
1260 setenv("HTTP_AUTHORIZATION", "", 1);
1261
1262 while(fgets(buff, MAX_LINE - 1, IOchannel) != NULL)
1263 {
1264 if(strcmp(buff, "\r\n") == 0 || strcmp(buff, "\n") == 0) break;
1265
1266 /* trim trailing white space */
1267 offset = strlen(buff) - 1;
1268 while(offset > 0 && *(buff + offset) <= ' ')
1269 *(buff + offset--) = 0;
1270
1271 if(my_strnicmp(buff, "content-length:", 15) == 0)
1272 {
1273 size = parseValue(buff + 15);
1274 #if defined(WINDOWS) || defined(TRU64)
1275 snprintf(numStr, 16, "%lu", (long unsigned int)size);
1276 #else
1277 snprintf(numStr, 16, "%llu", (long long unsigned int)size);
1278 #endif
1279 setenv("CONTENT_LENGTH", numStr, 1);
1280 }
1281 if(my_strnicmp(buff, "pragma: append", 14) == 0)
1282 *pragmaFlag = TRUE;
1283
1284 /* trim leading white space */
1285 if(my_strnicmp(buff, "content-type:", 13) == 0)
1286 setenv("CONTENT_TYPE", trim(buff + 13), 1);
1287 if(my_strnicmp(buff, "Host:", 5) == 0)
1288 setenv("HTTP_HOST", trim(buff + 5), 1);
1289 if(my_strnicmp(buff, "User-Agent:", 11) == 0)
1290 setenv("HTTP_USER_AGENT", trim(buff + 11), 1);
1291 if(my_strnicmp(buff, "Cookie:", 7) == 0)
1292 setenv("HTTP_COOKIE", trim(buff + 7), 1);
1293 if(my_strnicmp(buff, "Authorization:", 14) == 0)
1294 setenv("HTTP_AUTHORIZATION", trim(buff + 14), 1);
1295 }
1296
1297
1298 return(size);
1299 }
1300
1301
readPayLoad(ssize_t size,char * buff,int outFile,char * request)1302 ssize_t readPayLoad(ssize_t size, char * buff, int outFile, char * request)
1303 {
1304 ssize_t bytes, readsize;
1305 size_t offset = 0, transferred = 0;
1306
1307 #ifdef DEBUGHTTP
1308 printf("# Payload size:%ld\r\n", (long)size);
1309 #endif
1310
1311 while(size > 0)
1312 {
1313 readsize = (size > MAX_BUFF) ? MAX_BUFF : size;
1314 #ifndef WINDOWS
1315 bytes = read(fileno(IOchannel), buff + offset, readsize);
1316 #else /* it is WINDOWS */
1317 if(IOchannel != NULL && IOchannelIsSocketStream)
1318 bytes = recv(getSocket(IOchannel), buff + offset, readsize, NO_FLAGS_SET);
1319 else
1320 bytes = read(fileno(IOchannel), buff + offset, readsize);
1321 #endif
1322
1323 #ifdef DEBUGHTTP
1324 printf("Payload bytes:%ld:%s:\r\n", (long)bytes, buff + offset);
1325 #endif
1326
1327 if(bytes <= 0)
1328 {
1329 sendHTTPmessage(500, "Problem reading data", request);
1330 return(-1);
1331 }
1332
1333 if(outFile)
1334 {
1335 if(write(outFile, buff + offset, bytes) != bytes)
1336 {
1337 sendHTTPmessage(500, "Cannot create file", request);
1338 return(-1);
1339 }
1340 }
1341 else
1342 offset += bytes;
1343
1344 transferred += bytes;
1345 size -= bytes;
1346 }
1347 #ifndef WINDOWS
1348 fflush(NULL);
1349 #endif
1350 return(transferred);
1351 }
1352
1353
1354
handleHTTPcgi(char * request,char * query,ssize_t querySize)1355 void handleHTTPcgi(char * request, char * query, ssize_t querySize)
1356 {
1357 FILE * handle;
1358 char * command;
1359 char * content = NULL;
1360 ssize_t size;
1361 char tempfile[PATH_MAX];
1362 #ifdef WINDOWS_BEFORE_SETTING_BINARYMODE
1363 char * ptr;
1364 char * pos;
1365 int bytes = 0;
1366 #endif
1367
1368 srandom(milliSecTime());
1369
1370 #ifdef DEBUGHTTP
1371 printf("# CGI request:%s:%s:\r\n", request, query);
1372 #endif
1373
1374 if(isFile(request, 0) != 0)
1375 {
1376 sendHTTPmessage(404, ERROR_404, request);
1377 return;
1378 }
1379
1380 if(isFile(tempDir, 0) != 0)
1381 {
1382 sendHTTPmessage(500, "cannot find tmp directory", request);
1383 return;
1384 }
1385
1386 size = strlen(request) + PATH_MAX;
1387 command = alloca(size);
1388 snprintf(tempfile, PATH_MAX, "%s/nl%04x-%08x-%08x",
1389 tempDir, (unsigned int)size, (unsigned int)random(), (unsigned int)random());
1390
1391 #if defined (WINDOWS) || (OS2)
1392 snprintf(command, size - 1, "newlisp \"%s\" > %s", request, tempfile);
1393 #else
1394 snprintf(command, size - 1, "./\"%s\" > %s", request, tempfile);
1395 #endif
1396
1397 if((handle = popen(command, "w")) == NULL)
1398 {
1399 sendHTTPmessage(500, "failed creating pipe", request);
1400 return;
1401 }
1402
1403 if((size = fwrite(query, 1, querySize, handle)) < 0)
1404 fatalError(ERR_IO_ERROR, 0, 0);
1405
1406 fflush(handle);
1407 pclose(handle);
1408
1409 size = readFile(tempfile, &content);
1410 if(size == -1)
1411 sendHTTPmessage(500, "cannot read output of", tempfile);
1412 else
1413 sendHTTPpage(content, size, NULL);
1414
1415 #ifdef DEBUGHTTP
1416 printf("# Temporary file: %s\n", tempfile);
1417 #else
1418 unlink(tempfile);
1419 #endif
1420
1421 if(content) free(content);
1422 }
1423
1424
endsWith(char * str,char * ext)1425 int endsWith(char * str, char * ext)
1426 {
1427 size_t size, len;
1428
1429 size = strlen(str);
1430 len = strlen(ext);
1431
1432 return(strncmp(str + size - len, ext, len) == 0);
1433 }
1434
1435
1436 typedef struct
1437 {
1438 char * extension;
1439 char * type;
1440 } T_MEDIA_TYPE;
1441
1442 /* T_ prefix added in 10.4.8 to compile on later MinGW */
1443
1444 T_MEDIA_TYPE mediaType[] = {
1445 {".avi", "video/x-msvideo"},
1446 {".css", "text/css"},
1447 {".gif", "image/gif"},
1448 {".htm", "text/html"},
1449 {".html","text/html"},
1450 {".jpg", "image/jpeg"},
1451 {".js", "application/javascript"},
1452 {".mov", "video/quicktime"},
1453 {".mp3", "audio/mpeg"},
1454 {".mpg", "video/mpeg"},
1455 {".pdf", "application/pdf"},
1456 {".png", "image/png"},
1457 {".wav", "audio/x-wav"},
1458 {".zip", "application/zip"},
1459 { NULL, NULL},
1460 };
1461
getMediaType(char * request)1462 char * getMediaType(char * request)
1463 {
1464 int i;
1465
1466 for(i = 0; mediaType[i].extension != NULL; i++)
1467 {
1468 if(endsWith(request, mediaType[i].extension))
1469 return(mediaType[i].type);
1470 }
1471
1472 return(MEDIA_TEXT);
1473 }
1474
1475
url_decode(char * dest,char * src)1476 void url_decode(char *dest, char *src)
1477 {
1478 char code[3] = {0};
1479 unsigned int ascii = 0;
1480 char *end = NULL;
1481
1482 while(*src)
1483 {
1484 if(*src == '%')
1485 {
1486 memcpy(code, ++src, 2);
1487 ascii = strtoul(code, &end, 16);
1488 *dest++ = (char)ascii;
1489 src += 2;
1490 }
1491 else
1492 *dest++ = *src++;
1493 }
1494 *dest = 0;
1495 }
1496
1497 #endif /* ifndef EMSCRIPTEN */
1498 #endif /* ifndef LIBRARY */
1499 /* eof */
1500
1501