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