1 /* nl-web.c --- HTTP network protocol routines for newLISP
3 Copyright (C) 2008 Lutz Mueller
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.
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.
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/>.
27 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/ioctl.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
37 #define BUFFSIZE 10240
40 #define SOCKET_ERROR -1
42 #define fgets win32_fgets
43 #define fgetc win32_fgetc
44 #define close closesocket
47 #define ERROR_BAD_URL "ERR: bad formed URL"
50 #define MAX_PROTOCOL 8
51 #define NO_FLAGS_SET 0
53 /* with MinGW gcc 3.4.5 not needed
60 int gettimeofday( struct timeval *tp, struct timezone *tzp );
64 int parseUrl(char *url
, char * protocol
, char * host
, int * port
, char * path
, size_t maxlen
);
65 void parsePath(char * url
, char * path
, size_t maxlen
);
66 size_t parseValue(char * str
);
67 void trimTrailing(char * ptr
);
68 CELL
* base64(CELL
* params
, int type
);
70 size_t Curl_base64_encode(const char *inp
, size_t insize
, char **outptr
);
71 size_t Curl_base64_decode(const char *src
, char *dest
);
74 jmp_buf socketTimeoutJump
;
76 struct timeval socketStart
;
78 /* socket send and receive routines with timeout */
79 int recvc_tm(int sock
)
85 while(wait_ready(sock
, 1000, 0) <= 0)
89 gettimeofday(&tm
, NULL
);
90 if(timediff(tm
, socketStart
) > socketTimeout
)
91 longjmp(socketTimeoutJump
, 1);
95 bytes
= recv(sock
, &chr
, 1, NO_FLAGS_SET
);
96 if(bytes
<= 0) return(-1);
102 char * recvs_tm(char * buffer
, size_t size
, int sock
)
104 ssize_t bytesReceived
= 0;
107 while(bytesReceived
< size
)
109 if((chr
= recvc_tm(sock
)) < 0)
111 if(bytesReceived
== 0)
116 *(buffer
+ bytesReceived
++) = chr
;
117 if(chr
== '\n') break;
120 *(buffer
+ bytesReceived
) = 0;
124 size_t recvsize_tm(char * buffer
, size_t size
, int sock
)
126 ssize_t sizeRead
= 0;
127 size_t resultSize
= 0;
130 wait_ready(sock
, 1000, 0);
131 memset(buffer
, 0, size
);
132 while( (sizeRead
= recv(sock
, buffer
+ resultSize
, size
, NO_FLAGS_SET
)) < size
)
136 gettimeofday(&tm
, NULL
);
137 if(timediff(tm
, socketStart
) > socketTimeout
)
138 longjmp(socketTimeoutJump
, 1);
147 resultSize
+= sizeRead
;
149 wait_ready(sock
, 1000, 0);
152 return(resultSize
+ sizeRead
);
155 ssize_t
sendf(int sock
, int debug
, char * format
, ...)
161 va_start(argptr
,format
);
162 /* new in 7201 , defined in nl-filesys.c if not in libc */
163 vasprintf(&buffer
, format
, argptr
);
165 result
= send(sock
, buffer
, strlen(buffer
), NO_FLAGS_SET
);
166 if(debug
) varPrintf(OUT_CONSOLE
, buffer
);
176 CELL
* p_getUrl(CELL
* params
)
178 return(getPutPostDeleteUrl(NULL
, params
, HTTP_GET_URL
, 0));
182 CELL
* p_putUrl(CELL
* params
)
184 return(getPutPostDeleteUrl(NULL
, params
, HTTP_PUT_URL
, 0));
188 CELL
* p_postUrl(CELL
* params
)
190 return(getPutPostDeleteUrl(NULL
, params
, HTTP_POST_URL
, 0));
193 CELL
* p_deleteUrl(CELL
* params
)
195 return(getPutPostDeleteUrl(NULL
, params
, HTTP_DELETE_URL
, 0));
201 CELL
* p_base64Enc(CELL
* params
) { return(base64(params
, BASE64_ENC
)); }
202 CELL
* p_base64Dec(CELL
* params
) { return(base64(params
, BASE64_DEC
)); }
204 CELL
* base64(CELL
* params
, int type
)
208 size_t sizein
, sizeout
;
211 getStringSize(params
, &inPtr
, &sizein
, TRUE
);
214 if(type
== BASE64_ENC
)
217 return(stuffString("===="));
218 if((sizeout
= Curl_base64_encode(inPtr
, sizein
, &outPtr
)) == 0)
219 return(stuffString(""));
221 else /* BASE64_DEC */
223 outPtr
= allocMemory((sizein
* 3) / 4 + 9);
224 sizeout
= Curl_base64_decode(inPtr
, outPtr
);
225 *(outPtr
+ sizeout
) = 0;
228 strCell
= getCell(CELL_STRING
);
229 strCell
->contents
= (UINT
)outPtr
;
230 strCell
->aux
= sizeout
+ 1;
236 CELL
* getPutPostDeleteUrl(char * url
, CELL
* params
, int type
, int timeout
)
238 char * proxyUrl
, * putPostStr
= NULL
, *contentType
;
243 char * customHeader
= NULL
;
245 int port
, pPort
, sock
= 0;
246 char * option
, * method
= NULL
;
250 char * resultPtr
= NULL
;
251 int haveContentLength
= FALSE
, headRequest
= FALSE
, listFlag
= FALSE
, debugFlag
= FALSE
;
253 ssize_t sizeRead
= 0;
254 size_t resultSize
= 0, fSize
= 0, size
= 0;
255 CELL
* result
, * cell
;
256 CELL
* headerCell
= NULL
;
264 params
= getString(params
, &url
);
266 if(type
== HTTP_PUT_URL
|| type
== HTTP_PUT_APPEND_URL
|| type
== HTTP_POST_URL
)
267 params
= getStringSize(params
, &putPostStr
, &size
, TRUE
);
269 if(type
== HTTP_POST_URL
)
271 if(params
->type
!= CELL_NIL
)
272 params
= getString(params
, &contentType
);
274 contentType
= "application/x-www-form-urlencoded";
277 result
= evaluateExpression(params
);
278 params
= params
->next
;
279 socketTimeout
= timeout
;
280 if(isNumber(result
->type
))
281 getIntegerExt(result
, (UINT
*)&socketTimeout
, FALSE
);
282 else if(isString(result
->type
))
284 option
= (char *)result
->contents
;
285 if(my_strnicmp(option
, "header", 6) == 0)
287 if(my_strnicmp(option
, "list", 5) == 0)
289 /* "debug" or "header debug" or "list-debug" options
290 print all outgoing informatiopn on the console */
291 if(my_strnicmp(option
, "debug", 5) == 0)
293 if(my_strnicmp(option
+ 7, "debug", 5) == 0)
295 if(my_strnicmp(option
+ 6, "debug", 5) == 0)
297 if(params
!= nilCell
)
298 params
= getInteger(params
, (UINT
*)&socketTimeout
);
300 else if(result
!= nilCell
)
301 return(errorProcExt(ERR_NUMBER_OR_STRING_EXPECTED
, result
));
303 /* if timeout is specified, custom-header can be specified too */
304 if(socketTimeout
&& params
!= nilCell
)
305 getString(params
, &customHeader
);
307 maxlen
= strlen(url
);
308 if(maxlen
< MAX_URL_LEN
) maxlen
= MAX_URL_LEN
;
310 protocol
= alloca(8);
311 host
= alloca(maxlen
+ 1);
312 pHost
= alloca(maxlen
+ 1);
313 path
= alloca(maxlen
+ 1);
315 /* parse URL for parameters */
316 if(parseUrl(url
, protocol
, host
, &port
, path
, maxlen
) == FALSE
)
317 return(stuffString(ERROR_BAD_URL
));
319 proxyUrl
= getenv("HTTP_PROXY");
322 strncpy(pHost
, host
, maxlen
);
327 if(parseUrl(proxyUrl
, protocol
, pHost
, &pPort
, NULL
, maxlen
) == FALSE
)
328 return(stuffString(ERROR_BAD_URL
));
332 gettimeofday(&socketStart
, NULL
);
333 /* connect to host */
338 if((sock
= netConnect(pHost
, pPort
, SOCK_STREAM
, NULL
, 3)) == SOCKET_ERROR
)
339 return(stuffString("ERR: could not connect"));
341 if(type
== HTTP_GET_URL
)
342 method
= (headRequest
== TRUE
) ? "HEAD" : "GET";
343 else if(type
== HTTP_PUT_URL
|| type
== HTTP_PUT_APPEND_URL
)
345 else if(type
== HTTP_POST_URL
)
347 else if(type
== HTTP_DELETE_URL
)
352 sendf(sock
, debugFlag
, "%s %s://%s:%d/%s HTTP/1.1\r\n", method
, protocol
, host
, port
, path
);
354 sendf(sock
, debugFlag
, "%s /%s HTTP/1.1\r\n", method
, path
);
356 /* obligatory host spec */
357 sendf(sock
, debugFlag
, "Host: %s\r\n", host
);
359 /* send optional custom header entries */
360 if (customHeader
!= NULL
)
361 sendf(sock
, debugFlag
, customHeader
);
364 sendf(sock
, debugFlag
, "User-Agent: newLISP v%d\r\n", version
);
367 sendf(sock
, debugFlag
, "Connection: close\r\n");
369 /* expanded header for PUT, POST and body */
370 if(type
== HTTP_GET_URL
|| type
== HTTP_DELETE_URL
)
372 sendf(sock
, debugFlag
, "\r\n");
374 else if(type
== HTTP_PUT_URL
|| type
== HTTP_PUT_APPEND_URL
)
376 if(type
== HTTP_PUT_APPEND_URL
) sendf(sock
, debugFlag
, "Pragma: append\r\n");
377 if(customHeader
== NULL
)
378 sendf(sock
, debugFlag
, "Content-type: text/html\r\nContent-length: %d\r\n\r\n", size
);
380 sendf(sock
, debugFlag
, "Content-length: %d\r\n\r\n", size
);
382 send(sock
, putPostStr
, size
, NO_FLAGS_SET
);
383 if(debugFlag
) varPrintf(OUT_CONSOLE
, putPostStr
);
385 else if(type
== HTTP_POST_URL
)
387 sendf(sock
, debugFlag
, "Content-type: %s\r\nContent-length: %d\r\n\r\n", contentType
, size
);
388 send(sock
, putPostStr
, size
, NO_FLAGS_SET
);
389 if(debugFlag
) varPrintf(OUT_CONSOLE
, putPostStr
);
392 if(setjmp(socketTimeoutJump
) != 0)
394 if(sock
) close(sock
);
395 if(resultPtr
!= NULL
) free(resultPtr
);
396 return(stuffString("ERR: timeout"));
399 /* Retrieve HTTP response and check for status code. */
402 if(++responseLoop
== 4)
403 return(stuffString("ERR: invalid response from server"));
405 if (recvs_tm(buff
, BUFFSIZE
, sock
) == NULL
)
406 return(stuffString("ERR: no response from server"));
408 /* go past first token */
409 for (buffPtr
= buff
; *buffPtr
!= '\0' && !isspace((int)*buffPtr
); ++buffPtr
) {;}
411 /* trim leading spaces */
412 while(isspace((int)*buffPtr
)) ++buffPtr
;
414 /* get status code */
415 statusCode
= atoi(buffPtr
);
416 /* printf("statusCode:%d\n", statusCode); */
421 /* drain and continue */
422 while (ch
= recvc_tm(sock
), ch
!= EOF
&& ch
!= '\n') {;}
438 if(strlen(buff
) > 127) buff
[127] = 0;
439 snprintf(errorTxt
, 127, "ERR: server code %d: %s", atoi(buffPtr
), buff
);
443 /* Retrieve HTTP headers. */
444 memset(buff
, 0, BUFFSIZE
);
445 if(listFlag
|| headRequest
)
446 headerCell
= stuffString("");
448 /* printf("retrieving headers\n"); */
450 /* Retrieve header */
451 while(strcmp(buff
, "\r\n") != 0 && strcmp(buff
, "\n") != 0)
453 if(recvs_tm(buff
, BUFFSIZE
, sock
) == NULL
)
454 return(stuffString("ERR: problem in header"));
456 /* printf("==>%s<==\n", buff); */
458 if(listFlag
|| headRequest
) appendCellString(headerCell
, buff
, strlen(buff
));
460 if(my_strnicmp(buff
, "content-length:", 15) == 0)
462 fSize
= parseValue(buff
+ 15);
463 haveContentLength
= TRUE
;
465 if(my_strnicmp(buff
, "location:", 9) == 0 && (statusCode
== 301 || statusCode
== 303))
468 while(isspace((int)*buffPtr
)) ++buffPtr
;
470 strncpy(path
, buffPtr
+ 1, maxlen
);
471 else /* its a url or path */
473 if(parseUrl(buffPtr
, protocol
, host
, &port
, path
, maxlen
) == FALSE
)
475 parsePath(buffPtr
, path
, buffPtr
- buff
);
479 strncpy(pHost
, host
, maxlen
);
484 if(headerCell
) deleteList(headerCell
);
485 goto CONNECT_TO_HOST
;
488 if(my_strnicmp(buff
, "Transfer-Encoding:", 18) == 0
489 && searchBuffer(buff
, strlen(buff
), "chunked", 7, 0) != 0xFFFFFFFF)
496 if((haveContentLength
== TRUE
&& fSize
== 0) || statusCode
== 204)
500 /* Retrieve HTTP body. */
501 else if(chunked
== TRUE
)
504 if(recvs_tm(buff
, BUFFSIZE
, sock
) == NULL
)
505 return(stuffString("ERR: document empty"));
506 while((size
= strtoul(buff
, NULL
, 16)) > 0)
509 resultPtr
= allocMemory(size
+ 1);
511 resultPtr
= reallocMemory(resultPtr
, resultSize
+ size
+ 1);
513 if(recvsize_tm(resultPtr
+ resultSize
, size
, sock
) != size
)
516 return(stuffString("ERR: problem in chunked format"));
519 recvs_tm(buff
, BUFFSIZE
, sock
); /* empty line */
520 recvs_tm(buff
, BUFFSIZE
, sock
); /* chunck size */
524 else if(haveContentLength
== TRUE
)
526 resultPtr
= allocMemory(fSize
+ 1);
527 if((resultSize
= recvsize_tm(resultPtr
, fSize
, sock
)) == 0)
530 return(stuffString("ERR: document empty"));
534 else /* no content length given, relies on host closing the connection */
536 resultPtr
= allocMemory(BUFFSIZE
+ 1);
539 memset(buff
, 0, BUFFSIZE
);
540 while ((sizeRead
= recvsize_tm(buff
, BUFFSIZE
- 1, sock
)) > 0)
542 if((resultSize
+ sizeRead
) > size
)
544 size
= resultSize
+ BUFFSIZE
;
545 resultPtr
= reallocMemory(resultPtr
, size
+ 1);
547 memcpy(resultPtr
+ resultSize
, buff
, sizeRead
);
548 resultSize
+= sizeRead
;
549 memset(buff
, 0, BUFFSIZE
);
554 if(resultPtr
== NULL
)
557 result
= stuffString("");
559 result
= stuffString(errorTxt
);
563 *(resultPtr
+ resultSize
) = 0;
564 result
= getCell(CELL_STRING
);
565 if(statusCode
>= 400)
567 maxlen
= strlen(errorTxt
);
568 buffPtr
= allocMemory(maxlen
+ resultSize
+ 1);
569 memcpy(buffPtr
, errorTxt
, maxlen
);
570 memcpy(buffPtr
+ maxlen
, resultPtr
, resultSize
);
573 resultSize
+= maxlen
;
575 result
->contents
= (UINT
)resultPtr
;
576 result
->aux
= resultSize
+ 1;
583 cell
= getCell(CELL_EXPRESSION
);
584 cell
->contents
= (UINT
)headerCell
;
585 headerCell
->next
= result
;
593 int parseUrl(char * url
, char * protocol
, char * host
, int * port
, char * path
, size_t maxlen
)
599 /* trim trailing whitespace like '/r/n' from url */
601 while(*(url
+ len
) <= ' ' && len
> 0)
609 if(my_strnicmp(url
, "http://", 7) == 0)
611 strncpy(protocol
,"http", MAX_PROTOCOL
);
612 strncpy(host
, url
+7, maxlen
);
614 else if( my_strnicmp(url
, "https://", 8) == 0)
616 strncpy(protocol
, "https", MAX_PROTOCOL
);
617 strncpy(host
, url
+8, maxlen
);
622 colonPtr
= strchr(host
, ':');
623 slashPtr
= strchr(host
, '/');
625 if (colonPtr
!= NULL
&& (slashPtr
== NULL
|| colonPtr
< slashPtr
))
628 *port
= atoi(colonPtr
);
631 if(path
== NULL
) return(TRUE
);
633 if (slashPtr
!= NULL
)
636 strncpy(path
, slashPtr
, maxlen
);
639 strncpy(path
, "", maxlen
);
645 void parsePath(char * url
, char * path
, size_t maxlen
)
649 /* trim trailing whitespace like '/r/n' from url */
651 while(*(url
+ len
) <= ' ' && len
> 0)
657 /* trim leading whitespace */
658 while(*url
<= ' ') url
++;
659 strncpy(path
, url
, maxlen
);
662 size_t parseValue(char * str
)
665 while(!isDigit((unsigned char)*str
) && *str
!= 0) ++str
;
667 while(isDigit((unsigned char)*str
)) ++str
;
672 /***************************************************************************
674 * Project ___| | | | _ \| |
675 * / __| | | | |_) | |
676 * | (__| |_| | _ <| |___
677 * \___|\___/|_| \_\_____|
679 * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
681 * This software is licensed as described in the file COPYING, which
682 * you should have received as part of this distribution. The terms
683 * are also available at http://curl.haxx.se/docs/copyright.html.
685 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
686 * copies of the Software, and permit persons to whom the Software is
687 * furnished to do so, under the terms of the COPYING file.
689 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
690 * KIND, either express or implied.
692 * $Id: base64.c,v 1.32 2004/12/15 01:38:25 danf Exp $
693 ***************************************************************************/
695 /* Base64 encoding/decoding
697 this file from the cURL project is included in nl-web.c for the
698 newLISP functions 'base64-enc' and 'base64-dec'
700 all #include statements have and the test harness rootines have
701 been stripped. 2005-1-6 Lutz Mueller
705 static void decodeQuantum(unsigned char *dest
, const char *src
)
709 for(i
= 0; i
< 4; i
++) {
710 if(src
[i
] >= 'A' && src
[i
] <= 'Z')
711 x
= (x
<< 6) + (unsigned int)(src
[i
] - 'A' + 0);
712 else if(src
[i
] >= 'a' && src
[i
] <= 'z')
713 x
= (x
<< 6) + (unsigned int)(src
[i
] - 'a' + 26);
714 else if(src
[i
] >= '0' && src
[i
] <= '9')
715 x
= (x
<< 6) + (unsigned int)(src
[i
] - '0' + 52);
716 else if(src
[i
] == '+')
718 else if(src
[i
] == '/')
720 else if(src
[i
] == '=')
724 dest
[2] = (unsigned char)(x
& 255);
726 dest
[1] = (unsigned char)(x
& 255);
728 dest
[0] = (unsigned char)(x
& 255);
732 * Curl_base64_decode()
734 * Given a base64 string at src, decode it into the memory pointed to by
735 * dest. Returns the length of the decoded data.
737 size_t Curl_base64_decode(const char *src
, char *dest
)
743 unsigned char lastQuantum
[3];
746 while((src
[length
] != '=') && src
[length
])
748 while(src
[length
+equalsTerm
] == '=')
751 if(equalsTerm
> 3) equalsTerm
= 3; /* LM added 2006-09-08 */
753 numQuantums
= (length
+ equalsTerm
) / 4;
755 if(numQuantums
== 0) return(0);
757 rawlen
= (numQuantums
* 3) - equalsTerm
;
759 for(i
= 0; i
< numQuantums
- 1; i
++) {
760 decodeQuantum((unsigned char *)dest
, src
);
764 decodeQuantum(lastQuantum
, src
);
765 for(i
= 0; i
< 3 - equalsTerm
; i
++)
766 dest
[i
] = lastQuantum
[i
];
771 /* ---- Base64 Encoding --- */
772 static const char table64
[]=
773 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
776 * Curl_base64_encode()
778 * Returns the length of the newly created base64 string. The third argument
779 * is a pointer to an allocated area holding the base64 data. If something
780 * went wrong, -1 is returned.
783 size_t Curl_base64_encode(const char *inp
, size_t insize
, char **outptr
)
785 unsigned char ibuf
[3];
786 unsigned char obuf
[4];
792 char *indata
= (char *)inp
;
794 *outptr
= NULL
; /* set to NULL in case of failure before we reach the end */
797 insize
= strlen(indata
);
799 base64data
= output
= (char*)malloc(insize
*4/3+4);
804 for (i
= inputparts
= 0; i
< 3; i
++) {
815 obuf
[0] = (ibuf
[0] & 0xFC) >> 2;
816 obuf
[1] = ((ibuf
[0] & 0x03) << 4) | ((ibuf
[1] & 0xF0) >> 4);
817 obuf
[2] = ((ibuf
[1] & 0x0F) << 2) | ((ibuf
[2] & 0xC0) >> 6);
818 obuf
[3] = ibuf
[2] & 0x3F;
821 case 1: /* only one byte read */
822 snprintf(output
, 5, "%c%c==",
826 case 2: /* two bytes read */
827 snprintf(output
, 5, "%c%c%c=",
833 snprintf(output
, 5, "%c%c%c%c",
843 *outptr
= base64data
; /* make it return the actual data memory */
845 return strlen(base64data
); /* return the length of the new data */
847 /* ---- End of Base64 Encoding ---- */
850 /* --------------------------- HTTP server mode -----------------------------
851 handles GET, POST, PUT and DELETE requests
852 handles queries in GET requests and sets environment variable QUERY_STRING
853 sets HTTP_HOST and HTTP_USER_AGENTT when present in client request header
854 no special encodings are supported
855 subset HTTP/1.0 compliant
858 int sendHTTPmessage(char * fmt
, char * str
);
859 void handleHTTPcgi(char * command
, char * query
);
860 size_t readHeader(char * buff
, int * pragmaFlag
);
861 ssize_t
readPayLoad(ssize_t size
, char * content
, int outFile
, char * request
);
862 int endsWith(char * str
, char * ext
);
864 void sendHTTPpage(char * content
, size_t size
, char * media
, int closeFlag
)
866 varPrintf(OUT_CONSOLE
, "HTTP/1.0 200 OK\r\nServer: newLISP v.%d (%s)\r\n", version
, ostype
);
868 if(media
!= NULL
) /* else Content-type: is provided by CGI page */
869 varPrintf(OUT_CONSOLE
, "Content-length: %d\r\nContent-type: %s\r\n\r\n", size
, media
);
871 /* size = fwrite(content, 1, size, IOchannel); */ /* does not work with xinetd on OSX */
872 size
= write(fileno(IOchannel
), content
, size
);
880 if(IOchannel
!= NULL
&& isSocketStream(IOchannel
))
882 sendall(getSocket(IOchannel
), content
, size
);
883 close(getSocket(IOchannel
));
886 varPrintf(OUT_CONSOLE
, content
);
891 #define ERR_411 "ERR:411 length required for: %s\r\n"
892 #define MAX_BUFF 1024
893 #define DEFAULT_PAGE_1 "index.html"
894 #define DEFAULT_PAGE_2 "index.cgi"
895 #define CGI_EXTENSION ".cgi"
896 #define MEDIA_TXT "text/html"
898 #define ERROR_404 "ERR:404 File not found: %s\r\n"
900 int executeHTTPrequest(char * request
, int type
)
905 char * content
= NULL
;
906 ssize_t transferred
, size
;
910 char * fileMode
= "w";
913 CELL
* result
= NULL
;
916 query
= sptr
= request
;
918 setenv("DOCUMENT_ROOT", startupDir
, 1);
921 printf("HTTP request:%s:%d:\n", request
, type
);
924 /* stuff after request */
925 while(*sptr
> ' ') ++sptr
;
927 while(*query
!= 0 && *query
!= '?') ++query
;
934 setenv("QUERY_STRING", query
, 1);
936 /* if httpd-conf is defined call it with the request
937 the httpd-conf procedure returns a string transformation
938 of request, for security stuff, remappping etc. */
940 if(lookupSymbol("httpd-conf", mainContext
) != NULL
)
942 len
= strlen(request
) + strlen(query
) + 32;
943 command
= alloca(len
);
944 snprintf(command
, len
- 1, "(httpd-conf \"%s\" \"%s\")", request
, query
);
945 result
= sysEvalString(command
, nilCell
, mainContext
);
946 if(result
->type
== CELL_STRING
)
947 request
= (char *)result
->contents
;
948 else if (isNil(result
))
950 if(IOchannel
!= NULL
)
960 /* change to base dir of request file */
961 sptr
= request
+ strlen(request
);
962 while(*sptr
!= '/' && sptr
!= request
) --sptr
;
971 if((len
= strlen(request
)) == 0)
973 if(isFile(DEFAULT_PAGE_2
) == 0)
974 request
= DEFAULT_PAGE_2
;
976 request
= DEFAULT_PAGE_1
;
977 len
= strlen(request
);
980 size
= readHeader(buff
, &pragmaFlag
);
985 if(endsWith(request
, CGI_EXTENSION
))
986 handleHTTPcgi(request
, query
);
989 if(endsWith(request
, ".jpg")) mediaType
= "image/jpeg";
990 else if(endsWith(request
, ".png")) mediaType
= "image/png";
991 else if(endsWith(request
, ".gif")) mediaType
= "image/gif";
992 else if(endsWith(request
, ".pdf")) mediaType
= "application/pdf";
993 else if(endsWith(request
, ".mp3")) mediaType
= "audio/mpeg";
994 else if(endsWith(request
, ".mov")) mediaType
= "video/quicktime";
995 else if(endsWith(request
, ".mpg")) mediaType
= "video/mpeg";
996 else mediaType
= MEDIA_TXT
;
998 if(type
== HTTP_GET_HEAD
)
1000 snprintf(buff
, MAX_BUFF
- 1,
1002 "Content-length: %lld\r\nContent-type: %s\r\n\r\n",
1003 (long long int)fileSize(request
),
1005 "Content-length: %ld\r\nContent-type: %s\r\n\r\n",
1006 (long)fileSize(request
),
1009 sendHTTPpage(buff
, strlen(buff
), NULL
, TRUE
);
1013 if((size
= readFile(request
, &content
)) == -1)
1014 sendHTTPmessage(ERROR_404
, request
);
1016 sendHTTPpage(content
, size
, mediaType
, TRUE
);
1018 if(content
) free(content
);
1023 case HTTP_DELETE_URL
:
1024 if(unlink(request
) != 0)
1025 sendHTTPmessage("ERR:500 Could not delete: %s\r\n", request
);
1027 sendHTTPmessage("File deleted: %s\r\n", request
);
1033 sendHTTPmessage(ERR_411
, request
);
1037 query
= callocMemory(size
+ 1);
1039 if(readPayLoad(size
, query
, 0, request
) == -1)
1042 sendHTTPmessage("ERR:500 cannot read header: %s\r\n", request
);
1046 handleHTTPcgi(request
, query
);
1051 if(pragmaFlag
) fileMode
= "a";
1055 sendHTTPmessage(ERR_411
, request
);
1059 if( (outFile
= openFile(request
, fileMode
, NULL
)) == (int)-1)
1061 sendHTTPmessage("ERR:500 cannot create file: %s\r\n", request
);
1065 transferred
= readPayLoad(size
, buff
, outFile
, request
);
1068 if(transferred
!= -1)
1070 snprintf(buff
, 255, "%d bytes transferred for %s\r\n", (int)transferred
, request
);
1071 sendHTTPpage(buff
, strlen(buff
), MEDIA_TXT
, TRUE
);
1080 if(result
!= NULL
) deleteList(result
);
1085 int sendHTTPmessage(char * fmt
, char * str
)
1089 snprintf(msg
, 255, fmt
, str
);
1090 sendHTTPpage(msg
, strlen(msg
), MEDIA_TXT
, TRUE
);
1095 /* remove leading white space */
1096 char * trim(char * buff
)
1099 while(*ptr
<= ' ') ptr
++;
1103 /* retrieve rest of header */
1104 size_t readHeader(char * buff
, int * pragmaFlag
)
1111 memset(buff
, 0, MAX_LINE
);
1113 setenv("HTTP_HOST", "", 1);
1114 setenv("HTTP_USER_AGENT", "", 1);
1115 setenv("HTTP_COOKIE", "", 1);
1117 while(fgets(buff
, MAX_LINE
- 1, IOchannel
) != NULL
)
1119 if(strcmp(buff
, "\r\n") == 0 || strcmp(buff
, "\n") == 0) break;
1121 /* trim trailing white space */
1122 offset
= strlen(buff
) - 1;
1123 while(offset
> 0 && *(buff
+ offset
) <= ' ')
1124 *(buff
+ offset
--) = 0;
1126 if(my_strnicmp(buff
, "content-length:", 15) == 0)
1127 size
= parseValue(buff
+ 15);
1128 if(my_strnicmp(buff
, "pragma: append", 14) == 0)
1131 /* trim leading white space */
1132 if(my_strnicmp(buff
, "Host:", 5) == 0)
1133 setenv("HTTP_HOST", trim(buff
+ 5), 1);
1134 if(my_strnicmp(buff
, "User-Agent:", 11) == 0)
1135 setenv("HTTP_USER_AGENT", trim(buff
+ 11), 1);
1136 if(my_strnicmp(buff
, "Cookie:", 7) == 0)
1137 setenv("HTTP_COOKIE", trim(buff
+ 7), 1);
1145 ssize_t
readPayLoad(ssize_t size
, char * buff
, int outFile
, char * request
)
1148 size_t offset
= 0, transferred
= 0;
1151 printf("payload size:%ld:\n", size
);
1157 bytes
= read(fileno(IOchannel
), buff
+ offset
, MAX_BUFF
);
1159 if(IOchannel
!= NULL
&& isSocketStream(IOchannel
))
1160 bytes
= recv(getSocket(IOchannel
), buff
+ offset
, MAX_BUFF
, NO_FLAGS_SET
);
1162 bytes
= read(fileno(IOchannel
), buff
+ offset
, MAX_BUFF
);
1166 printf("payload bytes:%ld:%s:\n", bytes
, buff
+ offset
);
1171 sendHTTPmessage("ERR:500 error reading data: %s\r\n", request
);
1177 if(write(outFile
, buff
+ offset
, bytes
) != bytes
)
1179 sendHTTPmessage("ERR:500 cannot create file: %s\r\n", request
);
1186 transferred
+= bytes
;
1192 return(transferred
);
1197 void handleHTTPcgi(char * request
, char * query
)
1201 char * content
= NULL
;
1204 #ifdef WIN_32_BEFORE_SETTING_BINARYMODE
1210 srandom(milliSecTime());
1213 printf("CGI request:%s:%s:\n", request
, query
);
1216 if(isFile(request
) != 0)
1218 sendHTTPmessage(ERROR_404
, request
);
1222 if(isFile("/tmp") != 0)
1224 sendHTTPmessage("ERR:500 need /tmp directory configured: %s\n", request
);
1228 size
= strlen(request
) + 64;
1229 command
= alloca(size
);
1230 snprintf(tempfile
, 30, "/tmp/nl%02x%08x%08x", (unsigned int)size
, (unsigned int)random(), (unsigned int)random());
1232 #if defined (WIN_32) || (OS2)
1233 snprintf(command
, size
- 1, "newlisp %s > %s", request
, tempfile
);
1235 snprintf(command
, size
- 1, "./%s > %s", request
, tempfile
);
1238 if((handle
= popen(command
, "w")) == NULL
)
1240 sendHTTPmessage("ERR:500 failed creating pipe: %s\n", request
);
1244 fwrite(query
, 1, strlen(query
), handle
);
1248 size
= readFile(tempfile
, &content
);
1250 sendHTTPmessage("ERR:500 cannot read output of: %s", request
);
1253 #ifdef WIN_32_BEFORE_SETTING_BINARYMODE
1254 /* replace all \r\r\n with \r\n (artefact of Win32 out-piping) */
1257 while((pos
= strstr(ptr
, "\r\r\n")))
1259 memcpy(content
+ bytes
, ptr
, pos
- ptr
);
1261 memcpy(content
+ bytes
, "\r\n", 2);
1266 memcpy(content
+ bytes
, ptr
, size
- bytes
);
1267 *(content
+ size
) = 0;
1269 sendHTTPpage(content
, size
, NULL
, TRUE
);
1273 if(content
) free(content
);
1277 int endsWith(char * str
, char * ext
)
1284 return(strncmp(str
+ size
- len
, ext
, len
) == 0);