/* Convert queue data in key / value pairs MOB - should be able to remove this and use standard form parsing */ static int getParams(char ***keys, char *buf, int len) { char** keyList; char *eq, *cp, *pp, *tok; int i, keyCount; *keys = 0; /* Change all plus signs back to spaces */ keyCount = (len > 0) ? 1 : 0; for (cp = buf; cp < &buf[len]; cp++) { if (*cp == '+') { *cp = ' '; } else if (*cp == '&' && (cp > buf && cp < &buf[len - 1])) { keyCount++; } } if (keyCount == 0) { return 0; } /* Crack the input into name/value pairs */ keyList = mprAlloc((keyCount * 2) * sizeof(char**)); i = 0; tok = 0; for (pp = stok(buf, "&", &tok); pp; pp = stok(0, "&", &tok)) { if ((eq = strchr(pp, '=')) != 0) { *eq++ = '\0'; pp = mprUriDecode(pp); eq = mprUriDecode(eq); } else { pp = mprUriDecode(pp); eq = 0; } if (i < (keyCount * 2)) { keyList[i++] = pp; keyList[i++] = eq; } } *keys = keyList; return keyCount; }
/* Validate a URI path for use in a HTTP request line The URI must contain only valid characters and must being with "/" both before and after decoding. A decoded, normalized URI path is returned. */ PUBLIC char *httpValidateUriPath(cchar *uri) { char *up; if (uri == 0 || *uri != '/') { return 0; } if (!httpValidUriChars(uri)) { return 0; } up = mprUriDecode(uri); if ((up = httpNormalizeUriPath(up)) == 0) { return 0; } if (*up != '/' || strchr(up, '\\')) { return 0; } return up; }
/* Build the command arguments. NOTE: argv is untrusted input. */ static void buildArgs(HttpConn *conn, MprCmd *cmd, int *argcp, cchar ***argvp) { HttpRx *rx; HttpTx *tx; char **argv; char *indexQuery, *cp, *tok; cchar *actionProgram, *fileName; size_t len; int argc, argind, i; rx = conn->rx; tx = conn->tx; fileName = tx->filename; assert(fileName); actionProgram = 0; argind = 0; argc = *argcp; if (tx->ext) { actionProgram = mprGetMimeProgram(rx->route->mimeTypes, tx->ext); if (actionProgram != 0) { argc++; } /* This is an Apache compatible hack for PHP 5.3 */ mprAddKey(rx->headers, "REDIRECT_STATUS", itos(HTTP_CODE_MOVED_TEMPORARILY)); } /* Count the args for ISINDEX queries. Only valid if there is not a "=" in the query. If this is so, then we must not have these args in the query env also? */ indexQuery = rx->parsedUri->query; if (indexQuery && !strchr(indexQuery, '=')) { argc++; for (cp = indexQuery; *cp; cp++) { if (*cp == '+') { argc++; } } } else { indexQuery = 0; } #if ME_WIN_LIKE || VXWORKS { char *bangScript, *cmdBuf, *program, *cmdScript; /* On windows we attempt to find an executable matching the fileName. We look for *.exe, *.bat and also do unix style processing "#!/program" */ findExecutable(conn, &program, &cmdScript, &bangScript, fileName); assert(program); if (cmdScript) { /* Cmd/Batch script (.bat | .cmd) Convert the command to the form where there are 4 elements in argv that cmd.exe can interpret. argv[0] = cmd.exe argv[1] = /Q argv[2] = /C argv[3] = ""script" args ..." */ argc = 4; len = (argc + 1) * sizeof(char*); argv = (char**) mprAlloc(len); memset(argv, 0, len); argv[argind++] = program; /* Duped in findExecutable */ argv[argind++] = "/Q"; argv[argind++] = "/C"; len = strlen(cmdScript) + 2 + 1; cmdBuf = mprAlloc(len); fmt(cmdBuf, len, "\"%s\"", cmdScript); argv[argind++] = cmdBuf; mprSetCmdDir(cmd, cmdScript); /* program will get freed when argv[] gets freed */ } else if (bangScript) { /* Script used "#!/program". NOTE: this may be overridden by a mime Action directive. */ argc++; /* Adding bangScript arg */ len = (argc + 1) * sizeof(char*); argv = (char**) mprAlloc(len); memset(argv, 0, len); argv[argind++] = program; /* Will get freed when argv[] is freed */ argv[argind++] = bangScript; /* Will get freed when argv[] is freed */ mprSetCmdDir(cmd, bangScript); } else { /* Either unknown extension or .exe (.out) program. */ len = (argc + 1) * sizeof(char*); argv = (char**) mprAlloc(len); memset(argv, 0, len); if (actionProgram) { argv[argind++] = sclone(actionProgram); } argv[argind++] = program; } } #else len = (argc + 1) * sizeof(char*); argv = mprAlloc(len); memset(argv, 0, len); if (actionProgram) { argv[argind++] = sclone(actionProgram); } // OPT - why clone all these string? argv[argind++] = sclone(fileName); #endif /* ISINDEX queries. Only valid if there is not a "=" in the query. If this is so, then we must not have these args in the query env also? FUTURE - should query vars be set in the env? */ if (indexQuery) { indexQuery = sclone(indexQuery); cp = stok(indexQuery, "+", &tok); while (cp) { argv[argind++] = mprEscapeCmd(mprUriDecode(cp), 0); cp = stok(NULL, "+", &tok); } } assert(argind <= argc); argv[argind] = 0; *argcp = argc; *argvp = (cchar**) argv; mprDebug("http cgi", 5, "CGI: command:"); for (i = 0; i < argind; i++) { mprDebug("http cgi", 5, " argv[%d] = %s", i, argv[i]); } }
/* Process the content data. Returns < 0 on error == 0 when more data is needed == 1 when data successfully written */ static int processUploadData(HttpQueue *q) { HttpConn *conn; HttpPacket *packet; MprBuf *content; Upload *up; ssize size, dataLen; bool pureData; char *data, *bp, *key; conn = q->conn; up = q->queueData; content = q->first->content; packet = 0; size = mprGetBufLength(content); if (size < up->boundaryLen) { /* Incomplete boundary. Return and get more data */ return 0; } bp = getBoundary(mprGetBufStart(content), size, up->boundary, up->boundaryLen, &pureData); if (bp == 0) { if (up->clientFilename) { /* No signature found yet. probably more data to come. Must handle split boundaries. */ data = mprGetBufStart(content); dataLen = pureData ? size : (size - (up->boundaryLen - 1)); if (dataLen > 0) { if (writeToFile(q, mprGetBufStart(content), dataLen) < 0) { return MPR_ERR_CANT_WRITE; } } mprAdjustBufStart(content, dataLen); return 0; /* Get more data */ } } data = mprGetBufStart(content); dataLen = (bp) ? (bp - data) : mprGetBufLength(content); if (dataLen > 0) { mprAdjustBufStart(content, dataLen); /* This is the CRLF before the boundary */ if (dataLen >= 2 && data[dataLen - 2] == '\r' && data[dataLen - 1] == '\n') { dataLen -= 2; } if (up->clientFilename) { /* Write the last bit of file data and add to the list of files and define environment variables */ if (writeToFile(q, data, dataLen) < 0) { return MPR_ERR_CANT_WRITE; } defineFileFields(q, up); } else { /* Normal string form data variables */ data[dataLen] = '\0'; #if KEEP httpTrace(conn, "request.upload.variables", "context", "'%s':'%s'", up->name, data); #endif key = mprUriDecode(up->name); data = mprUriDecode(data); httpSetParam(conn, key, data); if (packet == 0) { packet = httpCreatePacket(ME_MAX_BUFFER); } if (httpGetPacketLength(packet) > 0) { /* Need to add www-form-urlencoding separators */ mprPutCharToBuf(packet->content, '&'); } else { conn->rx->mimeType = sclone("application/x-www-form-urlencoded"); } mprPutToBuf(packet->content, "%s=%s", up->name, data); } } if (up->clientFilename) { /* Now have all the data (we've seen the boundary) */ mprCloseFile(up->file); up->file = 0; up->clientFilename = 0; } if (packet) { httpPutPacketToNext(q, packet); } up->contentState = HTTP_UPLOAD_BOUNDARY; return 0; }
/* Decode a Uri component static function decodeComponent(str: String): String */ static EjsString *uri_decodeComponent(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv) { return ejsCreateStringFromAsc(ejs, mprUriDecode(ejsToMulti(ejs, argv[0]))); }
/* Decode a Uri (ECMA Standard) function decodeURI(str: String): String */ static EjsObj *decodeURI(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv) { return (EjsObj*) ejsCreateStringFromAsc(ejs, mprUriDecode(ejsToMulti(ejs, argv[0]))); }