static int websTidyUrl(webs_t wp) { char_t *parts[64]; /* Array of ptr's to URL parts */ char_t *token, *url, *tidyurl; int i, len, npart; a_assert(websValid(wp)); /* * Copy the string so we don't destroy the original (yet) */ url = bstrdup(B_L, wp->url); websDecodeUrl(url, url, gstrlen(url)); len = npart = 0; parts[0] = NULL; token = gstrtok(url, T("/")); /* * Look at each directory segment and process "." and ".." segments * Don't allow the browser to pop outside the root web. */ while (token != NULL) { if (gstrcmp(token, T("..")) == 0) { if (npart > 0) { npart--; } } else if (gstrcmp(token, T(".")) != 0) { parts[npart] = token; len += gstrlen(token) + 1; npart++; } token = gstrtok(NULL, T("/")); } /* * Re-construct URL. Need extra space all "/" and null. */ if (npart || (gstrcmp(url, T("/")) == 0) || (url[0] == '\0')) { tidyurl = balloc(B_L, (len + 2) * sizeof(char_t)); *tidyurl = '\0'; for (i = 0; i < npart; i++) { gstrcat(tidyurl, T("/")); gstrcat(tidyurl, parts[i]); } bfree(B_L, url); bfree(B_L, wp->url); wp->url = tidyurl; return 0; } else { bfree(B_L, url); return -1; } }
int websValidateUrl(webs_t wp, char_t *path) { char_t *parts[64]; /* Array of ptr's to URL parts */ char_t *token, *dir, *lpath; int i, len, npart; a_assert(websValid(wp)); a_assert(path); dir = websGetRequestDir(wp); if (dir == NULL || *dir == '\0') { return -1; } /* * Copy the string so we don't destroy the original */ path = bstrdup(B_L, path); websDecodeUrl(path, path, gstrlen(path)); len = npart = 0; parts[0] = NULL; /* * 22 Jul 02 -- there were reports that a directory traversal exploit was * possible in the WebServer running under Windows if directory paths * outside the server's specified root web were given by URL-encoding the * backslash character, like: * * GoAhead is vulnerable to a directory traversal bug. A request such as * * GoAhead-server/../../../../../../../ results in an error message * 'Cannot open URL'. * However, by encoding the '/' character, it is possible to break out of * the * web root and read arbitrary files from the server. * Hence a request like: * * GoAhead-server/..%5C..%5C..%5C..%5C..%5C..%5C/winnt/win.ini returns the * contents of the win.ini file. * (Note that the description uses forward slashes (0x2F), but the example * uses backslashes (0x5C). In my tests, forward slashes are correctly * trapped, but backslashes are not. The code below substitutes forward * slashes for backslashes before attempting to validate that there are no * unauthorized paths being accessed. */ token = gstrchr(path, '\\'); while (token != NULL) { *token = '/'; token = gstrchr(token, '\\'); } token = gstrtok(path, T("/")); /* * Look at each directory segment and process "." and ".." segments * Don't allow the browser to pop outside the root web. */ while (token != NULL) { if (gstrcmp(token, T("..")) == 0) { if (npart > 0) { npart--; } } else if (gstrcmp(token, T(".")) != 0) { parts[npart] = token; len += gstrlen(token) + 1; npart++; } token = gstrtok(NULL, T("/")); } /* * Create local path for document. Need extra space all "/" and null. */ if (npart || (gstrcmp(path, T("/")) == 0) || (path[0] == '\0')) { lpath = balloc(B_L, (gstrlen(dir) + 1 + len + 1) * sizeof(char_t)); gstrcpy(lpath, dir); for (i = 0; i < npart; i++) { gstrcat(lpath, T("/")); gstrcat(lpath, parts[i]); } websSetRequestLpath(wp, lpath); bfree(B_L, path); bfree(B_L, lpath); } else { bfree(B_L, path); return -1; } return 0; }
int websValidateUrl(webs_t wp, char_t *path) { /* Thanks to Dhanwa T ([email protected]) for this fix -- previously, if an URL was requested having more than (the hardcoded) 64 parts, the webServer would experience a hard crash as it attempted to write past the end of the array 'parts'. Also fixes: http://www.securiteam.com/securitynews/5MP0C1580W.html */ char_t *parts[MAX_URL_DEPTH]; /* Array of ptr's to URL parts */ char_t *token, *dir, *lpath; int i, len, npart; a_assert(websValid(wp)); a_assert(path); dir = websGetRequestDir(wp); if (dir == NULL || *dir == '\0') { return -1; } /* * Copy the string so we don't destroy the original */ path = bstrdup(B_L, path); websDecodeUrl(path, path, gstrlen(path)); len = npart = 0; parts[0] = NULL; /* * Fixed by Matt Moore, 22 Jul 02 * http://www.securiteam.com/securitynews/5RP0I007PG.html * http://www.securiteam.com/securitynews/5QP010U3FS.html * * There were reports that a directory traversal exploit was * possible in the WebServer running under Windows if directory paths * outside the server's specified root web were given by URL-encoding the * backslash character, like: * * GoAhead is vulnerable to a directory traversal bug. A request such as * * GoAhead-server/../../../../../../../ results in an error message * 'Cannot open URL'. * However, by encoding the '/' character, it is possible to break out of * the web root and read arbitrary files from the server. * Hence a request like: * * GoAhead-server/..%5C..%5C..%5C..%5C..%5C..%5C/winnt/win.ini returns the * contents of the win.ini file. * (Note that the description uses forward slashes (0x2F), but the example * uses backslashes (0x5C). In my tests, forward slashes are correctly * trapped, but backslashes are not. The code below substitutes forward * slashes for backslashes before attempting to validate that there are no * unauthorized paths being accessed. */ token = gstrchr(path, '\\'); while (token != NULL) { *token = '/'; token = gstrchr(token, '\\'); } token = gstrtok(path, T("/")); /* * Look at each directory segment and process "." and ".." segments * Don't allow the browser to pop outside the root web. */ while (token != NULL) { if (npart >= MAX_URL_DEPTH) { /* * malformed URL -- too many parts for us to process. */ bfree(B_L, path); return -1; } if (gstrcmp(token, T("..")) == 0) { if (npart > 0) { npart--; } } else if (gstrcmp(token, T(".")) != 0) { parts[npart] = token; len += gstrlen(token) + 1; npart++; } token = gstrtok(NULL, T("/")); } #ifdef WIN32 if (isBadWindowsPath(parts, npart)) { bfree(B_L, path); return -1; } #endif /* * Create local path for document. Need extra space all "/" and null. */ if (npart || (gstrcmp(path, T("/")) == 0) || (path[0] == '\0')) { lpath = balloc(B_L, (gstrlen(dir) + 1 + len + 1) * sizeof(char_t)); gstrcpy(lpath, dir); for (i = 0; i < npart; i++) { gstrcat(lpath, T("/")); gstrcat(lpath, parts[i]); } websSetRequestLpath(wp, lpath); bfree(B_L, path); bfree(B_L, lpath); } else { bfree(B_L, path); return -1; } return 0; }
static int processContentData(Webs *wp) { WebsUpload *file; WebsBuf *content; ssize size, nbytes; char *data, *bp; content = &wp->input; file = wp->currentFile; size = bufLen(content); if (size < wp->boundaryLen) { /* Incomplete boundary. Return and get more data */ return 0; } if ((bp = getBoundary(wp, content->servp, size)) == 0) { trace(7, "uploadFilter: Got boundary filename %x", wp->clientFilename); if (wp->clientFilename) { /* No signature found yet. probably more data to come. Must handle split boundaries. */ data = content->servp; nbytes = ((int) (content->endp - data)) - (wp->boundaryLen - 1); if (nbytes > 0 && writeToFile(wp, content->servp, nbytes) < 0) { return -1; } websConsumeInput(wp, nbytes); /* Get more data */ return 0; } } data = content->servp; nbytes = (bp) ? (bp - data) : bufLen(content); if (nbytes > 0) { websConsumeInput(wp, nbytes); /* This is the CRLF before the boundary */ if (nbytes >= 2 && data[nbytes - 2] == '\r' && data[nbytes - 1] == '\n') { nbytes -= 2; } if (wp->clientFilename) { /* Write the last bit of file data and add to the list of files and define environment variables */ if (writeToFile(wp, data, nbytes) < 0) { return -1; } hashEnter(wp->files, wp->uploadVar, valueSymbol(file), 0); defineUploadVars(wp); } else { /* Normal string form data variables */ data[nbytes] = '\0'; trace(5, "uploadFilter: form[%s] = %s", wp->uploadVar, data); websDecodeUrl(wp->uploadVar, wp->uploadVar, -1); websDecodeUrl(data, data, -1); websSetVar(wp, wp->uploadVar, data); } } if (wp->clientFilename) { /* Now have all the data (we've seen the boundary) */ close(wp->upfd); wp->upfd = -1; wp->clientFilename = 0; wfree(wp->uploadTmp); wp->uploadTmp = 0; } wp->uploadState = UPLOAD_BOUNDARY; return 0; }
/* * Process a form request. Returns 1 always to indicate it handled the URL */ int websCgiHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, char_t* query) { cgiRec *cgip; sym_t *s; char_t cgiBuf[FNAMESIZE], *stdIn, *stdOut, cwd[FNAMESIZE]; char_t *cp, *cgiName, *cgiPath, **argp, **envp, **ep; int n, envpsize, argpsize, pHandle, cid; a_assert(websValid(wp)); a_assert(url && *url); a_assert(path && *path == '/'); websStats.cgiHits++; /* * Extract the form name and then build the full path name. The form * name will follow the first '/' in path. */ gstrncpy(cgiBuf, path, TSZ(cgiBuf)); if ((cgiName = gstrchr(&cgiBuf[1], '/')) == NULL) { websError(wp, 200, T("Missing CGI name")); return 1; } cgiName++; if ((cp = gstrchr(cgiName, '/')) != NULL) { *cp = '\0'; } fmtAlloc(&cgiPath, FNAMESIZE, T("%s/%s/%s"), websGetDefaultDir(), CGI_BIN, cgiName); #ifndef VXWORKS /* * See if the file exists and is executable. If not error out. * Don't do this step for VxWorks, since the module may already * be part of the OS image, rather than in the file system. */ { gstat_t sbuf; if (gstat(cgiPath, &sbuf) != 0 || (sbuf.st_mode & S_IFREG) == 0) { websError(wp, 200, T("CGI process file does not exist")); bfree(B_L, cgiPath); return 1; } #if (defined (WIN) || defined (CE)) if (gstrstr(cgiPath, T(".exe")) == NULL && gstrstr(cgiPath, T(".bat")) == NULL) { #elif (defined (NW)) if (gstrstr(cgiPath, T(".nlm")) == NULL) { #else if (gaccess(cgiPath, X_OK) != 0) { #endif /* WIN || CE */ websError(wp, 200, T("CGI process file is not executable")); bfree(B_L, cgiPath); return 1; } } #endif /* ! VXWORKS */ /* * Get the CWD for resetting after launching the child process CGI */ ggetcwd(cwd, FNAMESIZE); /* * Retrieve the directory of the child process CGI */ if ((cp = gstrrchr(cgiPath, '/')) != NULL) { *cp = '\0'; gchdir(cgiPath); *cp = '/'; } /* * Build command line arguments. Only used if there is no non-encoded * = character. This is indicative of a ISINDEX query. POST separators * are & and others are +. argp will point to a balloc'd array of * pointers. Each pointer will point to substring within the * query string. This array of string pointers is how the spawn or * exec routines expect command line arguments to be passed. Since * we don't know ahead of time how many individual items there are in * the query string, the for loop includes logic to grow the array * size via brealloc. */ argpsize = 10; argp = balloc(B_L, argpsize * sizeof(char_t *)); *argp = cgiPath; n = 1; if (gstrchr(query, '=') == NULL) { websDecodeUrl(query, query, gstrlen(query)); for (cp = gstrtok(query, T(" ")); cp != NULL; ) { *(argp+n) = cp; n++; if (n >= argpsize) { argpsize *= 2; argp = brealloc(B_L, argp, argpsize * sizeof(char_t *)); } cp = gstrtok(NULL, T(" ")); } } *(argp+n) = NULL; /* * Add all CGI variables to the environment strings to be passed * to the spawned CGI process. This includes a few we don't * already have in the symbol table, plus all those that are in * the cgiVars symbol table. envp will point to a balloc'd array of * pointers. Each pointer will point to a balloc'd string containing * the keyword value pair in the form keyword=value. Since we don't * know ahead of time how many environment strings there will be the * for loop includes logic to grow the array size via brealloc. */ envpsize = WEBS_SYM_INIT; envp = balloc(B_L, envpsize * sizeof(char_t *)); n = 0; fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("PATH_TRANSLATED"), cgiPath); n++; fmtAlloc(envp+n, FNAMESIZE, T("%s=%s/%s"),T("SCRIPT_NAME"), CGI_BIN, cgiName); n++; fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("REMOTE_USER"), wp->userName); n++; fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("AUTH_TYPE"), wp->authType); n++; for (s = symFirst(wp->cgiVars); s != NULL; s = symNext(wp->cgiVars)) { if (s->content.valid && s->content.type == string && gstrcmp(s->name.value.string, T("REMOTE_HOST")) != 0 && gstrcmp(s->name.value.string, T("HTTP_AUTHORIZATION")) != 0) { fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"), s->name.value.string, s->content.value.string); n++; if (n >= envpsize) { envpsize *= 2; envp = brealloc(B_L, envp, envpsize * sizeof(char_t *)); } } } if (wp->flags & WEBS_CGI_UPLOAD){ // set filename into enviornment variables fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"), T("UPLOAD_FILENAME"), wp->cgiStdin); n++; } *(envp+n) = NULL; /* * Create temporary file name(s) for the child's stdin and stdout. * For POST data the stdin temp file (and name) should already exist. */ if (wp->cgiStdin == NULL) { wp->cgiStdin = websGetCgiCommName(wp); } stdIn = wp->cgiStdin; stdOut = websGetCgiCommName(wp); /* * Now launch the process. If not successful, do the cleanup of resources. * If successful, the cleanup will be done after the process completes. */ if ((pHandle = websLaunchCgiProc(cgiPath, argp, envp, stdIn, stdOut)) == -1) { websError(wp, 200, T("failed to spawn CGI task")); for (ep = envp; *ep != NULL; ep++) { bfreeSafe(B_L, *ep); } bfreeSafe(B_L, cgiPath); bfreeSafe(B_L, argp); bfreeSafe(B_L, envp); bfreeSafe(B_L, stdOut); } else { /* * If the spawn was successful, put this wp on a queue to be * checked for completion. */ cid = hAllocEntry((void***) &cgiList, &cgiMax, sizeof(cgiRec)); cgip = cgiList[cid]; cgip->handle = pHandle; cgip->stdIn = stdIn; cgip->stdOut = stdOut; cgip->cgiPath = cgiPath; cgip->argp = argp; cgip->envp = envp; cgip->wp = wp; cgip->fplacemark = 0; websTimeoutCancel(wp); } /* * Restore the current working directory after spawning child CGI */ gchdir(cwd); return 1; } /******************************************************************************/ /* * Any entry in the cgiList need to be checked to see if it has */ void websCgiGatherOutput (cgiRec *cgip) { gstat_t sbuf; char_t cgiBuf[FNAMESIZE]; if ((gstat(cgip->stdOut, &sbuf) == 0) && (sbuf.st_size > cgip->fplacemark)) { int fdout; fdout = gopen(cgip->stdOut, O_RDONLY | O_BINARY, 0444 ); /* * Check to see if any data is available in the * output file and send its contents to the socket. */ if (fdout >= 0) { webs_t wp = cgip->wp; int nRead; /* * Write the HTTP header on our first pass */ if (cgip->fplacemark == 0) { websWrite(wp, T("HTTP/1.0 200 OK\r\n")); } glseek(fdout, cgip->fplacemark, SEEK_SET); while ((nRead = gread(fdout, cgiBuf, FNAMESIZE)) > 0) { websWriteBlock(wp, cgiBuf, nRead); cgip->fplacemark += nRead; } gclose(fdout); } } } /******************************************************************************/ /* * Any entry in the cgiList need to be checked to see if it has * completed, and if so, process its output and clean up. */ void websCgiCleanup() { cgiRec *cgip; webs_t wp; char_t **ep; int cid, nTries; for (cid = 0; cid < cgiMax; cid++) { if ((cgip = cgiList[cid]) != NULL) { int exit_status; wp = cgip->wp; websCgiGatherOutput (cgip); if ( websCheckCgiProc(cgip->handle, &exit_status) == 0) { /* * We get here if the CGI process has terminated. Clean up. */ nTries = 0; /* * Make sure we didn't miss something during a task switch. * Maximum wait is 100 times 10 msecs (1 second). */ while ((cgip->fplacemark == 0) && (nTries < 100)) { websCgiGatherOutput(cgip); /* * There are some cases when we detect app exit * before the file is ready. */ if (cgip->fplacemark == 0) { #ifdef WIN Sleep(10); #endif /* WIN*/ } nTries++; } if (cgip->fplacemark == 0) { websError(wp, 200, T("CGI generated no output")); } else { websDone(wp, 200); } /* * Remove the temporary re-direction files */ gunlink(cgip->stdIn); gunlink(cgip->stdOut); /* * Free all the memory buffers pointed to by cgip. * The stdin file name (wp->cgiStdin) gets freed as * part of websFree(). */ cgiMax = hFree((void***) &cgiList, cid); for (ep = cgip->envp; ep != NULL && *ep != NULL; ep++) { bfreeSafe(B_L, *ep); } bfreeSafe(B_L, cgip->cgiPath); bfreeSafe(B_L, cgip->argp); bfreeSafe(B_L, cgip->envp); bfreeSafe(B_L, cgip->stdOut); bfreeSafe(B_L, cgip); #if 0 //DAVIDM - we do not want this, netflash does it for us if(wp->has_firmware_upload_clean){ if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) != 0) return; sync(); doSystem("sleep 3 && reboot &"); } #endif } } } }
/* Process a form request. Returns 1 always to indicate it handled the URL */ static bool cgiHandler(Webs *wp) { Cgi *cgip; WebsKey *s; char cgiPrefix[BIT_GOAHEAD_LIMIT_FILENAME], *stdIn, *stdOut, cwd[BIT_GOAHEAD_LIMIT_FILENAME]; char *cp, *cgiName, *cgiPath, **argp, **envp, **ep, *tok, *query, *dir, *extraPath; int n, envpsize, argpsize, pHandle, cid; assert(websValid(wp)); websSetEnv(wp); /* Extract the form name and then build the full path name. The form name will follow the first '/' in path. */ scopy(cgiPrefix, sizeof(cgiPrefix), wp->path); if ((cgiName = strchr(&cgiPrefix[1], '/')) == NULL) { websError(wp, HTTP_CODE_NOT_FOUND, "Missing CGI name"); return 1; } *cgiName++ = '\0'; getcwd(cwd, BIT_GOAHEAD_LIMIT_FILENAME); dir = wp->route->dir ? wp->route->dir : cwd; chdir(dir); extraPath = 0; if ((cp = strchr(cgiName, '/')) != NULL) { extraPath = sclone(cp); *cp = '\0'; websSetVar(wp, "PATH_INFO", extraPath); websSetVarFmt(wp, "PATH_TRANSLATED", "%s%s%s", dir, cgiPrefix, extraPath); wfree(extraPath); } else { websSetVar(wp, "PATH_INFO", ""); websSetVar(wp, "PATH_TRANSLATED", ""); } cgiPath = sfmt("%s%s/%s", dir, cgiPrefix, cgiName); websSetVarFmt(wp, "SCRIPT_NAME", "%s/%s", cgiPrefix, cgiName); websSetVar(wp, "SCRIPT_FILENAME", cgiPath); /* See if the file exists and is executable. If not error out. Don't do this step for VxWorks, since the module may already be part of the OS image, rather than in the file system. */ #if !VXWORKS { WebsStat sbuf; if (stat(cgiPath, &sbuf) != 0 || (sbuf.st_mode & S_IFREG) == 0) { error("Cannot find CGI program: ", cgiPath); websError(wp, HTTP_CODE_NOT_FOUND | WEBS_NOLOG, "CGI program file does not exist"); wfree(cgiPath); return 1; } #if BIT_WIN_LIKE if (strstr(cgiPath, ".exe") == NULL && strstr(cgiPath, ".bat") == NULL) #else if (access(cgiPath, X_OK) != 0) #endif { websError(wp, HTTP_CODE_NOT_FOUND, "CGI process file is not executable"); wfree(cgiPath); return 1; } } #endif /* ! VXWORKS */ /* Build command line arguments. Only used if there is no non-encoded = character. This is indicative of a ISINDEX query. POST separators are & and others are +. argp will point to a walloc'd array of pointers. Each pointer will point to substring within the query string. This array of string pointers is how the spawn or exec routines expect command line arguments to be passed. Since we don't know ahead of time how many individual items there are in the query string, the for loop includes logic to grow the array size via wrealloc. */ argpsize = 10; argp = walloc(argpsize * sizeof(char *)); *argp = cgiPath; n = 1; query = 0; if (strchr(wp->query, '=') == NULL) { query = sclone(wp->query); websDecodeUrl(query, query, strlen(query)); for (cp = stok(query, " ", &tok); cp != NULL; ) { *(argp+n) = cp; trace(5, "ARG[%d] %s", n, argp[n-1]); n++; if (n >= argpsize) { argpsize *= 2; argp = wrealloc(argp, argpsize * sizeof(char *)); } cp = stok(NULL, " ", &tok); } } *(argp+n) = NULL; /* Add all CGI variables to the environment strings to be passed to the spawned CGI process. This includes a few we don't already have in the symbol table, plus all those that are in the vars symbol table. envp will point to a walloc'd array of pointers. Each pointer will point to a walloc'd string containing the keyword value pair in the form keyword=value. Since we don't know ahead of time how many environment strings there will be the for loop includes logic to grow the array size via wrealloc. */ envpsize = 64; envp = walloc(envpsize * sizeof(char*)); for (n = 0, s = hashFirst(wp->vars); s != NULL; s = hashNext(wp->vars, s)) { if (s->content.valid && s->content.type == string && strcmp(s->name.value.string, "REMOTE_HOST") != 0 && strcmp(s->name.value.string, "HTTP_AUTHORIZATION") != 0) { envp[n++] = sfmt("%s=%s", s->name.value.string, s->content.value.string); trace(5, "Env[%d] %s", n, envp[n-1]); if (n >= envpsize) { envpsize *= 2; envp = wrealloc(envp, envpsize * sizeof(char *)); } } } *(envp+n) = NULL; /* Create temporary file name(s) for the child's stdin and stdout. For POST data the stdin temp file (and name) should already exist. */ if (wp->cgiStdin == NULL) { wp->cgiStdin = websGetCgiCommName(); } stdIn = wp->cgiStdin; stdOut = websGetCgiCommName(); /* Now launch the process. If not successful, do the cleanup of resources. If successful, the cleanup will be done after the process completes. */ if ((pHandle = launchCgi(cgiPath, argp, envp, stdIn, stdOut)) == -1) { websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "failed to spawn CGI task"); for (ep = envp; *ep != NULL; ep++) { wfree(*ep); } wfree(cgiPath); wfree(argp); wfree(envp); wfree(stdOut); wfree(query); } else { /* If the spawn was successful, put this wp on a queue to be checked for completion. */ cid = wallocObject(&cgiList, &cgiMax, sizeof(Cgi)); cgip = cgiList[cid]; cgip->handle = pHandle; cgip->stdIn = stdIn; cgip->stdOut = stdOut; cgip->cgiPath = cgiPath; cgip->argp = argp; cgip->envp = envp; cgip->wp = wp; cgip->fplacemark = 0; wfree(query); } /* Restore the current working directory after spawning child CGI */ chdir(cwd); return 1; }
int websUrlParse(char_t *url, char_t **pbuf, char_t **phost, char_t **ppath, char_t **pport, char_t **pquery, char_t **pproto, char_t **ptag, char_t **pext) { char_t *tok, *cp, *host, *path, *port, *proto, *tag, *query, *ext; char_t *hostbuf, *portbuf, *buf; int c, len, ulen; a_assert(url); a_assert(pbuf); ulen = gstrlen(url); /* * We allocate enough to store separate hostname and port number fields. * As there are 3 strings in the one buffer, we need room for 3 null chars. * We allocate MAX_PORT_LEN char_t's for the port number. */ len = ulen * 2 + MAX_PORT_LEN + 3; if ((buf = balloc(B_L, len * sizeof(char_t))) == NULL) { return -1; } portbuf = &buf[len - MAX_PORT_LEN - 1]; hostbuf = &buf[ulen+1]; /* Handle any URL encoding. Otherwise a URL ending in ".as%70", for example, causes trouble. */ websDecodeUrl(buf, url, ulen); url = buf; /* * Convert the current listen port to a string. We use this if the URL has * no explicit port setting */ stritoa(websGetPort(), portbuf, MAX_PORT_LEN); port = portbuf; path = T("/"); proto = T("http"); host = T("localhost"); query = T(""); ext = htmExt; tag = T(""); if (gstrncmp(url, T("http://"), 7) == 0) { tok = &url[7]; tok[-3] = '\0'; proto = url; host = tok; for (cp = tok; *cp; cp++) { if (*cp == '/') { break; } if (*cp == ':') { *cp++ = '\0'; port = cp; tok = cp; } } if ((cp = gstrchr(tok, '/')) != NULL) { /* * If a full URL is supplied, we need to copy the host and port * portions into static buffers. */ c = *cp; *cp = '\0'; gstrncpy(hostbuf, host, ulen); gstrncpy(portbuf, port, MAX_PORT_LEN); *cp = c; host = hostbuf; port = portbuf; path = cp; tok = cp; } } else { path = url; tok = url; } /* * Parse the query string */ if ((cp = gstrchr(tok, '?')) != NULL) { *cp++ = '\0'; query = cp; path = tok; tok = query; } /* * Parse the fragment identifier */ if ((cp = gstrchr(tok, '#')) != NULL) { *cp++ = '\0'; if (*query == 0) { path = tok; } } /* * Only do the following if asked for the extension */ if (pext) { /* Later the path will be cleaned up for trailing slashes and so on. To be ready, we need to clean up here, much as in websValidateUrl. Otherwise a URL ending in "asp/" or "asP" sends Ejscript source to the browser. */ if ((cp = gstrrchr(path, '.')) != NULL) { const char_t* garbage = T("/\\"); int length = gstrcspn(cp, garbage); int garbageLength = gstrspn(cp + length, garbage); int ok = (length + garbageLength == (int) gstrlen(cp)); if (ok) { cp[length] = '\0'; #ifdef WIN strlower(cp); #endif ext = cp; } } } /* * Pass back the fields requested (if not NULL) */ if (phost) *phost = host; if (ppath) *ppath = path; if (pport) *pport = port; if (pproto) *pproto = proto; if (pquery) *pquery = query; if (ptag) *ptag = tag; if (pext) *pext = ext; *pbuf = buf; return 0; }