callcgi: Added option to change CWD to the script's directory.
[ashd.git] / src / callcgi.c
index 42ac389..e6e909b 100644 (file)
@@ -66,7 +66,7 @@ static char *absolutify(char *file)
 static void forkchild(int inpath, char *prog, char *file, char *method, char *url, char *rest, int *infd, int *outfd)
 {
     int i;
-    char *qp, **env;
+    char *qp, **env, *name;
     int inp[2], outp[2];
     pid_t pid;
 
@@ -85,21 +85,31 @@ static void forkchild(int inpath, char *prog, char *file, char *method, char *ur
            close(i);
        if((qp = strchr(url, '?')) != NULL)
            *(qp++) = 0;
-       /*
-        * XXX: Currently missing:
-        *  SERVER_NAME (Partially)
-        *  SERVER_PORT
-        */
        putenv(sprintf2("SERVER_SOFTWARE=ashd/%s", VERSION));
        putenv("GATEWAY_INTERFACE=CGI/1.1");
        if(getenv("HTTP_VERSION"))
            putenv(sprintf2("SERVER_PROTOCOL=%s", getenv("HTTP_VERSION")));
        putenv(sprintf2("REQUEST_METHOD=%s", method));
-       putenv(sprintf2("PATH_INFO=%s", rest));
-       putenv(sprintf2("SCRIPT_NAME=%s", url));
+       if(*rest)
+           putenv(sprintf2("PATH_INFO=/%s", rest));
+       else
+           putenv("PATH_INFO=");
+       name = url;
+       /* XXX: This is an ugly hack (I think), but though I can think
+        * of several alternatives, none seem to be better. */
+       if(*rest && (strlen(url) > strlen(rest)) &&
+          !strcmp(rest, url + strlen(url) - strlen(rest)) &&
+          (url[strlen(url) - strlen(rest) - 1] == '/')) {
+           name = sprintf2("%.*s", (int)(strlen(url) - strlen(rest) - 1), url);
+       }
+       putenv(sprintf2("SCRIPT_NAME=%s", name));
        putenv(sprintf2("QUERY_STRING=%s", qp?qp:""));
        if(getenv("REQ_HOST"))
            putenv(sprintf2("SERVER_NAME=%s", getenv("REQ_HOST")));
+       if(getenv("REQ_X_ASH_SERVER_PORT"))
+           putenv(sprintf2("SERVER_PORT=%s", getenv("REQ_X_ASH_SERVER_PORT")));
+       if(getenv("REQ_X_ASH_PROTOCOL") && !strcmp(getenv("REQ_X_ASH_PROTOCOL"), "https"))
+           putenv("HTTPS=on");
        if(getenv("REQ_X_ASH_ADDRESS"))
            putenv(sprintf2("REMOTE_ADDR=%s", getenv("REQ_X_ASH_ADDRESS")));
        if(getenv("REQ_CONTENT_TYPE"))
@@ -279,14 +289,14 @@ static void sendheaders(char **headers, FILE *out)
 
 static void usage(void)
 {
-    flog(LOG_ERR, "usage: callcgi [-p PROGRAM] METHOD URL REST");
+    flog(LOG_ERR, "usage: callcgi [-c] [-p PROGRAM] METHOD URL REST");
 }
 
 int main(int argc, char **argv, char **envp)
 {
     int c;
-    char *file, *prog;
-    int inpath;
+    char *file, *prog, *sp;
+    int inpath, cd;
     int infd, outfd;
     FILE *in, *out;
     char **headers;
@@ -296,8 +306,12 @@ int main(int argc, char **argv, char **envp)
     
     prog = NULL;
     inpath = 0;
-    while((c = getopt(argc, argv, "p:")) >= 0) {
+    cd = 0;
+    while((c = getopt(argc, argv, "cp:")) >= 0) {
        switch(c) {
+       case 'c':
+           cd = 1;
+           break;
        case 'p':
            prog = optarg;
            inpath = 1;
@@ -316,6 +330,21 @@ int main(int argc, char **argv, char **envp)
        flog(LOG_ERR, "callcgi: needs to be called with the X-Ash-File header");
        exit(1);
     }
+    
+    if(cd) {
+       /* This behavior is encouraged by the CGI specification (RFC 3875, 7.2),
+        * but not strictly required, and I get the feeling it might break some
+        * relative paths here or there, so it's not the default for now. */
+       if((sp = strrchr(file, '/')) != NULL) {
+           *sp = 0;
+           if(chdir(file)) {
+               *sp = '/';
+           } else {
+               file = sp + 1;
+           }
+       }
+    }
+    
     if(prog == NULL)
        prog = file;
     forkchild(inpath, prog, file, argv[optind], argv[optind + 1], argv[optind + 2], &infd, &outfd);