/* nl-web.c --- HTTP network protocol routines for newLISP Copyright (C) 2007 Lutz Mueller This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "newlisp.h" #include #include "protos.h" #ifdef WIN_32 #include #else #include #include #include #include #include #include #include #include #endif #define BUFFSIZE 10240 #ifndef WIN_32 #define SOCKET_ERROR -1 #else #define fgets win32_fgets #define fgetc win32_fgetc #define close closesocket #endif #define ERROR_BAD_URL "ERR: bad formed URL" #define MAX_PROTOCOL 8 #define NO_FLAGS_SET 0 #ifdef WIN_32 struct timezone { int tz_minuteswest; int tz_dsttime; }; int gettimeofday( struct timeval *tp, struct timezone *tzp ); #endif int parseUrl(char *url, char * protocol, char * host, int * port, char * path, int maxlen); void parsePath(char * url, char * path, int maxlen); size_t parseValue(char * str); void trimTrailing(char * ptr); CELL * base64(CELL * params, int type); size_t Curl_base64_encode(const char *inp, size_t insize, char **outptr); size_t Curl_base64_decode(const char *src, char *dest); jmp_buf socketTimeoutJump; long socketTimeout; struct timeval socketStart; /* socket send and receive routines with timeout */ int recvc_tm(int sock) { struct timeval tm; char chr; ssize_t bytes; while(wait_ready(sock, 1000, 0) <= 0) { if(socketTimeout) { gettimeofday(&tm, NULL); if(timediff(tm, socketStart) > socketTimeout) longjmp(socketTimeoutJump, 1); } } bytes = recv(sock, &chr, 1, NO_FLAGS_SET); if(bytes <= 0) return(-1); return(chr); } char * recvs_tm(char * buffer, size_t size, int sock) { ssize_t bytesReceived = 0; int chr; while(bytesReceived < size) { if((chr = recvc_tm(sock)) < 0) { if(bytesReceived == 0) return(NULL); else break; } *(buffer + bytesReceived++) = chr; if(chr == '\n') break; } *(buffer + bytesReceived) = 0; return(buffer); } size_t recvsize_tm(char * buffer, size_t size, int sock) { ssize_t sizeRead = 0; size_t resultSize = 0; struct timeval tm; wait_ready(sock, 1000, 0); memset(buffer, 0, size); while( (sizeRead = recv(sock, buffer + resultSize, size, NO_FLAGS_SET)) < size) { if(socketTimeout) { gettimeofday(&tm, NULL); if(timediff(tm, socketStart) > socketTimeout) longjmp(socketTimeoutJump, 1); } if(size == 0) break; if(sizeRead <= 0) { sizeRead = 0; break; } resultSize += sizeRead; size -= sizeRead; wait_ready(sock, 1000, 0); } return(resultSize + sizeRead); } ssize_t sendf(int sock, char * format, ...) { char * buffer; va_list argptr; int result; va_start(argptr,format); /* new in 7201 , defined in nl-filesys.c if not in libc */ vasprintf(&buffer, format, argptr); result = send(sock, buffer, strlen(buffer), NO_FLAGS_SET); freeMemory(buffer); va_end(argptr); return(result); } CELL * p_getUrl(CELL * params) { return(getPutPostDeleteUrl(NULL, params, HTTP_GET_URL, 0)); } CELL * p_putUrl(CELL * params) { return(getPutPostDeleteUrl(NULL, params, HTTP_PUT_URL, 0)); } CELL * p_postUrl(CELL * params) { return(getPutPostDeleteUrl(NULL, params, HTTP_POST_URL, 0)); } CELL * p_deleteUrl(CELL * params) { return(getPutPostDeleteUrl(NULL, params, HTTP_DELETE_URL, 0)); } #define BASE64_ENC 0 #define BASE64_DEC 1 CELL * p_base64Enc(CELL * params) { return(base64(params, BASE64_ENC)); } CELL * p_base64Dec(CELL * params) { return(base64(params, BASE64_DEC)); } CELL * base64(CELL * params, int type) { char * inPtr; char * outPtr; size_t sizein, sizeout; CELL * strCell; getStringSize(params, &inPtr, &sizein, TRUE); if(type == BASE64_ENC) { if(sizein == 0) return(stuffString("====")); if((sizeout = Curl_base64_encode(inPtr, sizein, &outPtr)) == 0) return(stuffString("")); } else /* BASE64_DEC */ { outPtr = allocMemory((sizein * 3) / 4 + 9); sizeout = Curl_base64_decode(inPtr, outPtr); *(outPtr + sizeout) = 0; } strCell = getCell(CELL_STRING); strCell->contents = (UINT)outPtr; strCell->aux = sizeout + 1; return(strCell); } CELL * getPutPostDeleteUrl(char * url, CELL * params, int type, int timeout) { char * proxyUrl, * putPostStr = NULL, *contentType; char * protocol; char * host; char * pHost; char * path; char * customHeader = NULL; int maxlen; int port, pPort, sock = 0; char * option, * method = NULL; char buff[BUFFSIZE]; char errorTxt[96]; char * buffPtr; char * resultPtr = NULL; int haveContentLength = FALSE, headRequest = FALSE, listFlag = FALSE; int chunked = FALSE; ssize_t sizeRead = 0; size_t resultSize = 0, fileSize = 0, size = 0; CELL * result, * cell; CELL * headerCell = NULL; int ch; int responseLoop; /* get parameters */ if(url == NULL) params = getString(params, &url); if(type == HTTP_PUT_URL || type == HTTP_PUT_APPEND_URL || type == HTTP_POST_URL) params = getStringSize(params, &putPostStr, &size, TRUE); if(type == HTTP_POST_URL) { if(params->type != CELL_NIL) params = getString(params, &contentType); else contentType = "application/x-www-form-urlencoded"; } result = evaluateExpression(params); socketTimeout = timeout; if(isNumber(result->type)) params = getIntegerExt(result, (UINT*)&socketTimeout, FALSE); else if(isString(result->type)) { option = (char *)result->contents; if(my_strnicmp(option, "header", 6) == 0) headRequest = TRUE; if(my_strnicmp(option, "list", 5) == 0) listFlag = TRUE; if(params->next != nilCell) params = getInteger(params->next, (UINT*)&socketTimeout); } else if(result != nilCell) return(errorProcExt(ERR_NUMBER_OR_STRING_EXPECTED, params)); /* if timeout is specified, custom-header can be specified too */ if(socketTimeout && params != nilCell) getString(params, &customHeader); maxlen = strlen(url); if(maxlen < MAX_URL_LEN) maxlen = MAX_URL_LEN; protocol = alloca(8); host = alloca(maxlen + 1); pHost = alloca(maxlen + 1); path = alloca(maxlen + 1); /* parse URL for parameters */ if(parseUrl(url, protocol, host, &port, path, maxlen) == FALSE) return(stuffString(ERROR_BAD_URL)); proxyUrl = getenv("HTTP_PROXY"); if(proxyUrl == NULL) { strncpy(pHost, host, maxlen); pPort = port; } else { if(parseUrl(proxyUrl, protocol, pHost, &pPort, NULL, maxlen) == FALSE) return(stuffString(ERROR_BAD_URL)); } /* start timer */ gettimeofday(&socketStart, NULL); /* connect to host */ CONNECT_TO_HOST: if(sock) close(sock); if((sock = netConnect(pHost, pPort, SOCK_STREAM, NULL, 3)) == SOCKET_ERROR) return(stuffString("ERR: could not connect")); if(type == HTTP_GET_URL) method = (headRequest == TRUE) ? "HEAD" : "GET"; else if(type == HTTP_PUT_URL || type == HTTP_PUT_APPEND_URL) method = "PUT"; else if(type == HTTP_POST_URL) method = "POST"; else if(type == HTTP_DELETE_URL) method = "DELETE"; /* send header */ if(proxyUrl != NULL) sendf(sock, "%s %s://%s:%d/%s HTTP/1.1\r\n", method, protocol, host, port, path); else sendf(sock, "%s /%s HTTP/1.1\r\n", method, path); /* obligatory host spec */ sendf(sock, "Host: %s\r\n", host); /* send optional custom header entries */ if (customHeader != NULL) sendf(sock, customHeader); else sendf(sock, "User-Agent: newLISP v%d\r\n", version); sendf(sock, "Connection: close\r\n"); /* expanded header for PUT, POST and body */ if(type == HTTP_GET_URL || type == HTTP_DELETE_URL) { sendf(sock, "\r\n"); #ifdef DEBUG varPrintf(OUT_CONSOLE, "\r\n"); #endif } else if(type == HTTP_PUT_URL || type == HTTP_PUT_APPEND_URL) { if(type == HTTP_PUT_APPEND_URL) sendf(sock, "Pragma: append\r\n"); sendf(sock, "Content-type: text/html\r\nContent-length: %d\r\n\r\n", size); send(sock, putPostStr, size, NO_FLAGS_SET); #ifdef DEBUG varPrintf(OUT_CONSOLE, "Content-type: text/html\r\nContent-length: %d\r\n\r\n", size); varPrintf(OUT_CONSOLE, putPostStr); #endif } else if(type == HTTP_POST_URL) { sendf(sock, "Content-type: %s\r\nContent-length: %d\r\n\r\n", contentType, size); send(sock, putPostStr, size, NO_FLAGS_SET); #ifdef DEBUG varPrintf(OUT_CONSOLE, "Content-type: %s\r\nContent-length: %d\r\n\r\n", contentType, size); varPrintf(OUT_CONSOLE, putPostStr); #endif } if(setjmp(socketTimeoutJump) != 0) { if(sock) close(sock); if(resultPtr != NULL) free(resultPtr); return(stuffString("ERR: timeout")); } /* Retrieve HTTP response and check for status code. */ responseLoop = 0; READ_RESPONSE: if(++responseLoop == 4) return(stuffString("ERR: invalid response from server")); if (recvs_tm(buff, BUFFSIZE, sock) == NULL) return(stuffString("ERR: no response from server")); /* if(debug) printf(buff); */ /* go past first token */ for (buffPtr = buff; *buffPtr != '\0' && !isspace((int)*buffPtr); ++buffPtr) {;} /* trim leading spaces */ while(isspace((int)*buffPtr)) ++buffPtr; /* get status code */ switch (atoi(buffPtr)) { case 0: case 100: /* drain and continue */ while (ch = recvc_tm(sock), ch != EOF && ch != '\n') {;} goto READ_RESPONSE; case 200: case 201: case 205: case 206: case 300: case 301: case 302: case 303: case 307: break; default: if(strlen(buff) > 64) buff[64] = 0; snprintf(errorTxt, 94, "ERR: server code %d: %s", atoi(buffPtr), buff); return(stuffString(errorTxt)); } /* Retrieve HTTP headers. */ memset(buff, 0, BUFFSIZE); if(listFlag || headRequest) headerCell = stuffString(""); /* Retrieve header */ while(strcmp(buff, "\r\n") != 0 && strcmp(buff, "\n") != 0) { if(recvs_tm(buff, BUFFSIZE, sock) == NULL) return(stuffString("ERR: problem in header")); /* if(debugFlag) printf(buff); */ if(listFlag || headRequest) appendCellString(headerCell, buff, strlen(buff)); if(my_strnicmp(buff, "content-length:", 15) == 0) { fileSize = parseValue(buff + 15); haveContentLength = TRUE; } if(my_strnicmp(buff, "location:", 9) == 0) { buffPtr = buff + 9; while(isspace((int)*buffPtr)) ++buffPtr; if(*buffPtr == '/') strncpy(path, buffPtr + 1, maxlen); else /* its a url or path */ { if(parseUrl(buffPtr, protocol, host, &port, path, maxlen) == FALSE) /* path only */ parsePath(buffPtr, path, buffPtr - buff); if(proxyUrl == NULL) { strncpy(pHost, host, maxlen); pPort = port; } } if(headerCell) deleteList(headerCell); goto CONNECT_TO_HOST; } if(my_strnicmp(buff, "Transfer-Encoding:", 18) == 0 && searchBuffer(buff, strlen(buff), "chunked", 7, 0) != 0xFFFFFFFF) chunked = TRUE; } if(headRequest) return(headerCell); /* Retrieve HTTP body. */ if(chunked == TRUE) { resultPtr = NULL; if(recvs_tm(buff, BUFFSIZE, sock) == NULL) return(stuffString("ERR: document empty")); while((size = strtoul(buff, NULL, 16)) > 0) { if(resultSize == 0) resultPtr = allocMemory(size + 1); else resultPtr = reallocMemory(resultPtr, resultSize + size + 1); if(recvsize_tm(resultPtr + resultSize, size, sock) != size) { free(resultPtr); return(stuffString("ERR: problem in chunked format")); } resultSize += size; recvs_tm(buff, BUFFSIZE, sock); /* empty line */ recvs_tm(buff, BUFFSIZE, sock); /* chunck size */ } } else if(haveContentLength == TRUE) { resultPtr = allocMemory(fileSize + 1); if(fileSize == 0 || (resultSize = recvsize_tm(resultPtr, fileSize, sock)) == 0) { free(resultPtr); return(stuffString("ERR: document empty")); } } else /* no content length given, relies on host closing the connection */ { resultPtr = allocMemory(BUFFSIZE + 1); resultSize = 0; size = BUFFSIZE; memset(buff, 0, BUFFSIZE); while ((sizeRead = recvsize_tm(buff, BUFFSIZE - 1, sock)) > 0) { if((resultSize + sizeRead) > size) { size = resultSize + BUFFSIZE; resultPtr = reallocMemory(resultPtr, size + 1); } memcpy(resultPtr + resultSize, buff, sizeRead); resultSize += sizeRead; memset(buff, 0, BUFFSIZE); } } if(resultPtr == NULL) result = stuffString(""); else { *(resultPtr + resultSize) = 0; result = getCell(CELL_STRING); result->contents = (UINT)resultPtr; result->aux = resultSize + 1; } close(sock); if(listFlag) { cell = getCell(CELL_EXPRESSION); cell->contents = (UINT)headerCell; headerCell->next = result; return(cell); } return(result); } int parseUrl(char * url, char * protocol, char * host, int * port, char * path, int maxlen) { char * colonPtr; char * slashPtr; int len; /* trim trailing whitespace like '/r/n' from url */ len = strlen(url); while(*(url + len) <= ' ' && len > 0) { *(url + len) = 0; len--; } *port = 80; if(my_strnicmp(url, "http://", 7) == 0) { strncpy(protocol,"http", MAX_PROTOCOL ); strncpy(host, url+7, maxlen); } else if( my_strnicmp(url, "https://", 8) == 0) { strncpy(protocol, "https", MAX_PROTOCOL); strncpy(host, url+8, maxlen); } else return(FALSE); colonPtr = strchr(host, ':'); slashPtr = strchr(host, '/'); if (colonPtr != NULL && (slashPtr == NULL || colonPtr < slashPtr)) { *colonPtr++ = '\0'; *port = atoi(colonPtr); } if(path == NULL) return(TRUE); if (slashPtr != NULL) { *slashPtr++ = '\0'; strncpy(path, slashPtr, maxlen); } else strncpy(path, "", maxlen); return(TRUE); } void parsePath(char * url, char * path, int maxlen) { int len; /* trim trailing whitespace like '/r/n' from url */ len = strlen(url); while(*(url + len) <= ' ' && len > 0) { *(url + len) = 0; len--; } /* trim leading whitespace */ while(*url <= ' ') url++; strncpy(path, url, maxlen); } size_t parseValue(char * str) { char * number; while(!isDigit((unsigned char)*str) && *str != 0) ++str; number = str; while(isDigit((unsigned char)*str)) ++str; return atol(number); } /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * $Id: base64.c,v 1.32 2004/12/15 01:38:25 danf Exp $ ***************************************************************************/ /* Base64 encoding/decoding this file from the cURL project is included in nl-web.c for the newLISP functions 'base64-enc' and 'base64-dec' all #include statements have and the test harness rootines have been stripped. 2005-1-6 Lutz Mueller */ static void decodeQuantum(unsigned char *dest, const char *src) { unsigned int x = 0; int i; for(i = 0; i < 4; i++) { if(src[i] >= 'A' && src[i] <= 'Z') x = (x << 6) + (unsigned int)(src[i] - 'A' + 0); else if(src[i] >= 'a' && src[i] <= 'z') x = (x << 6) + (unsigned int)(src[i] - 'a' + 26); else if(src[i] >= '0' && src[i] <= '9') x = (x << 6) + (unsigned int)(src[i] - '0' + 52); else if(src[i] == '+') x = (x << 6) + 62; else if(src[i] == '/') x = (x << 6) + 63; else if(src[i] == '=') x = (x << 6); } dest[2] = (unsigned char)(x & 255); x >>= 8; dest[1] = (unsigned char)(x & 255); x >>= 8; dest[0] = (unsigned char)(x & 255); } /* * Curl_base64_decode() * * Given a base64 string at src, decode it into the memory pointed to by * dest. Returns the length of the decoded data. */ size_t Curl_base64_decode(const char *src, char *dest) { int length = 0; int equalsTerm = 0; int i; int numQuantums; unsigned char lastQuantum[3]; size_t rawlen=0; while((src[length] != '=') && src[length]) length++; while(src[length+equalsTerm] == '=') equalsTerm++; if(equalsTerm > 3) equalsTerm = 3; /* LM added 2006-09-08 */ numQuantums = (length + equalsTerm) / 4; if(numQuantums == 0) return(0); rawlen = (numQuantums * 3) - equalsTerm; for(i = 0; i < numQuantums - 1; i++) { decodeQuantum((unsigned char *)dest, src); dest += 3; src += 4; } decodeQuantum(lastQuantum, src); for(i = 0; i < 3 - equalsTerm; i++) dest[i] = lastQuantum[i]; return rawlen; } /* ---- Base64 Encoding --- */ static const char table64[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* * Curl_base64_encode() * * Returns the length of the newly created base64 string. The third argument * is a pointer to an allocated area holding the base64 data. If something * went wrong, -1 is returned. * */ size_t Curl_base64_encode(const char *inp, size_t insize, char **outptr) { unsigned char ibuf[3]; unsigned char obuf[4]; int i; int inputparts; char *output; char *base64data; char *indata = (char *)inp; *outptr = NULL; /* set to NULL in case of failure before we reach the end */ if(0 == insize) insize = strlen(indata); base64data = output = (char*)malloc(insize*4/3+4); if(NULL == output) return 0; while(insize > 0) { for (i = inputparts = 0; i < 3; i++) { if(insize > 0) { inputparts++; ibuf[i] = *indata; indata++; insize--; } else ibuf[i] = 0; } obuf [0] = (ibuf [0] & 0xFC) >> 2; obuf [1] = ((ibuf [0] & 0x03) << 4) | ((ibuf [1] & 0xF0) >> 4); obuf [2] = ((ibuf [1] & 0x0F) << 2) | ((ibuf [2] & 0xC0) >> 6); obuf [3] = ibuf [2] & 0x3F; switch(inputparts) { case 1: /* only one byte read */ snprintf(output, 5, "%c%c==", table64[obuf[0]], table64[obuf[1]]); break; case 2: /* two bytes read */ snprintf(output, 5, "%c%c%c=", table64[obuf[0]], table64[obuf[1]], table64[obuf[2]]); break; default: snprintf(output, 5, "%c%c%c%c", table64[obuf[0]], table64[obuf[1]], table64[obuf[2]], table64[obuf[3]] ); break; } output += 4; } *output=0; *outptr = base64data; /* make it return the actual data memory */ return strlen(base64data); /* return the length of the new data */ } /* ---- End of Base64 Encoding ---- */ /* --------------------------- HTTP server mode ----------------------------- handles GET, POST, PUT and DELETE requests handles queries in GET requests and sets environment variable QUERY_STRING sets HTTP_HOST and HTTP_USER_AGENTT when present in client request header no special encodings are supported subset HTTP/1.0 compliant */ int sendHTTPmessage(char * fmt, char * str); void handleHTTPcgi(char * command, char * query); size_t readHeader(char * buff, int * pragmaFlag); ssize_t readPayLoad(size_t size, char * content, int outFile, char * request); int endsWith(char * str, char * ext); void sendHTTPpage(char * content, size_t size, char * media, int closeFlag) { varPrintf(OUT_CONSOLE, "HTTP/1.0 200 OK\r\nServer: newLISP v.%d (%s)\r\n", version, ostype); if(media != NULL) /* else Content-type: is provided by CGI page */ varPrintf(OUT_CONSOLE, "Content-length: %d\r\nContent-type: %s\r\n\r\n", size, media); #ifndef WIN_32 /* size = fwrite(content, 1, size, IOchannel); */ /* does not work with xinetd on OSX */ size = write(fileno(IOchannel), content, size); fflush(IOchannel); if(closeFlag) { fclose(IOchannel); IOchannel = NULL; } #else if(IOchannel != NULL && isSocketStream(IOchannel)) { sendall(getSocket(IOchannel), content, size); close(getSocket(IOchannel)); } else varPrintf(OUT_CONSOLE, content); return; #endif } #define ERR_411 "ERR:411 length required for: %s\r\n" #define MAX_BUFF 1024 #define DEFAULT_PAGE_1 "index.html" #define DEFAULT_PAGE_2 "index.cgi" #define CGI_EXTENSION ".cgi" #define MEDIA_TXT "text/html" #define ERROR_404 "ERR:404 File not found: %s\r\n" int executeHTTPrequest(char * request, int type) { char * sptr; char * query; int len; char * content = NULL; ssize_t transferred, size; char buff[MAX_BUFF]; int outFile; int pragmaFlag; char * fileMode = "w"; char * mediaType; char * command; CELL * result = NULL; chdir(startupDir); query = sptr = request; setenv("DOCUMENT_ROOT", startupDir, 1); /* stuff after request */ while(*sptr > ' ') ++sptr; *sptr = 0; while(*query != 0 && *query != '?') ++query; if(*query == '?') { *query = 0; query++; } setenv("QUERY_STRING", query, 1); /* if httpd-conf is defined call it with the request the httpd-conf procedure returns a string transformation of request, for security stuff, remappping etc. */ if(lookupSymbol("httpd-conf", mainContext) != NULL) { len = strlen(request) + strlen(query) + 32; command = alloca(len); snprintf(command, len - 1, "(httpd-conf \"%s\" \"%s\")", request, query); result = sysEvalString(command, nilCell, mainContext); if(result->type == CELL_STRING) request = (char *)result->contents; else if (isNil(result)) { if(IOchannel != NULL) { deleteList(result); fclose(IOchannel); IOchannel = NULL; } return(TRUE); } } /* change to base dir of request file */ sptr = request + strlen(request); while(*sptr != '/' && sptr != request) --sptr; if(*sptr == '/') { *sptr = 0; sptr++; chdir(request); request = sptr; } if((len = strlen(request)) == 0) { if(isFile(DEFAULT_PAGE_2) == 0) request = DEFAULT_PAGE_2; else request = DEFAULT_PAGE_1; len = strlen(request); } size = readHeader(buff, &pragmaFlag); switch(type) { case HTTP_GET_HEAD: case HTTP_GET_URL: if(endsWith(request, CGI_EXTENSION)) handleHTTPcgi(request, query); else { if(endsWith(request, ".jpg")) mediaType = "image/jpeg"; else if(endsWith(request, ".png")) mediaType = "image/png"; else if(endsWith(request, ".gif")) mediaType = "image/gif"; else if(endsWith(request, ".pdf")) mediaType = "application/pdf"; else if(endsWith(request, ".mp3")) mediaType = "audio/mpeg"; else if(endsWith(request, ".mov")) mediaType = "video/quicktime"; else if(endsWith(request, ".mpg")) mediaType = "video/mpeg"; else mediaType = MEDIA_TXT; if(type == HTTP_GET_HEAD) { snprintf(buff, MAX_BUFF - 1, #ifndef WIN_32 "Content-length: %lld\r\nContent-type: %s\r\n\r\n", (long long int)fileSize(request), #else "Content-length: %ld\r\nContent-type: %s\r\n\r\n", (long)fileSize(request), #endif mediaType); sendHTTPpage(buff, strlen(buff), NULL, TRUE); } else { if((size = readFile(request, &content)) == -1) sendHTTPmessage(ERROR_404, request); else sendHTTPpage(content, size, mediaType, TRUE); if(content) free(content); } } break; case HTTP_DELETE_URL: if(unlink(request) != 0) sendHTTPmessage("ERR:500 Could not delete: %s\r\n", request); else sendHTTPmessage("File deleted: %s\r\n", request); break; case HTTP_POST_URL: if(!size) return(sendHTTPmessage(ERR_411, request)); query = callocMemory(size + 1); if(readPayLoad(size, query, 0, request) != -1) handleHTTPcgi(request, query); free(query); break; case HTTP_PUT_URL: if(pragmaFlag) fileMode = "a"; if(!size) return(sendHTTPmessage(ERR_411, request)); if( (outFile = openFile(request, fileMode, NULL)) == (int)-1) return(sendHTTPmessage("ERR:500 cannot create file: %s\r\n", request)); transferred = readPayLoad(size, buff, outFile, request); close(outFile); if(transferred != -1) { snprintf(buff, 255, "%d bytes transferred for %s\r\n", (int)transferred, request); sendHTTPpage(buff, strlen(buff), MEDIA_TXT, TRUE); } break; default: break; } if(result != NULL) deleteList(result); return(TRUE); } int sendHTTPmessage(char * fmt, char * str) { char msg[256]; snprintf(msg, 255, fmt, str); sendHTTPpage(msg, strlen(msg), MEDIA_TXT, TRUE); return(0); } /* remove leading white space */ char * trim(char * buff) { char * ptr = buff; while(*ptr <= ' ') ptr++; return(ptr); } /* retrieve rest of header */ size_t readHeader(char * buff, int * pragmaFlag) { size_t size = 0; int offset; *pragmaFlag = 0; memset(buff, 0, MAX_LINE); setenv("HTTP_HOST", "", 1); setenv("HTTP_USER_AGENT", "", 1); setenv("HTTP_COOKIE", "", 1); while(fgets(buff, MAX_LINE - 1, IOchannel) != NULL) { if(strcmp(buff, "\r\n") == 0 || strcmp(buff, "\n") == 0) break; /* trim trailing white space */ offset = strlen(buff) - 1; while(offset > 0 && *(buff + offset) <= ' ') *(buff + offset--) = 0; if(my_strnicmp(buff, "content-length:", 15) == 0) size = parseValue(buff + 15); if(my_strnicmp(buff, "pragma: append", 14) == 0) *pragmaFlag = TRUE; /* trim leading white space */ if(my_strnicmp(buff, "Host:", 5) == 0) setenv("HTTP_HOST", trim(buff + 5), 1); if(my_strnicmp(buff, "User-Agent:", 11) == 0) setenv("HTTP_USER_AGENT", trim(buff + 11), 1); if(my_strnicmp(buff, "Cookie:", 7) == 0) setenv("HTTP_COOKIE", trim(buff + 7), 1); } return(size); } ssize_t readPayLoad(size_t size, char * buff, int outFile, char * request) { ssize_t bytes; size_t offset = 0, transferred = 0; while(size > 0) { #ifndef WIN_32 bytes = read(fileno(IOchannel), buff + offset, MAX_BUFF); #else if(IOchannel != NULL && isSocketStream(IOchannel)) bytes = recv(getSocket(IOchannel), buff + offset, MAX_BUFF, NO_FLAGS_SET); else bytes = read(fileno(IOchannel), buff + offset, MAX_BUFF); #endif if(bytes <= 0) { sendHTTPmessage("ERR:500 error reading data: %s\r\n", request); return(-1); } if(outFile) { if(write(outFile, buff + offset, bytes) != bytes) { sendHTTPmessage("ERR:500 cannot create file: %s\r\n", request); return(-1); } } else offset += bytes; transferred += bytes; size -= bytes; } #ifndef WIN_32 fflush(NULL); #endif return(transferred); } void handleHTTPcgi(char * request, char * query) { FILE * handle; char * command; char * content = NULL; size_t size; char tempfile[32]; #ifdef WIN_32 char * ptr; char * pos; int bytes = 0; #endif srandom(milliSecTime()); if(isFile(request) != 0) { sendHTTPmessage(ERROR_404, request); return; } if(isFile("/tmp") != 0) { sendHTTPmessage("ERR:500 need /tmp directory configured: %s\n", request); return; } size = strlen(request) + 64; command = alloca(size); snprintf(tempfile, 30, "/tmp/nl%02x%08x%08x", (unsigned int)size, (unsigned int)random(), (unsigned int)random()); #ifndef WIN_32 #ifndef OS2 snprintf(command, size - 1, "./%s > %s", request, tempfile); #endif #else snprintf(command, size - 1, "newlisp %s > %s", request, tempfile); #endif if((handle = popen(command, "w")) == NULL) { sendHTTPmessage("ERR:500 failed creating pipe: %s\n", request); return; } fwrite(query, 1, strlen(query), handle); fflush(handle); pclose(handle); size = readFile(tempfile, &content); if(size == -1) sendHTTPmessage("ERR:500 cannot read output of: %s", request); else { #ifdef WIN_32 /* replace all \r\r\n with \r\n (artefact of Win32 out-piping) */ ptr = content; bytes = 0; while((pos = strstr(ptr, "\r\r\n"))) { memcpy(content + bytes, ptr, pos - ptr); bytes += pos - ptr; memcpy(content + bytes, "\r\n", 2); bytes += 2; ptr = pos + 3 ; --size; } memcpy(content + bytes, ptr, size - bytes); *(content + size) = 0; #endif sendHTTPpage(content, size, NULL, TRUE); } unlink(tempfile); if(content) free(content); } int endsWith(char * str, char * ext) { size_t size, len; size = strlen(str); len = strlen(ext); return(strncmp(str + size - len, ext, len) == 0); } /* eof */