void MaDir::setPath(char *fileName) { char buf[MPR_MAX_FNAME]; int len; mprFree(path); mprGetFullPathName(buf, sizeof(buf) - 1, fileName); len = strlen(buf); if (buf[len - 1] != '/') { buf[len] = '/'; buf[++len] = '\0'; } path = mprStrdup(buf); pathLen = strlen(path); #if WIN // // Windows is case insensitive for file names. Always map to lower case. // mprStrLower(path); #endif }
// // Algorithm originally in the GoAhead WebServer. // int MaUrl::parse(char *url) { char *tok, *cp, *portStr, *last_delim, *hostbuf, *portbuf; char *htmlExt = "html"; int c, len, ulen; mprAssert(url && *url); if (parsedUrlBuf) { mprFree(parsedUrlBuf); } ulen = strlen(url); // // Allocate a single buffer to hold all the cracked fields. // Store host, port and url strings (3 nulls). // len = ulen * 2 + MAX_PORT_LEN + 3; parsedUrlBuf = (char*) mprMalloc(len * sizeof(char)); portbuf = &parsedUrlBuf[len - MAX_PORT_LEN - 1]; hostbuf = &parsedUrlBuf[ulen+1]; strcpy(parsedUrlBuf, url); url = parsedUrlBuf; // // Defaults for missing ULR fields // strcpy(portbuf, "80"); portStr = portbuf; uri = "/"; proto = "http"; host = "localhost"; query = ""; ext = htmlExt; if (strncmp(url, "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'; portStr = cp; tok = cp; } } if ((cp = strchr(tok, '/')) != NULL) { c = *cp; *cp = '\0'; mprStrcpy(hostbuf, ulen + 1, host); mprStrcpy(portbuf, MAX_PORT_LEN, portStr); *cp = c; host = hostbuf; portStr = portbuf; uri = cp; tok = cp; } } else { uri = url; tok = url; } // // Split off the query string. // if ((cp = strchr(tok, '?')) != NULL) { *cp++ = '\0'; query = cp; uri = tok; tok = query; } // // Split off fragment identifier. // if ((cp = strchr(tok, '#')) != NULL) { *cp++ = '\0'; if (*query == 0) { uri = tok; } } // // FUTURE -- this logic could be improved // if ((cp = strrchr(uri, '.')) != NULL) { if ((last_delim = strrchr(uri, '/')) != NULL) { if (last_delim > cp) { ext = htmlExt; } else { ext = cp + 1; #if WIN mprStrLower(ext); #endif } } else { ext = cp + 1; #if WIN mprStrLower(ext); #endif } } else { if (uri[strlen(uri) - 1] == '/') { ext = htmlExt; } } port = atoi(portStr); return 0; }
/* * Parse the request headers. Return true if the header parsed. */ static bool parseHeaders(MaConn *conn, MaPacket *packet) { MaHostAddress *address; MaRequest *req; MaHost *host, *hp; MaLimits *limits; MprBuf *content; char keyBuf[MPR_MAX_STRING]; char *key, *value, *cp, *tok; int count, keepAlive; req = conn->request; host = req->host; content = packet->content; conn->request->headerPacket = packet; limits = &conn->http->limits; keepAlive = 0; strcpy(keyBuf, "HTTP_"); mprAssert(strstr((char*) content->start, "\r\n")); for (count = 0; content->start[0] != '\r' && !conn->connectionFailed; count++) { if (count >= limits->maxNumHeaders) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Too many headers"); return 0; } if ((key = getToken(conn, ":")) == 0 || *key == '\0') { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad header format"); return 0; } value = getToken(conn, "\r\n"); while (isspace((int) *value)) { value++; } if (conn->requestFailed) { continue; } mprStrUpper(key); for (cp = key; *cp; cp++) { if (*cp == '-') { *cp = '_'; } } mprLog(req, 8, "Key %s, value %s", key, value); if (strspn(key, "%<>/\\") > 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad header key value"); continue; } /* * Define the header with a "HTTP_" prefix */ mprStrcpy(&keyBuf[5], sizeof(keyBuf) - 5, key); mprAddDuplicateHash(req->headers, keyBuf, value); switch (key[0]) { case 'A': if (strcmp(key, "AUTHORIZATION") == 0) { value = mprStrdup(req, value); req->authType = mprStrTok(value, " \t", &tok); req->authDetails = tok; } else if (strcmp(key, "ACCEPT_CHARSET") == 0) { req->acceptCharset = value; } else if (strcmp(key, "ACCEPT") == 0) { req->accept = value; } else if (strcmp(key, "ACCEPT_ENCODING") == 0) { req->acceptEncoding = value; } break; case 'C': if (strcmp(key, "CONTENT_LENGTH") == 0) { if (req->length >= 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Mulitple content length headers"); continue; } req->length = mprAtoi(value, 10); if (req->length < 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad content length"); continue; } if (req->length >= host->limits->maxBody) { maFailConnection(conn, MPR_HTTP_CODE_REQUEST_TOO_LARGE, "Request content length %Ld is too big. Limit %Ld", req->length, host->limits->maxBody); continue; } mprAssert(req->length >= 0); req->remainingContent = req->length; req->contentLengthStr = value; } else if (strcmp(key, "CONTENT_RANGE") == 0) { /* * This headers specifies the range of any posted body data * Format is: Content-Range: bytes n1-n2/length * Where n1 is first byte pos and n2 is last byte pos */ char *sp; int start, end, size; start = end = size = -1; sp = value; while (*sp && !isdigit((int) *sp)) { sp++; } if (*sp) { start = (int) mprAtoi(sp, 10); if ((sp = strchr(sp, '-')) != 0) { end = (int) mprAtoi(++sp, 10); } if ((sp = strchr(sp, '/')) != 0) { /* * Note this is not the content length transmitted, but the original size of the input of which * the client is transmitting only a portion. */ size = (int) mprAtoi(++sp, 10); } } if (start < 0 || end < 0 || size < 0 || end <= start) { maFailRequest(conn, MPR_HTTP_CODE_RANGE_NOT_SATISFIABLE, "Bad content range"); continue; } req->inputRange = maCreateRange(conn, start, end); } else if (strcmp(key, "CONTENT_TYPE") == 0) { req->mimeType = value; req->form = strstr(value, "application/x-www-form-urlencoded") != 0; } else if (strcmp(key, "COOKIE") == 0) { if (req->cookie && *req->cookie) { req->cookie = mprStrcat(req, -1, req->cookie, "; ", value, NULL); } else { req->cookie = value; } } else if (strcmp(key, "CONNECTION") == 0) { req->connection = value; if (mprStrcmpAnyCase(value, "KEEP-ALIVE") == 0) { keepAlive++; } else if (mprStrcmpAnyCase(value, "CLOSE") == 0) { conn->keepAliveCount = 0; } if (!host->keepAlive) { conn->keepAliveCount = 0; } } break; case 'F': req->forwarded = value; break; case 'H': if (strcmp(key, "HOST") == 0) { req->hostName = value; address = conn->address; if (maIsNamedVirtualHostAddress(address)) { hp = maLookupVirtualHost(address, value); if (hp == 0) { maFailRequest(conn, 404, "No host to serve request. Searching for %s", value); mprLog(conn, 1, "Can't find virtual host %s", value); continue; } req->host = hp; /* * Reassign this request to a new host */ maRemoveConn(host, conn); host = hp; conn->host = hp; maAddConn(hp, conn); } } break; case 'I': if ((strcmp(key, "IF_MODIFIED_SINCE") == 0) || (strcmp(key, "IF_UNMODIFIED_SINCE") == 0)) { MprTime newDate = 0; char *cp; bool ifModified = (key[3] == 'M'); if ((cp = strchr(value, ';')) != 0) { *cp = '\0'; } if (mprParseTime(conn, &newDate, value, MPR_UTC_TIMEZONE, NULL) < 0) { mprAssert(0); break; } if (newDate) { setIfModifiedDate(conn, newDate, ifModified); req->flags |= MA_REQ_IF_MODIFIED; } } else if ((strcmp(key, "IF_MATCH") == 0) || (strcmp(key, "IF_NONE_MATCH") == 0)) { char *word, *tok; bool ifMatch = key[3] == 'M'; if ((tok = strchr(value, ';')) != 0) { *tok = '\0'; } req->ifMatch = ifMatch; req->flags |= MA_REQ_IF_MODIFIED; value = mprStrdup(conn, value); word = mprStrTok(value, " ,", &tok); while (word) { addMatchEtag(conn, word); word = mprStrTok(0, " ,", &tok); } } else if (strcmp(key, "IF_RANGE") == 0) { char *word, *tok; if ((tok = strchr(value, ';')) != 0) { *tok = '\0'; } req->ifMatch = 1; req->flags |= MA_REQ_IF_MODIFIED; value = mprStrdup(conn, value); word = mprStrTok(value, " ,", &tok); while (word) { addMatchEtag(conn, word); word = mprStrTok(0, " ,", &tok); } } break; case 'P': if (strcmp(key, "PRAGMA") == 0) { req->pragma = value; } break; case 'R': if (strcmp(key, "RANGE") == 0) { if (!parseRange(conn, value)) { maFailRequest(conn, MPR_HTTP_CODE_RANGE_NOT_SATISFIABLE, "Bad range"); } } else if (strcmp(key, "REFERER") == 0) { /* NOTE: yes the header is misspelt in the spec */ req->referer = value; } break; case 'T': if (strcmp(key, "TRANSFER_ENCODING") == 0) { mprStrLower(value); if (strcmp(value, "chunked") == 0) { req->flags |= MA_REQ_CHUNKED; /* * This will be revised by the chunk filter as chunks are processed and will be set to zero when the * last chunk has been received. */ req->remainingContent = MAXINT; } } break; #if BLD_DEBUG case 'X': if (strcmp(key, "X_APPWEB_CHUNK_SIZE") == 0) { mprStrUpper(value); conn->response->chunkSize = atoi(value); if (conn->response->chunkSize <= 0) { conn->response->chunkSize = 0; } else if (conn->response->chunkSize > conn->http->limits.maxChunkSize) { conn->response->chunkSize = conn->http->limits.maxChunkSize; } } break; #endif case 'U': if (strcmp(key, "USER_AGENT") == 0) { req->userAgent = value; } break; } } if (conn->protocol == 0 && !keepAlive) { conn->keepAliveCount = 0; } if (!(req->flags & MA_REQ_CHUNKED)) { /* * Step over "\r\n" after headers. As an optimization, don't do this if chunked so chunking can parse a single * chunk delimiter of "\r\nSIZE ...\r\n" */ mprAdjustBufStart(content, 2); } mprLog(conn, 3, "Select host \"%s\"", conn->host->name); if (maSetRequestUri(conn, req->url, "") < 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad URI format"); return 0; } if (conn->host->secure) { req->parsedUri->scheme = mprStrdup(req, "https"); } req->parsedUri->port = conn->sock->port; req->parsedUri->host = req->hostName ? req->hostName : conn->host->name; return 1; }
/* * Parse a complete URI. This accepts full URIs with schemes (http:) and partial URLs */ MprUri *mprParseUri(MprCtx ctx, cchar *uri) { MprUri *up; char *tok, *cp, *last_delim, *hostbuf, *urlTok; int c, len, ulen, http; mprAssert(uri); up = mprAllocObj(ctx, MprUri); if (up == 0) { return 0; } /* * Allocate a single buffer to hold all the cracked fields. */ ulen = (int) strlen(uri); len = ulen * 2 + 3; up->originalUri = mprStrdup(up, uri); up->parsedUriBuf = (char*) mprAlloc(up, len * (int) sizeof(char)); hostbuf = &up->parsedUriBuf[ulen+1]; strcpy(up->parsedUriBuf, uri); urlTok = up->parsedUriBuf; /* * Defaults for missing URL fields */ up->url = "/"; up->scheme = "http"; up->host = "localhost"; up->port = 80; up->query = 0; up->ext = 0; up->secure = 0; up->reference = 0; http = 0; tok = 0; if (strncmp(urlTok, "https://", 8) == 0) { up->secure = 1; up->port = 443; tok = &urlTok[8]; http++; } else if (strncmp(urlTok, "http://", 7) == 0) { tok = &urlTok[7]; http++; } if (http) { up->scheme = urlTok; up->host = tok; tok[-3] = '\0'; for (cp = tok; *cp; cp++) { if (*cp == '/') { break; } if (*cp == ':') { *cp++ = '\0'; up->port = atoi(cp); tok = cp; } } if ((cp = strchr(tok, '/')) != NULL) { c = *cp; *cp = '\0'; mprStrcpy(hostbuf, ulen + 1, up->host); *cp = c; up->host = hostbuf; up->url = cp; tok = cp; } } else { up->url = urlTok; tok = urlTok; } if ((cp = strchr(tok, '#')) != NULL) { *cp++ = '\0'; up->reference = cp; tok = cp; } if ((cp = strchr(tok, '?')) != NULL) { *cp++ = '\0'; up->query = cp; tok = up->query; } if ((cp = strrchr(up->url, '.')) != NULL) { if ((last_delim = strrchr(up->url, '/')) != NULL) { if (last_delim <= cp) { up->ext = cp + 1; #if UNUSED && BLD_WIN_LIKE mprStrLower(up->ext); #endif } } else { up->ext = cp + 1; #if UNUSED && BLD_WIN_LIKE mprStrLower(up->ext); #endif } } else { len = (int) strlen(up->url); } return up; }
/* Parse the CGI output headers. Sample CGI program: Content-type: text/html <html..... */ static bool parseHeader(MaConn *conn, MprCmd *cmd) { MaResponse *resp; MaQueue *q; MprBuf *buf; char *endHeaders, *headers, *key, *value, *location; int fd, len; resp = conn->response; location = 0; value = 0; buf = mprGetCmdBuf(cmd, MPR_CMD_STDOUT); mprAddNullToBuf(buf); headers = mprGetBufStart(buf); /* Split the headers from the body. */ len = 0; fd = mprGetCmdFd(cmd, MPR_CMD_STDOUT); if ((endHeaders = strstr(headers, "\r\n\r\n")) == NULL) { if ((endHeaders = strstr(headers, "\n\n")) == NULL) { if (fd >= 0 && strlen(headers) < MA_MAX_HEADERS) { /* Not EOF and less than max headers and have not yet seen an end of headers delimiter */ return 0; } } else len = 2; } else { len = 4; } if (endHeaders) { endHeaders[len - 1] = '\0'; endHeaders += len; } /* Want to be tolerant of CGI programs that omit the status line. */ if (strncmp((char*) buf->start, "HTTP/1.", 7) == 0) { if (!parseFirstCgiResponse(conn, cmd)) { /* maFailConnection already called */ return 0; } } if (endHeaders && strchr(mprGetBufStart(buf), ':')) { mprLog(conn, 4, "CGI: parseHeader: header\n%s", headers); while (mprGetBufLength(buf) > 0 && buf->start[0] && (buf->start[0] != '\r' && buf->start[0] != '\n')) { if ((key = getCgiToken(buf, ":")) == 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad header format"); return 0; } value = getCgiToken(buf, "\n"); while (isspace((int) *value)) { value++; } len = (int) strlen(value); while (len > 0 && (value[len - 1] == '\r' || value[len - 1] == '\n')) { value[len - 1] = '\0'; len--; } mprStrLower(key); if (strcmp(key, "location") == 0) { location = value; } else if (strcmp(key, "status") == 0) { maSetResponseCode(conn, atoi(value)); } else if (strcmp(key, "content-type") == 0) { maSetResponseMimeType(conn, value); } else if (strcmp(key, "content-length") == 0) { maSetEntityLength(conn, (MprOff) mprAtoi(value, 10)); resp->chunkSize = 0; } else { /* Now pass all other headers back to the client */ maSetHeader(conn, 0, key, "%s", value); } } buf->start = endHeaders; } if (location) { maRedirect(conn, resp->code, location); q = resp->queue[MA_QUEUE_SEND].nextQ; maPutForService(q, maCreateEndPacket(q), 1); } cmd->userFlags |= MA_CGI_SEEN_HEADER; return 1; }
/* * Parse the appweb.conf directives for authorization */ static int parseAuth(MaHttp *http, cchar *key, char *value, MaConfigState *state) { MaServer *server; MaHost *host; MaAuth *auth; MaAcl acl; char *path, *names, *tok, *type, *aclSpec; server = state->server; host = state->host; auth = state->auth; if (mprStrcmpAnyCase(key, "AuthGroupFile") == 0) { path = maMakePath(host, mprStrTrim(value, "\"")); if (maReadGroupFile(server, auth, path) < 0) { mprError(http, "Can't open AuthGroupFile %s", path); return MPR_ERR_BAD_SYNTAX; } mprFree(path); return 1; } else if (mprStrcmpAnyCase(key, "AuthMethod") == 0) { value = mprStrTrim(value, "\""); if (mprStrcmpAnyCase(value, "pam") == 0) { auth->method = MA_AUTH_METHOD_PAM; return 1; } else if (mprStrcmpAnyCase(value, "file") == 0) { auth->method = MA_AUTH_METHOD_FILE; return 1; } else { return MPR_ERR_BAD_SYNTAX; } } else if (mprStrcmpAnyCase(key, "AuthName") == 0) { maSetAuthRealm(auth, mprStrTrim(value, "\"")); return 1; } else if (mprStrcmpAnyCase(key, "AuthType") == 0) { value = mprStrTrim(value, "\""); if (mprStrcmpAnyCase(value, "Basic") == 0) { auth->type = MA_AUTH_BASIC; } else if (mprStrcmpAnyCase(value, "None") == 0) { auth->type = 0; #if BLD_FEATURE_AUTH_DIGEST } else if (mprStrcmpAnyCase(value, "Digest") == 0) { auth->type = MA_AUTH_DIGEST; #endif } else { mprError(http, "Unsupported authorization protocol"); return MPR_ERR_BAD_SYNTAX; } return 1; } else if (mprStrcmpAnyCase(key, "AuthUserFile") == 0) { path = maMakePath(host, mprStrTrim(value, "\"")); if (maReadUserFile(server, auth, path) < 0) { mprError(http, "Can't open AuthUserFile %s", path); return MPR_ERR_BAD_SYNTAX; } mprFree(path); return 1; #if BLD_FEATURE_AUTH_DIGEST } else if (mprStrcmpAnyCase(key, "AuthDigestQop") == 0) { value = mprStrTrim(value, "\""); mprStrLower(value); if (strcmp(value, "none") != 0 && strcmp(value, "auth") != 0 && strcmp(value, "auth-int") != 0) { return MPR_ERR_BAD_SYNTAX; } maSetAuthQop(auth, value); return 1; } else if (mprStrcmpAnyCase(key, "AuthDigestAlgorithm") == 0) { return 1; } else if (mprStrcmpAnyCase(key, "AuthDigestDomain") == 0) { return 1; } else if (mprStrcmpAnyCase(key, "AuthDigestNonceLifetime") == 0) { return 1; #endif } else if (mprStrcmpAnyCase(key, "Require") == 0) { if (maGetConfigValue(http, &type, value, &tok, 1) < 0) { return MPR_ERR_BAD_SYNTAX; } if (mprStrcmpAnyCase(type, "acl") == 0) { aclSpec = mprStrTrim(tok, "\""); acl = maParseAcl(auth, aclSpec); maSetRequiredAcl(auth, acl); } else if (mprStrcmpAnyCase(type, "valid-user") == 0) { maSetAuthAnyValidUser(auth); } else { names = mprStrTrim(tok, "\""); if (mprStrcmpAnyCase(type, "user") == 0) { maSetAuthRequiredUsers(auth, names); } else if (mprStrcmpAnyCase(type, "group") == 0) { maSetAuthRequiredGroups(auth, names); } else { mprError(http, "Bad Require syntax: %s", type); return MPR_ERR_BAD_SYNTAX; } } return 1; } return 0; }
void MaCgiHandler::parseHeader(MaRequest *rq) { MaDataStream *dynBuf; char *endHeaders, *header, *key, *value, *tok; char *cp, *lp, *saveHeader; int len; header = headerBuf->getStart(); endHeaders = strstr(header, "\r\n\r\n"); if (endHeaders == 0) { endHeaders = strstr(header, "\n\n"); if (endHeaders == 0) { endHeaders = strstr(header, "\r\r\n\r\r\n"); if (endHeaders == 0) { return; } } headerBuf->adjustStart(endHeaders + 2 - header); } else { headerBuf->adjustStart(endHeaders + 4 - header); } *endHeaders = '\0'; mprLog(5, log, "%d: parseHeader: header\n%s\n", rq->getFd(), header); if (endHeaders) { lp = header; tok = lp; while (tok && *tok) { for (cp = tok; *cp && *cp != '\r' && *cp != '\n'; cp++) ; len = cp - tok + 1; mprAssert(len >= 0); mprAllocMemcpy(&saveHeader, len, tok, len); saveHeader[len - 1] = '\0'; if ((key = mprStrTok(lp, ": \t\r\n", &tok)) == 0) { break; } lp = 0; if ((value = mprStrTok(0, "\r\n", &tok)) == 0) { rq->requestError(503, "Bad header format"); rq->finishRequest(); mprFree(saveHeader); return; } while (isspace((uchar) *value)) { value++; } mprStrLower(key); if (strcmp(key, "location") == 0) { mprFree(newLocation); newLocation = mprStrdup(value); } else if (strcmp(key, "status") == 0) { rq->setResponseCode(atoi(value)); } else if (strcmp(key, "content-type") == 0) { rq->setResponseMimeType(value); #if OLD } else if ((key[0] == 'x' || key[0] == 'X') && key[1] == '-') { // // Pass "X-Headers" through to the client // rq->setHeader(saveHeader); } #endif } else { // // Now pass all other headers back to the client // rq->setHeader(saveHeader); } mprFree(saveHeader); }
int MaClient::parseHeader(char *line) { char *key, *value, *tok, *tp; mprAssert(line && *line); if ((key = mprStrTok(line, ": \t\n", &tok)) == 0) { formatError("Bad HTTP header"); responseCode = MPR_HTTP_CLIENT_ERROR; finishRequest(1); return MPR_ERR_BAD_STATE; } if ((value = mprStrTok(0, "\n", &tok)) == 0) { value = ""; } while (isspace((uchar) *value)) { value++; } // Upper to be consistent with Request? mprStrLower(key); headerValues->insert(new MprStringHashEntry(key, value)); if (strcmp("www-authenticate", key) == 0) { tp = value; while (*value && !isspace((uchar) *value)) { value++; } *value++ = '\0'; mprStrLower(tp); mprFree(serverAuthType); serverAuthType = mprStrdup(tp); if (parseAuthenticate(value) < 0) { formatError("Bad Authenticate header"); responseCode = MPR_HTTP_CLIENT_ERROR; finishRequest(1); return MPR_ERR_BAD_STATE; } } else if (strcmp("content-length", key) == 0) { contentLength = atoi(value); if (mprStrCmpAnyCase(method, "HEAD") != 0) { contentRemaining = atoi(value); } } else if (strcmp("connection", key) == 0) { mprStrLower(value); if (strcmp(value, "close") == 0) { flags &= ~MPR_HTTP_KEEP_ALIVE; #if BLD_FEATURE_KEEP_ALIVE } else if (strcmp(value, "keep-alive") == 0) { if (userFlags & MPR_HTTP_KEEP_ALIVE) { flags |= MPR_HTTP_KEEP_ALIVE; } #endif } } else if (strcmp("transfer-encoding", key) == 0) { mprStrLower(value); if (strcmp(value, "chunked") == 0) { flags |= MPR_HTTP_INPUT_CHUNKED; } } return 0; }