free(buf);
}
-static void forkchild(char *prog, char *file, char *method, char *url, char *rest, int *infd, int *outfd)
+static char *absolutify(char *file)
+{
+ char cwd[1024];
+
+ if(*file != '/') {
+ getcwd(cwd, sizeof(cwd));
+ return(sprintf2("%s/%s", cwd, file));
+ }
+ return(sstrdup(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;
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=HTTP/%s", 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));
+ putenv(sprintf2("PATH_INFO=/%s", rest));
+ 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"))
* This is (understandably) missing from the CGI
* specification, but PHP seems to require it.
*/
- putenv(sprintf2("SCRIPT_FILENAME=%s", file));
- execlp(prog, prog, file, NULL);
+ putenv(sprintf2("SCRIPT_FILENAME=%s", absolutify(file)));
+ if(inpath)
+ execlp(prog, prog, file, NULL);
+ else
+ execl(prog, prog, file, NULL);
exit(127);
}
close(inp[0]);
return(NULL);
}
+static char *defstatus(int code)
+{
+ if(code == 200)
+ return("OK");
+ else if(code == 201)
+ return("Created");
+ else if(code == 202)
+ return("Accepted");
+ else if(code == 204)
+ return("No Content");
+ else if(code == 300)
+ return("Multiple Choices");
+ else if(code == 301)
+ return("Moved Permanently");
+ else if(code == 302)
+ return("Found");
+ else if(code == 303)
+ return("See Other");
+ else if(code == 304)
+ return("Not Modified");
+ else if(code == 307)
+ return("Moved Temporarily");
+ else if(code == 400)
+ return("Bad Request");
+ else if(code == 401)
+ return("Unauthorized");
+ else if(code == 403)
+ return("Forbidden");
+ else if(code == 404)
+ return("Not Found");
+ else if(code == 500)
+ return("Internal Server Error");
+ else if(code == 501)
+ return("Not Implemented");
+ else if(code == 503)
+ return("Service Unavailable");
+ else
+ return("Unknown status");
+}
+
static void sendstatus(char **headers, FILE *out)
{
char **hp;
**hp = 0;
} else if(!strcasecmp(hp[0], "location")) {
location = hp[1];
+ hp += 2;
} else {
hp += 2;
}
}
if(status) {
- fprintf(out, "HTTP/1.1 %s\r\n", status);
+ if(strchr(status, ' '))
+ fprintf(out, "HTTP/1.1 %s\n", status);
+ else
+ fprintf(out, "HTTP/1.1 %i %s\n", atoi(status), defstatus(atoi(status)));
} else if(location) {
- fprintf(out, "HTTP/1.1 303 See Other\r\n");
+ fprintf(out, "HTTP/1.1 303 See Other\n");
} else {
- fprintf(out, "HTTP/1.1 200 OK\r\n");
+ fprintf(out, "HTTP/1.1 200 OK\n");
}
}
{
while(*headers) {
if(**headers)
- fprintf(out, "%s: %s\r\n", headers[0], headers[1]);
+ fprintf(out, "%s: %s\n", headers[0], headers[1]);
headers += 2;
}
}
+static void usage(void)
+{
+ flog(LOG_ERR, "usage: callcgi [-p PROGRAM] METHOD URL REST");
+}
+
int main(int argc, char **argv, char **envp)
{
- char *file;
- int in, out;
- FILE *ins, *outs;
+ int c;
+ char *file, *prog;
+ int inpath;
+ int infd, outfd;
+ FILE *in, *out;
char **headers;
environ = envp;
signal(SIGPIPE, SIG_IGN);
- if(argc < 5) {
- flog(LOG_ERR, "usage: callcgi PROGRAM METHOD URL REST");
+
+ prog = NULL;
+ inpath = 0;
+ while((c = getopt(argc, argv, "p:")) >= 0) {
+ switch(c) {
+ case 'p':
+ prog = optarg;
+ inpath = 1;
+ break;
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ if(argc - optind < 3) {
+ usage();
exit(1);
}
if((file = getenv("REQ_X_ASH_FILE")) == NULL) {
flog(LOG_ERR, "callcgi: needs to be called with the X-Ash-File header");
exit(1);
}
- forkchild(argv[1], file, argv[2], argv[3], argv[4], &in, &out);
- ins = fdopen(in, "w");
- passdata(stdin, ins);
- fclose(ins);
- outs = fdopen(out, "r");
- if((headers = parseheaders(outs)) == NULL) {
+ if(prog == NULL)
+ prog = file;
+ forkchild(inpath, prog, file, argv[optind], argv[optind + 1], argv[optind + 2], &infd, &outfd);
+ in = fdopen(infd, "w");
+ passdata(stdin, in);
+ fclose(in);
+ out = fdopen(outfd, "r");
+ if((headers = parseheaders(out)) == NULL) {
flog(LOG_WARNING, "CGI handler returned invalid headers");
exit(1);
}
sendstatus(headers, stdout);
sendheaders(headers, stdout);
- printf("\r\n");
- passdata(outs, stdout);
+ printf("\n");
+ passdata(out, stdout);
return(0);
}