ports/58119: New Port: www/pserv (pico webserver)

Alex Dupre sysadmin at alexdupre.com
Thu Oct 16 13:00:38 UTC 2003


>Number:         58119
>Category:       ports
>Synopsis:       New Port: www/pserv (pico webserver)
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-ports-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Thu Oct 16 06:00:34 PDT 2003
>Closed-Date:
>Last-Modified:
>Originator:     Alex Dupre
>Release:        FreeBSD 4.9-ALEXDUPRE i386
>Organization:
>Environment:
System: FreeBSD 4.9-ALEXDUPRE i386
>Description:
New pserv port.

pServ is a small, portable HTTP server. It is written in pure C for speed and
portability. It runs as a standalone program and does not require inetd.
It should be small enough to be used in a mobile computer or to be run on your
obsolete workstation you have somewhere.

This port contains a patched version to support the execution of php scripts.

WWW: http://sourceforge.net/projects/pserv/

>How-To-Repeat:
>Fix:

--- pserv.sh begins here ---
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	pserv
#	pserv/files
#	pserv/files/patch-main.c
#	pserv/files/patch-Makefile
#	pserv/files/patch-main.h
#	pserv/files/patch-mime_types.dat
#	pserv/files/patch-pserv.conf
#	pserv/files/patch-handlers.c
#	pserv/files/patch-handlers.h
#	pserv/files/patch-mime.c
#	pserv/files/pserv.sh
#	pserv/Makefile
#	pserv/distinfo
#	pserv/pkg-descr
#	pserv/pkg-plist
#
echo c - pserv
mkdir -p pserv > /dev/null 2>&1
echo c - pserv/files
mkdir -p pserv/files > /dev/null 2>&1
echo x - pserv/files/patch-main.c
sed 's/^X//' >pserv/files/patch-main.c << 'END-of-pserv/files/patch-main.c'
X--- main.c.orig	Mon Sep 22 10:39:24 2003
X+++ main.c	Thu Oct 16 14:00:02 2003
X@@ -23,6 +23,7 @@
X char defaultFileName[MAX_PATH_LEN+1];
X char logFileName[MAX_PATH_LEN+1];
X char mimeTypesFileName[MAX_PATH_LEN+1];
X+char phpFileName[MAX_PATH_LEN+1];
X char cgiRoot[MAX_PATH_LEN+1]; /* root for CGI scripts exec */
X struct timeval sockTimeVal;
X mimeData *mimeArray; /* here we will hold all MIME data, inited once, never to be changed */
X@@ -206,10 +207,10 @@
X     int reqSize;
X     int readLines;
X     int tokenEnd;
X-   
X-    /* we copy the header lines to an array for easier parsing */ 
X+
X+    /* we copy the header lines to an array for easier parsing */
X     /* but first we make sure that our string has a newline and an end */
X-    req[BUFFER_SIZE] = '\0';    
X+    req[BUFFER_SIZE] = '\0';
X     reqSize = strlen(req);
X     req[reqSize] = '\n';
X     reqSize++;
X@@ -230,7 +231,7 @@
X     for (k = 0; k < readLines; k++)
X         printf("%d - |%s|\n", k, reqArray[k]);
X #endif
X-    
X+
X     /* first line: method, path and protocol version */
X     /* we copy to a temporary buffer to be more secure against overflows */
X     i = j = 0;
X@@ -246,7 +247,7 @@
X     else
X         tokenEnd = NO;
X     i++;
X-    
X+
X     /* we look for the document address */
X     j = 0;
X     reqStruct->documentAddress[0] = '\0';
X@@ -259,14 +260,14 @@
X         else
X             token[j] = '\0';      /* to make sure we have a string */
X         /* now we need to convert some escapings from the path like %20 */
X-	convertPercents(token, j);
X+        convertPercents(token, j);
X         strcpy(reqStruct->documentAddress, token);  /* copy back */
X         if (reqArray[0][i] == '\0')
X             tokenEnd = YES;
X         else
X             tokenEnd = NO;
X         i++;
X-    
X+
X         /* we need now to separate path from query string ("?" separated) */
X         if (reqArray[0][i-1] == '?')
X         {
X@@ -282,7 +283,7 @@
X             i++;
X         }
X     }
X-    
X+
X     /* we analyze the HTTP protocol version */
X     /* default is 0.9 since that version didn't report itself */
X     strcpy(reqStruct->protocolVersion, "HTTP/0.9");
X@@ -306,10 +307,13 @@
X     else if (!strncmp(reqArray[1], "Connection: Keep-Alive", strlen("Connection: keep-alive")))
X         reqStruct->keepAlive = YES;
X 
X-    /* user-agent, content-length and else */
X+    /* user-agent, content-length, content-type, cookie and else */
X     i = 1;
X     j = NO;
X     reqStruct->userAgent[0] = '\0';
X+    reqStruct->contentLength = -1;
X+    reqStruct->contentType[0] = '\0';
X+    reqStruct->cookie[0] = '\0';
X     while (i < readLines)
X     {
X         if (!strncmp(reqArray[i], "User-Agent:", strlen("User-Agent:")))
X@@ -317,14 +321,28 @@
X             strncpy(reqStruct->userAgent, &reqArray[i][strlen("User-Agent: ")], USER_AGENT_LEN - 1);
X             reqStruct->userAgent[USER_AGENT_LEN] = '\0';
X         }
X-	else if (!strncmp(reqArray[i], "Content-Length:", strlen("Content-length:")) || !strncmp(reqArray[i], "Content-length:", strlen("Content-length:")))
X-    	{
X-	    strcpy(token, &reqArray[i][strlen("Content-length: ")]);
X-	    sscanf(token, "%ld", &(reqStruct->contentLength));
X+        else if (!strncmp(reqArray[i], "Content-Length:", strlen("Content-length:")) || !strncmp(reqArray[i], "Content-length:", strlen("Content-length:")))
X+        {
X+            strcpy(token, &reqArray[i][strlen("Content-length: ")]);
X+            sscanf(token, "%ld", &(reqStruct->contentLength));
X #ifdef PRINTF_DEBUG
X-	    printf("content length %ld\n", reqStruct->contentLength);
X+            printf("content length %ld\n", reqStruct->contentLength);
X #endif
X-	}
X+        }
X+        else if (!strncmp(reqArray[i], "Content-Type:", strlen("Content-type:")) || !strncmp(reqArray[i], "Content-type:", strlen("Content-type:")))
X+        {
X+            strncpy(reqStruct->contentType, &reqArray[i][strlen("Content-type: ")], CONTENT_TYPE_LEN - 1);
X+#ifdef PRINTF_DEBUG
X+            printf("content type %s\n", reqStruct->contentType);
X+#endif
X+        }
X+        else if (!strncmp(reqArray[i], "Cookie:", strlen("Cookie:")))
X+        {
X+            strncpy(reqStruct->cookie, &reqArray[i][strlen("Cookie: ")], MAX_COOKIE_LEN - 1);
X+#ifdef PRINTF_DEBUG
X+            printf("cookie %s\n", reqStruct->cookie);
X+#endif
X+        }
X         i++;
X     }
X     /* if we didn't find a User-Aget we fill in a (N)ot(R)ecognized */
X@@ -414,18 +432,39 @@
X                     /* we append the default file name */
X                     strcat(completeFilePath, defaultFileName);
X                     analyzeExtension(mimeType, completeFilePath);
X-                    dumpFile(sock, completeFilePath, mimeType, req);
X+#ifdef PHP
X+                    if (strncmp(mimeType, "application/x-httpd-php", 23))
X+#endif
X+                        dumpFile(sock, completeFilePath, mimeType, req);
X+#ifdef PHP
X+                    else
X+                        phpHandler(port, sock, phpFileName, completeFilePath, req, NULL);
X+#endif
X                 }
X #else
X                 /* we append the default file name */
X                 strcat(completeFilePath, defaultFileName);
X                 analyzeExtension(mimeType, completeFilePath);
X-                dumpFile(sock, completeFilePath, mimeType, req);
X+#ifdef PHP
X+                if (strncmp(mimeType, "application/x-httpd-php", 23))
X+#endif
X+                    dumpFile(sock, completeFilePath, mimeType, req);
X+#ifdef PHP
X+                else
X+                    phpHandler(port, sock, phpFileName, completeFilePath, req, NULL);
X+#endif
X #endif
X             } else
X             { /* it is a plain file */
X                 analyzeExtension(mimeType, completeFilePath);
X-                dumpFile(sock, completeFilePath, mimeType, req);
X+#ifdef PHP
X+                if (strncmp(mimeType, "application/x-httpd-php", 23))
X+#endif
X+                    dumpFile(sock, completeFilePath, mimeType, req);
X+#ifdef PHP
X+                else
X+                    phpHandler(port, sock, phpFileName, completeFilePath, req, NULL);
X+#endif
X             }
X         }
X     } else if (!strcmp(req.method, "HEAD"))
X@@ -494,7 +533,14 @@
X                 strcat(completeFilePath, defaultFileName);
X             }
X             analyzeExtension(mimeType, completeFilePath);
X-            dumpHeader(sock, completeFilePath, mimeType, req);
X+#ifdef PHP
X+            if (strncmp(mimeType, "application/x-httpd-php", 23))
X+#endif
X+                dumpFile(sock, completeFilePath, mimeType, req);
X+#ifdef PHP
X+            else
X+                phpHandler(port, sock, phpFileName, completeFilePath, req, NULL);
X+#endif
X         }
X     } else if (!strcmp(req.method, "POST"))
X     {
X@@ -507,13 +553,6 @@
X         int readFinished;
X         
X         printf("Handling of POST method\n");
X-        /* first we check if the path contains the directory selected for cgi's and in case handle it */
X-        if (strncmp(req.documentAddress, CGI_MATCH_STRING, strlen(CGI_MATCH_STRING)))
X-        {
X-            /* non cgi POST is not supported */
X-            sayError(sock, UNHANDLED_METHOD, "", req);
X-            return -1;
X-        }
X #ifdef PRINTF_DEBUG
X         printf ("begin of post handling\n");
X 
X@@ -523,9 +562,15 @@
X         totalRead = 0;
X         stuckCounter = 0;
X         timeOutCounter = 0;
X-        while (!readFinished)
X-        {
X-            howMany = recv(newSocket, tempBuff, BUFFER_SIZE, 0);
X+
X+        /* SECURITY: Avoid malicious Content-Length -- check \r\n\r\n\0 also */
X+        if (req.contentLength < 0 || req.contentLength >= BUFFER_SIZE-5) {
X+                sayError(sock, 500, "", req);
X+                return -1;
X+        }
X+
X+        /* SECURITY: Remove loop to prevent buffer overflow */
X+            howMany = recv(newSocket, tempBuff, req.contentLength+5, 0);
X 	    tempBuff[howMany] = '\0'; /* seems that some Unices need this */
X #ifdef PRINTF_DEBUG
X         printf ("read: %d\n%s\n", howMany, tempBuff);
X@@ -579,16 +624,15 @@
X 	    	if (howMany == req.contentLength)
X 		    readFinished = YES;
X             }
X-    }
X #ifdef PRINTF_DEBUG
X-    printf("total read %d\n", totalRead);
X+        printf("total read %d\n", totalRead);
X #endif
X-    if (totalRead == 0)
X-    {
X-        printf("Request read error\n");
X-    } else
X-    {
X-        if (buff[totalRead - 1] != '\n') /* we need a trailing \n or the script will wait forever */
X+        if (totalRead == 0)
X+        {
X+            printf("Request read error\n");
X+        } else
X+        {
X+            if (buff[totalRead - 1] != '\n') /* we need a trailing \n or the script will wait forever */
X             {
X                 buff[totalRead++] = '\n';
X                 buff[totalRead] = '\0';
X@@ -596,7 +640,77 @@
X #ifdef PRINTF_DEBUG
X             printf("buff: |%s|\n", buff);
X #endif
X-            cgiHandler(port, sock, req, buff);
X+            if (!strncmp(req.documentAddress, CGI_MATCH_STRING, strlen(CGI_MATCH_STRING)))
X+            {
X+                cgiHandler(port, sock, req, buff);
X+            } else
X+            {
X+#ifdef PHP
X+                strcpy(completeFilePath, homePath);
X+                strcat(completeFilePath, req.documentAddress);
X+                /* now we check if the given path tries to get out of the root */
X+                {
X+                    int i,j;
X+                    int sL;
X+                    char dirName[MAX_PATH_LEN+1];
X+                    int depthCount = 0;
X+
X+                    sL = strlen(req.documentAddress);
X+                    dirName[0] = '\0';
X+                    if (sL > 3) {
X+                        dirName[0] = req.documentAddress[1];
X+                        dirName[1] = req.documentAddress[2];
X+                        dirName[2] = req.documentAddress[3];
X+                        dirName[3] ='\0';
X+                        if (!strcmp(dirName, "../"))
X+                        {
X+                            sayError(sock, FORBIDDEN, req.documentAddress, req);
X+                            return -1;
X+                        }
X+                    }
X+                    j = 0;
X+                    for (i = 1; i < sL; i++) {
X+                        if (req.documentAddress[i] == '/')
X+                        {
X+                            dirName[j] = '\0';
X+                            if (strcmp(dirName, ".."))
X+                                depthCount ++;
X+                            else
X+                                depthCount--;
X+                            j = 0;
X+                        } else
X+                            dirName[j++] = req.documentAddress[i];
X+                    }
X+                    if (depthCount < 0)
X+                    {
X+                        sayError(sock, FORBIDDEN, req.documentAddress, req);
X+                        return -1;
X+                    }
X+                }
X+                /* now we check if the given file is a directory or a plain file */
X+                stat(completeFilePath, &fileStats);
X+                if ((fileStats.st_mode & S_IFDIR) == S_IFDIR)
X+                {
X+                    /* if does not end with a slash, we get an error */
X+                    if(completeFilePath[strlen(completeFilePath)-1] != '/')
X+                    {
X+                        sayError(sock, NOT_FOUND, req.documentAddress, req);
X+                        return -1;
X+                    }
X+                    /* we append the default file name */
X+                    strcat(completeFilePath, defaultFileName);
X+                }
X+                analyzeExtension(mimeType, completeFilePath);
X+                if (strncmp(mimeType, "application/x-httpd-php", 23))
X+                {
X+#endif
X+                    /* non cgi POST is not supported */
X+                    sayError(sock, UNHANDLED_METHOD, "", req);
X+                    return -1;
X+#ifdef PHP
X+                } else phpHandler(port, sock, phpFileName, completeFilePath, req, buff);
X+#endif
X+            }
X         }
X     } else
X     {
X@@ -625,7 +739,7 @@
X     f = fopen(configFile, "r");
X     if (f == NULL)
X     {
X-        printf("Error opening config file. Setting defaults.\n");
X+        printf("Config file not found. Setting defaults.\n");
X         *serverPort = DEFAULT_PORT;
X         *maxChildren = DEFAULT_MAX_CHILDREN;
X         strcpy(homePath, DEFAULT_DOCS_LOCATION);
X@@ -634,7 +748,9 @@
X         sockTimeVal.tv_usec = DEFAULT_USEC_TO;
X         strcpy(logFileName, DEFAULT_LOG_FILE);
X         strcpy(mimeTypesFileName, DEFAULT_MIME_FILE);
X+        strcpy(phpFileName, DEFAULT_PHP_FILE);
X         strcpy(cgiRoot, DEFAULT_CGI_ROOT);
X+        initMimeTypes();
X         return -1;
X     }
X     if (!feof(f)) fscanf(f, "%s %s", str1, str2);
X@@ -735,11 +851,25 @@
X         if (mimeTypesFileName == NULL)
X         {
X             strcpy(mimeTypesFileName, DEFAULT_MIME_FILE);
X-            printf("Error reading mimeTypesFileName from file, setting default, %s\n", mimeTypesFileName);
X+            printf("Error reading mimeTypesFile from file, setting default, %s\n", mimeTypesFileName);
X         }
X     } else {
X         strcpy(mimeTypesFileName, DEFAULT_MIME_FILE);
X-        printf("Error reading mimeTypesFileName from file, setting default, %s\n", mimeTypesFileName);
X+        printf("Error reading mimeTypesFile from file, setting default, %s\n", mimeTypesFileName);
X+    }
X+    if (!feof(f)) fscanf(f, "%s %s", str1, str2);
X+    if (str1 != NULL && str2 != NULL && !strcmp(str1, "phpFile"))
X+    {
X+        sscanf(str2, "%s", phpFileName);
X+        if (logFileName == NULL)
X+        {
X+            strcpy(phpFileName, DEFAULT_LOG_FILE);
X+            printf("Error reading phpFile from file, setting default, %s\n", phpFileName);
X+        }
X+    } else
X+    {
X+        strcpy(phpFileName, DEFAULT_PHP_FILE);
X+        printf("Error reading phpFile from file, setting default, %s\n", phpFileName);
X     }
X     if (!feof(f)) fscanf(f, "%s %s", str1, str2);
X     if (str1 != NULL && str2 != NULL && !strcmp(str1, "cgiRoot"))
X@@ -775,6 +905,7 @@
X     int readFinished;
X     struct request gottenReq;
X     int isKeepAlive;
X+    int bool;
X     struct sockaddr_in listenName;           /* data struct for the listen port */
X     struct sockaddr_in acceptedSockStruct;   /* sockaddr for the internetworking */
X     int acceptedSocketLen;                   /* size of the structure */
X@@ -808,9 +939,16 @@
X         printf("socket creation error occoured\n");
X         return -1;
X     }
X+    bool = 1;
X+    error = setsockopt (theSocket, SOL_SOCKET, SO_REUSEADDR, &bool, sizeof(bool));
X+    if (error == -1)
X+    {   if (errno == EADDRINUSE)
X+        printf("set socket option error occoured\n");
X+        return -1;
X+    }
X     error = bind (theSocket, (struct sockaddr*)  &listenName, sizeof(listenName));
X     if (error == -1)
X-    {
X+    {   if (errno == EADDRINUSE)
X         printf("socket binding error occoured\n");
X         return -2;
X     }
END-of-pserv/files/patch-main.c
echo x - pserv/files/patch-Makefile
sed 's/^X//' >pserv/files/patch-Makefile << 'END-of-pserv/files/patch-Makefile'
X--- Makefile.orig	Mon Sep  8 20:05:54 2003
X+++ Makefile	Thu Oct 16 14:24:50 2003
X@@ -1,11 +1,11 @@
X #Change the following to your needs
X-CC = gcc
X+CC ?= gcc
X #insert here flags, eg. optimizations
X-CFLAGS = -Wall -O3
X-LIBS = -lnsl -lsocket
X SRCS = main.c handlers.c mime.c log.c
X OBJS = main.o handlers.o mime.o log.o
X PROGRAM = pserv
X+
X+all : $(PROGRAM)
X 
X $(PROGRAM) : $(OBJS)
X 	$(CC) -o $(PROGRAM) $(OBJS) $(LIBS)
END-of-pserv/files/patch-Makefile
echo x - pserv/files/patch-main.h
sed 's/^X//' >pserv/files/patch-main.h << 'END-of-pserv/files/patch-main.h'
X--- main.h.orig	Fri Sep 19 00:36:03 2003
X+++ main.h	Thu Oct 16 13:52:18 2003
X@@ -24,11 +24,12 @@
X 
X 
X /* --- CPP parsing options --- */
X-#define PRINTF_DEBUG          /* enable this to print some debugging messages */
X+#undef PRINTF_DEBUG          /* enable this to print some debugging messages */
X #undef ON_THE_FLY_CONVERSION /* enable this for line ending conversion */
X #undef BRAIN_DEAD_CAST       /* if your compiler is brainwashed and does not cast standard types.h structures */
X #define FORKING_SERVER       /* enables to fork for every request */
X #define AUTO_INDEX           /* enables auto-index of directories */
X+#define PHP                  /* enables transparent PHP support */
X 
X /* --- Configure options --- */
X #define CONFIG_FILE_NAME "pserv.conf"
X@@ -39,19 +40,19 @@
X #define MIME_TYPE_DEFAULT "application/octet-stream"
X 
X /* configuration file location */
X-#define DEFAULT_CONFIG_LOCATION "/export/home/multix/pserv/"
X-//#define DEFAULT_CONFIG_LOCATION "/Users/multix/Documents/code/pserv/"
X+#define DEFAULT_CONFIG_LOCATION "/usr/local/etc/"
X 
X /* hard-wired defaults, if loading of config file fails */
X-#define DEFAULT_PORT 2000
X+#define DEFAULT_PORT 80
X #define DEFAULT_MAX_CHILDREN 5
X-#define DEFAULT_DOCS_LOCATION "/export/home/multix/public_html"
X+#define DEFAULT_DOCS_LOCATION "/usr/local/www/data"
X #define DEFAULT_FILE_NAME "index.html"
X #define DEFAULT_SEC_TO 1
X #define DEFAULT_USEC_TO 100
X-#define DEFAULT_LOG_FILE "/export/home/multix/pserv/pserv.log"
X-#define DEFAULT_MIME_FILE "/export/home/multix/pserv/mime_types.dat"
X-#define DEFAULT_CGI_ROOT "/export/home/multix/public_html/cgi-bin"
X+#define DEFAULT_LOG_FILE "/var/log/pserv.log"
X+#define DEFAULT_MIME_FILE "/usr/local/etc/mime.types"
X+#define DEFAULT_PHP_FILE "/usr/local/bin/php"
X+#define DEFAULT_CGI_ROOT "/usr/local/www/cgi-bin"
X #define DEFAULT_SERVER_NAME "localhost"
X 
X /* amount of connections queued in listening */
X@@ -120,9 +121,11 @@
X #define ADDRESS_LEN 16
X #define METHOD_LEN 16
X #define PROTOCOL_LEN 16
X+#define CONTENT_TYPE_LEN 256
X #define USER_AGENT_LEN 256
X #define MAX_QUERY_STRING_LEN 1024
X #define MAX_PATH_LEN 1024
X+#define MAX_COOKIE_LEN 4096
X 
X struct request
X {
X@@ -133,7 +136,9 @@
X     char protocolVersion[PROTOCOL_LEN+1];
X     int keepAlive;
X     char userAgent[USER_AGENT_LEN+1];
X+    char cookie[MAX_COOKIE_LEN+1];
X     long int contentLength;
X+    char contentType[CONTENT_TYPE_LEN+1];
X     char rest[BUFFER_SIZE+1];
X };
X 
END-of-pserv/files/patch-main.h
echo x - pserv/files/patch-mime_types.dat
sed 's/^X//' >pserv/files/patch-mime_types.dat << 'END-of-pserv/files/patch-mime_types.dat'
X--- mime_types.dat.orig	Wed Oct 15 15:57:08 2003
X+++ mime_types.dat	Wed Oct 15 15:57:22 2003
X@@ -14,3 +14,4 @@
X tar	application/x-tar
X lha	application/octet-stream
X lzh	application/octet-stream
X+php	application/x-httpd-php
END-of-pserv/files/patch-mime_types.dat
echo x - pserv/files/patch-pserv.conf
sed 's/^X//' >pserv/files/patch-pserv.conf << 'END-of-pserv/files/patch-pserv.conf'
X--- pserv.conf.orig	Wed Oct 15 16:18:05 2003
X+++ pserv.conf	Wed Oct 15 16:20:01 2003
X@@ -1,9 +1,10 @@
X-port	80
X+port		80
X maxChildren	4
X-documentsPath	/export/home/multix/public_html
X+documentsPath	%%PREFIX%%/www/data
X defaultFile	index.html
X secTimeout	1
X uSecTimeout	100000
X-logFile		/export/home/multix/pserv/pserv.log
X-mimeTypesFile	/export/home/multix/pserv/mime_types.dat
X-cgiRoot		/export/home/multix/public_html/cgi-bin
X+logFile		/var/log/pserv.log
X+mimeTypesFile	%%PREFIX%%/etc/mime.types
X+phpFile		%%LOCALBASE%%/bin/php
X+cgiRoot		%%PREFIX%%/www/cgi-bin
END-of-pserv/files/patch-pserv.conf
echo x - pserv/files/patch-handlers.c
sed 's/^X//' >pserv/files/patch-handlers.c << 'END-of-pserv/files/patch-handlers.c'
X--- handlers.c.orig	Thu Sep 18 15:26:48 2003
X+++ handlers.c	Thu Oct 16 14:16:05 2003
X@@ -24,6 +24,7 @@
X #endif
X 
X extern char cgiRoot[MAX_PATH_LEN+1]; /* root for CGI scripts exec */
X+extern char homePath[MAX_PATH_LEN+1]; /* root for PHP scripts exec */
X extern int port;                     /* server port */
X extern char defaultFileName[MAX_PATH_LEN+1]; /* default name for index, default or similar file */
X 
X@@ -262,12 +263,33 @@
X         newArgv[i] = NULL; /* we correctly terminate argv */
X         
X         i = 0;
X+        if (req.contentLength != -1)
X+        {
X+            sprintf(newEnvp[i++], "CONTENT_LENGTH=%ld", req.contentLength);
X+            strcpy(newEnvp[i], "CONTENT_TYPE=");
X+            strcat(newEnvp[i++], req.contentType);
X+        }
X+        strcpy(newEnvp[i], "SERVER_NAME=");
X+        strcat(newEnvp[i++], DEFAULT_SERVER_NAME);
X         strcpy(newEnvp[i], "SERVER_SOFTWARE=");
X         strcat(newEnvp[i], SERVER_SOFTWARE_STR);
X         strcat(newEnvp[i], "/");
X         strcat(newEnvp[i++], SERVER_VERSION_STR);
X+        strcpy(newEnvp[i], "SERVER_PROTOCOL=");
X+        strcat(newEnvp[i++], req.protocolVersion);
X         strcpy(newEnvp[i], "REQUEST_METHOD=");
X         strcat(newEnvp[i++], req.method);
X+        strcpy(newEnvp[i], "REMOTE_ADDR=");
X+        strcat(newEnvp[i++], req.address);
X+        strcpy(newEnvp[i], "HTTP_USER_AGENT=");
X+        strcat(newEnvp[i++], req.userAgent);
X+        if (req.cookie[0] != '\0')
X+        {
X+            strcpy(newEnvp[i], "HTTP_COOKIE=");
X+            strcat(newEnvp[i++], req.cookie);
X+        }
X+        strcpy(newEnvp[i], "SCRIPT_FILENAME=");
X+        strcat(newEnvp[i++], completedPath);
X         strcpy(newEnvp[i], "SCRIPT_NAME=");
X         strcat(newEnvp[i++], req.documentAddress);
X         strcpy(newEnvp[i], "GATEWAY_INTERFACE=");
X@@ -302,13 +324,256 @@
X         execve(completedPath, newArgv, newEnvp);
X 	/* we reach this line only if an execution error occoured */
X 	/* logging will happen in the father */
X-	printf("\n<HTML><HEAD><TITLE>CGI Error</TITLE></HEAD><BODY><H1>Cgi Exec error</H1></BODY></HTML>\n");
X+	printf("\n<HTML><HEAD><TITLE>CGI Error</TITLE></HEAD><BODY><H1>CGI Exec error</H1></BODY></HTML>\n");
X         exit(-1);        
X     }
X     return 0;
X }
X 
X-int dumpHeader(sock, filePath, mimeType, req)
X+
X+#ifdef PHP
X+int phpHandler(port, sock, phpFileName, completedPath, req, postStr)
X+int port;
X+int sock;
X+char *phpFileName;
X+char *completedPath;
X+struct request req;
X+char *postStr;
X+{
X+    char envPath[MAX_PATH_LEN+1]; /* where to hold the envrion PATH parameter */
X+    char *relativePath;
X+    char scriptWorkingDir[MAX_PATH_LEN+1];
X+    char **newArgv;
X+    char **newEnvp;
X+    int i;
X+    int outStdPipe[2]; /* we will redirect the script output to a pipe so we can read it */
X+    int inStdPipe[2];  /* we will redirect the script input to a pipe so we can read it */
X+    int pid;           /* we fork and execute inside the child the script */
X+    char pipeReadBuf[PIPE_READ_BUF+1];
X+    int howMany;
X+    int totalSentFromPipe; /* ampunt of bytes sucked from the pipe and pushed in to the socket */
X+    int fatal;
X+
X+    relativePath = strrchr(completedPath, '/');
X+    strncpy(scriptWorkingDir, completedPath, strlen(completedPath) - strlen(relativePath));
X+    scriptWorkingDir[strlen(completedPath) - strlen(relativePath)] = '\0';
X+
X+    /* first we create the pipes needed for stdout redirection */
X+    if (pipe(outStdPipe))
X+    {
X+#ifdef PRINTF_DEBUG
X+        printf("Pipe creation error\n");
X+        return -1;
X+#endif
X+    }
X+    if (pipe(inStdPipe))
X+    {
X+#ifdef PRINTF_DEBUG
X+        printf("Pipe creation error\n");
X+        return -1;
X+#endif
X+    }
X+
X+
X+    /* now we fork to subsequently execve */
X+    pid = fork();
X+    if (pid)
X+    { /* this is the parent process */
X+        if (pid < 0)
X+        { /* we check for creation error */
X+            printf ("Forking error during cgi exec: %d\n", errno);
X+            return -1;
X+        }
X+        /* we close the unused end of the pipe */
X+        close(outStdPipe[WRITE]);
X+        close(inStdPipe[READ]);
X+
X+        if (!strcmp(req.method, "POST")) /* we have to feed the stdin of the script */
X+        {
X+            if(!strlen(postStr))
X+            {
X+#ifdef PRINTF_DEBUG
X+                printf("cannot post empty data\n");
X+#endif
X+                return -1;
X+            }
X+            howMany = write(inStdPipe[WRITE], postStr, strlen(postStr));
X+            if (howMany < 0)
X+                printf("Error during script pipe read.\n");
X+        }
X+        totalSentFromPipe = 0;
X+        fatal = NO;
X+        howMany = 1;
X+        while (howMany > 0 && !fatal)
X+        {
X+            howMany = read(outStdPipe[READ], &pipeReadBuf, PIPE_READ_BUF);
X+            if (howMany < 0)
X+                printf("Error during script pipe read.\n");
X+            else if (!howMany)
X+                printf("Nothing read from script pipe.\n");
X+            else {
X+                pipeReadBuf[howMany] = '\0';
X+                if (send(sock, pipeReadBuf, howMany, 0) < 0)
X+                {
X+                    printf("error during CGI sock writing! %d\n", errno);
X+                    if (errno == EAGAIN)
X+                        printf("output resource temporarily not available\n");
X+                    else if (errno == EPIPE)
X+                    {
X+                        printf("broken pipe during CGI out.\n");
X+                        fatal = YES;
X+                    } else if (errno == EBADF)
X+                    {
X+#ifdef PRINTF_DEBUG
X+                        printf("invalid out descriptor.\n");
X+#endif
X+                        fatal = YES;
X+                    }
X+                } else
X+                    totalSentFromPipe += howMany;
X+            }
X+        }
X+        /* now we finished and we clean up */
X+        wait(&i);
X+        if (i) /* check if execution exited cleanly or with code */
X+            logWriter(LOG_CGI_FAILURE, NULL, 0, req, i);
X+        else
X+            logWriter(LOG_CGI_SUCCESS, NULL, totalSentFromPipe, req, 0);
X+        close(outStdPipe[READ]);
X+        close(inStdPipe[WRITE]);
X+    } else
X+    { /* this is the child process */
X+        /* now we do some environment setup work */
X+        newArgv = calloc(MAX_ARGV_LEN + 1, sizeof(char*));
X+        for (i = 0; i < MAX_ARGV_LEN + 1; i++)
X+        {
X+            newArgv[i] = calloc(MAX_PATH_LEN, sizeof(char));
X+        }
X+
X+        newEnvp = calloc(MAX_ENVP_LEN + 1, sizeof(char*));
X+        for (i = 0; i < MAX_ENVP_LEN + 1; i++)
X+        {
X+            newEnvp[i] = calloc(MAX_PATH_LEN, sizeof(char));
X+        }
X+
X+
X+
X+        /* extracting PATH env variable */
X+        i = 0;
X+        while (environ && strncmp(environ[i], PATH_MATCH_STRING, strlen(PATH_MATCH_STRING)))
X+            i++;
X+        if(environ[i])
X+            strcpy(envPath, environ[i]);
X+        else
X+            envPath[0] = '\0'; /* maybe we should set some default? */
X+
X+        i = 0;
X+        strcpy(newArgv[i++], phpFileName);     /* here we should pass the phppath */
X+        strcpy(newArgv[i++], completedPath);  /* here we should pass the scriptpath */
X+        if (strlen(req.queryString))
X+        {
X+            int toParse;
X+            int j, k;
X+
X+            toParse = YES;
X+            j = strlen(req.queryString);
X+            while (toParse && j > 0)
X+            {
X+                if (req.queryString[j] == '=')
X+                    toParse = NO;
X+                j--;
X+            }
X+            if (toParse)
X+            {
X+                j = 0;
X+                k = 0;
X+                howMany = strlen(req.queryString);
X+                while (j < howMany)
X+                {
X+                    if (req.queryString[j] == '+')
X+                    {
X+                        newArgv[i++][k] = '\0';
X+                        k = 0;
X+                    } else
X+                        newArgv[i][k++] = req.queryString[j];
X+                    j++;
X+                }
X+                i++; /* after all we will have at least one argument! */
X+            }
X+        }
X+        newArgv[i] = NULL; /* we correctly terminate argv */
X+
X+        i = 0;
X+        if (req.contentLength != -1)
X+        {
X+            sprintf(newEnvp[i++], "CONTENT_LENGTH=%ld", req.contentLength);
X+            strcpy(newEnvp[i], "CONTENT_TYPE=");
X+            strcat(newEnvp[i++], req.contentType);
X+        }
X+        strcpy(newEnvp[i], "SERVER_NAME=");
X+        strcat(newEnvp[i++], DEFAULT_SERVER_NAME);
X+        strcpy(newEnvp[i], "SERVER_SOFTWARE=");
X+        strcat(newEnvp[i], SERVER_SOFTWARE_STR);
X+        strcat(newEnvp[i], "/");
X+        strcat(newEnvp[i++], SERVER_VERSION_STR);
X+        strcpy(newEnvp[i], "SERVER_PROTOCOL=");
X+        strcat(newEnvp[i++], req.protocolVersion);
X+        strcpy(newEnvp[i], "REQUEST_METHOD=");
X+        strcat(newEnvp[i++], req.method);
X+        strcpy(newEnvp[i], "REMOTE_ADDR=");
X+        strcat(newEnvp[i++], req.address);
X+        strcpy(newEnvp[i], "HTTP_USER_AGENT=");
X+        strcat(newEnvp[i++], req.userAgent);
X+        if (req.cookie[0] != '\0')
X+        {
X+            strcpy(newEnvp[i], "HTTP_COOKIE=");
X+            strcat(newEnvp[i++], req.cookie);
X+        }
X+        strcpy(newEnvp[i], "SCRIPT_FILENAME=");
X+        strcat(newEnvp[i++], completedPath);
X+        strcpy(newEnvp[i], "SCRIPT_NAME=");
X+        strcat(newEnvp[i++], req.documentAddress);
X+        strcpy(newEnvp[i], "GATEWAY_INTERFACE=");
X+        strcat(newEnvp[i++], CGI_VERSION);
X+        sprintf(newEnvp[i++], "SERVER_PORT=%d", port);
X+        strcpy(newEnvp[i++], envPath);
X+        strcpy(newEnvp[i], "QUERY_STRING=");
X+        strcat(newEnvp[i++], req.queryString);
X+        newEnvp[i] = NULL;
X+
X+        /* we change the current working directory to the scripts one */
X+        if(chdir(scriptWorkingDir))
X+        {
X+#ifdef PRINTF_DEBUG
X+            printf("error while changing PWD in script execution: %d\n", errno);
X+#endif
X+        }
X+
X+        close(outStdPipe[READ]);    /* we close the unused end*/
X+        dup2(outStdPipe[WRITE], 1); /* we duplicate the pipe to the stdout */
X+        close(outStdPipe[WRITE]);   /* we close the pipe, since we use the duplicate */
X+
X+        close(inStdPipe[WRITE]);    /* we close the unused end*/
X+        dup2(inStdPipe[READ], 0);   /* we duplicate the pipe to the stdin */
X+        close(inStdPipe[READ]);     /* we close the pipe, since we use the duplicate */
X+
X+
X+        /* generate a reduced mimeHeader, no type, no size, etc */
X+        generateMimeHeader(sock, 200, "", NULL, req.protocolVersion, CGI_ONLY_HEADER);
X+
X+        /* now we execute the script replacing the current child */
X+        execve(phpFileName, newArgv, newEnvp);
X+        /* we reach this line only if an execution error occoured */
X+        /* logging will happen in the father */
X+        printf("\n<HTML><HEAD><TITLE>PHP Error</TITLE></HEAD><BODY><H1>PHP Exec error</H1></BODY></HTML>\n");
X+        exit(-1);
X+    }
X+    return 0;
X+}
X+#endif
X+
X+int dumpHeader(port, sock, filePath, mimeType, req)
X+int port;
X int sock;
X char filePath[];
X char mimeType[];
X@@ -351,11 +616,11 @@
X         return -1;
X     }
X     stat(filePath, &fileStats);
X-    generateMimeHeader(sock, 200, mimeType, &fileStats, req.protocolVersion, FULL_HEADER);
X-    logWriter(LOG_GET_SUCCESS, req.documentAddress, (long int)fileStats.st_size, req, 0);
X     howMany = 0;
X     if (strncmp(mimeType, "text", 4)) /* check if it is a text type */
X     {   /* raw binary output routine */
X+        generateMimeHeader(sock, 200, mimeType, &fileStats, req.protocolVersion, FULL_HEADER);
X+        logWriter(LOG_GET_SUCCESS, req.documentAddress, (long int)fileStats.st_size, req, 0);
X         fatal = NO;
X         retry = NO;
X         while(!feof(inFile) && !fatal)
X@@ -399,11 +664,11 @@
X             if (howMany > 0)
X             {
X #ifdef ON_THE_FLY_CONVERSION
X-		 {
X-		     int i;
X-		     for (i = 0; i < howMany; i++)
X-		         if(outBuff[i] == '\r') outBuff[i] = '\n';
X-		 }
X+                 {
X+                     int i;
X+                     for (i = 0; i < howMany; i++)
X+                         if(outBuff[i] == '\r') outBuff[i] = '\n';
X+                 }
X #endif
X                 if (send(sock, outBuff, howMany, 0) < 0)
X                 {
X@@ -449,7 +714,7 @@
X     FILE *tempFile;
X     size_t generatedBytes;
X     char tempStr[MAX_PATH_LEN+1];
X-    char linkStr[MAX_PATH_LEN+1];
X+    char linkStr[MAX_PATH_LEN+2];
X     time_t currTime;
X     char timeStr[256];
X     
X@@ -497,10 +762,16 @@
X         if (strcmp(dp->d_name, ".")) /* not self */
X         {
X             if (strcmp(dp->d_name, ".."))
X+            {
X                 strcpy(linkStr, dp->d_name);
X-            else
X+                if (dp->d_type == DT_DIR)
X+                    strcat(linkStr, "/");
X+                sprintf(tempStr, "<A HREF=\"%s\">%s</A><BR>\n", linkStr, linkStr);
X+            } else
X+            {
X                 strcpy(linkStr, "Parent Directory");
X-            sprintf(tempStr, "<A HREF=\"%s\">%s</A><BR>\n", dp->d_name, linkStr);
X+                sprintf(tempStr, "<A HREF=\"%s/\">%s</A><BR>\n", dp->d_name, linkStr);
X+            }
X             generatedBytes += strlen(tempStr);
X             fprintf(tempFile, "%s\n", tempStr);
X         }
END-of-pserv/files/patch-handlers.c
echo x - pserv/files/patch-handlers.h
sed 's/^X//' >pserv/files/patch-handlers.h << 'END-of-pserv/files/patch-handlers.h'
X--- handlers.h.orig	Wed Oct 15 17:06:59 2003
X+++ handlers.h	Wed Oct 15 17:07:08 2003
X@@ -19,6 +19,7 @@
X 
X /* handlers.c */
X int cgiHandler();
X+int phpHandler();
X int dumpHeader();
X int dumpFile();
X int generateIndex();
END-of-pserv/files/patch-handlers.h
echo x - pserv/files/patch-mime.c
sed 's/^X//' >pserv/files/patch-mime.c << 'END-of-pserv/files/patch-mime.c'
X--- mime.c.orig	Thu Sep 18 15:26:55 2003
X+++ mime.c	Wed Oct 15 18:38:42 2003
X@@ -43,8 +43,8 @@
X     f = fopen(mimeTypesFileName, "r");
X     if (f == NULL)
X     {
X-        printf("Error opening mime types file. Setting defaults.\n");
X-        entries = 3;
X+        printf("Mime types file not found. Setting defaults.\n");
X+        entries = 6;
X         mimeArray = (mimeData *) calloc(entries, sizeof(mimeData));
X         if (mimeArray == NULL) {
X             printf("Errory while allocating mime types Array. Exiting.\n");
X@@ -52,10 +52,16 @@
X         }
X         strcpy(mimeArray[0].ext, "html");
X         strcpy(mimeArray[0].type, "text/html");
X-        strcpy(mimeArray[1].ext, "gif");
X-        strcpy(mimeArray[1].type, "image/gif");
X-        strcpy(mimeArray[2].ext, "jpg");
X-        strcpy(mimeArray[2].type, "image/jpg");
X+        strcpy(mimeArray[1].ext, "htm");
X+        strcpy(mimeArray[1].type, "text/html");
X+        strcpy(mimeArray[2].ext, "gif");
X+        strcpy(mimeArray[2].type, "image/gif");
X+        strcpy(mimeArray[3].ext, "jpg");
X+        strcpy(mimeArray[3].type, "image/jpg");
X+        strcpy(mimeArray[4].ext, "png");
X+        strcpy(mimeArray[4].type, "image/png");
X+        strcpy(mimeArray[5].ext, "php");
X+        strcpy(mimeArray[5].type, "application/x-httpd-php");
X         mimeEntries = entries;
X         return -1;
X     }
END-of-pserv/files/patch-mime.c
echo x - pserv/files/pserv.sh
sed 's/^X//' >pserv/files/pserv.sh << 'END-of-pserv/files/pserv.sh'
X#!/bin/sh
X
Xif ! PREFIX=$(expr $0 : "\(/.*\)/etc/rc\.d/$(basename $0)\$"); then
X    echo "$0: Cannot determine the PREFIX" >&2
X    exit 1
Xfi
X
Xcase "$1" in
Xstart)
X	if [ -x ${PREFIX}/sbin/pserv ]; then
X		${PREFIX}/sbin/pserv > /dev/null &
X		echo -n ' pserv'
X	fi
X	;;
Xstop)
X	killall pserv > /dev/null 2>&1
X	echo -n ' pserv'
X	;;
X*)
X	echo "Usage: `basename $0` {start|stop}" >&2
X	;;
Xesac
X
Xexit 0
END-of-pserv/files/pserv.sh
echo x - pserv/Makefile
sed 's/^X//' >pserv/Makefile << 'END-of-pserv/Makefile'
X# New ports collection makefile for:	pserv
X# Date created:			Wed Oct 15 14:16:14 CEST 2003
X# Whom:				Alex Dupre <sysadmin at alexdupre.com>
X#
X# $FreeBSD$
X#
X
XPORTNAME=	pserv
XPORTVERSION=	3.0.b1
XCATEGORIES=	www
XMASTER_SITES=	${MASTER_SITE_SOURCEFORGE}
XMASTER_SITE_SUBDIR=	${PORTNAME}
XDISTNAME=	${PORTNAME}-22-Sep-03
XEXTRACT_SUFX=	.tar.Z
X
XMAINTAINER=	sysadmin at alexdupre.com
XCOMMENT=	A portable and small webserver written in C
X
XWRKSRC=		${WRKDIR}/${PORTNAME}
XUSE_REINPLACE=	yes
X
Xpost-patch:
X	@${REINPLACE_CMD} "s|%%PREFIX%%|${PREFIX}|g;s|%%LOCALBASE%%|${LOCALBASE}|g" \
X		${WRKSRC}/pserv.conf
X
Xdo-install:
X	${INSTALL_PROGRAM} ${WRKSRC}/pserv ${PREFIX}/sbin/
X	${INSTALL_DATA} ${WRKSRC}/mime_types.dat ${PREFIX}/etc/mime.types
X	${INSTALL_DATA} ${WRKSRC}/pserv.conf ${PREFIX}/etc/
X	${INSTALL_SCRIPT} ${FILESDIR}/pserv.sh ${PREFIX}/etc/rc.d/
X
X.include <bsd.port.mk>
END-of-pserv/Makefile
echo x - pserv/distinfo
sed 's/^X//' >pserv/distinfo << 'END-of-pserv/distinfo'
XMD5 (pserv-22-Sep-03.tar.Z) = 4895631b730836c9202d5ac28b05f0fa
END-of-pserv/distinfo
echo x - pserv/pkg-descr
sed 's/^X//' >pserv/pkg-descr << 'END-of-pserv/pkg-descr'
XpServ is a small, portable HTTP server. It is written in pure C for speed and
Xportability. It runs as a standalone program and does not require inetd.
XIt should be small enough to be used in a mobile computer or to be run on your
Xobsolete workstation you have somewhere.
X
XThis port contains a patched version to support the execution of php scripts.
X
XWWW: http://sourceforge.net/projects/pserv/
X
X- Alex Dupre
Xsysadmin at alexdupre.com
END-of-pserv/pkg-descr
echo x - pserv/pkg-plist
sed 's/^X//' >pserv/pkg-plist << 'END-of-pserv/pkg-plist'
Xsbin/pserv
Xetc/mime.types
Xetc/pserv.conf
Xetc/rc.d/pserv.sh
END-of-pserv/pkg-plist
exit
--- pserv.sh ends here ---


>Release-Note:
>Audit-Trail:
>Unformatted:



More information about the freebsd-ports-bugs mailing list