/* Get the mimeType function mimeType(): String? */ static EjsString *uri_mimeType(Ejs *ejs, EjsUri *up, int argc, EjsObj **argv) { if (up->uri->ext == 0) { return ESV(null); } return ejsCreateStringFromAsc(ejs, mprLookupMime(NULL, up->uri->ext)); }
/* Write upload data. This routine blocks. If you need non-blocking ... cut and paste. */ ssize httpWriteUploadData(HttpConn *conn, MprList *fileData, MprList *formData) { char *path, *pair, *key, *value, *name; ssize rc; int next; rc = 0; if (formData) { for (rc = next = 0; rc >= 0 && (pair = mprGetNextItem(formData, &next)) != 0; ) { key = stok(sclone(pair), "=", &value); rc += httpWrite(conn->writeq, "%s\r\nContent-Disposition: form-data; name=\"%s\";\r\n", conn->boundary, key); rc += httpWrite(conn->writeq, "Content-Type: application/x-www-form-urlencoded\r\n\r\n%s\r\n", value); } } if (fileData) { for (rc = next = 0; rc >= 0 && (path = mprGetNextItem(fileData, &next)) != 0; ) { name = mprGetPathBase(path); rc += httpWrite(conn->writeq, "%s\r\nContent-Disposition: form-data; name=\"file%d\"; filename=\"%s\"\r\n", conn->boundary, next - 1, name); rc += httpWrite(conn->writeq, "Content-Type: %s\r\n\r\n", mprLookupMime(MPR->mimeTypes, path)); rc += blockingFileCopy(conn, path); rc += httpWrite(conn->writeq, "\r\n"); } } rc += httpWrite(conn->writeq, "%s--\r\n--", conn->boundary); httpFinalize(conn); return rc; }
/* Get a printable version of a buffer. Return a pointer to the start of printable data. This will use the tx or rx mime type if possible. Skips UTF encoding prefixes */ PUBLIC cchar *httpMakePrintable(HttpTrace *trace, HttpConn *conn, cchar *event, cchar *buf, ssize *lenp) { cchar *start, *cp, *digits; char *data, *dp; ssize len; int i; if (conn) { if (smatch(event, "rx.body")) { if (sstarts(mprLookupMime(0, conn->rx->mimeType), "text/")) { return buf; } } else if (smatch(event, "tx.body")) { if (sstarts(mprLookupMime(0, conn->tx->mimeType), "text/")) { return buf; } } } start = buf; len = *lenp; if (len > 3 && start[0] == (char) 0xef && start[1] == (char) 0xbb && start[2] == (char) 0xbf) { /* Step over UTF encoding */ start += 3; *lenp -= 3; } len = min(len, trace->maxContent); for (i = 0; i < len; i++) { if (!isprint((uchar) start[i]) && start[i] != '\n' && start[i] != '\r' && start[i] != '\t') { data = mprAlloc(len * 3 + ((len / 16) + 1) + 1); digits = "0123456789ABCDEF"; for (i = 0, cp = start, dp = data; cp < &start[len]; cp++) { *dp++ = digits[(*cp >> 4) & 0x0f]; *dp++ = digits[*cp & 0x0f]; *dp++ = ' '; if ((++i % 16) == 0) { *dp++ = '\n'; } } *dp++ = '\n'; *dp = '\0'; start = data; *lenp = dp - start; break; } }
/* Write upload data. This routine blocks. If you need non-blocking ... cut and paste. */ PUBLIC ssize httpWriteUploadData(HttpConn *conn, MprList *fileData, MprList *formData) { char *path, *pair, *key, *value, *name; cchar *type; ssize rc; int next; rc = 0; if (formData) { for (rc = next = 0; rc >= 0 && (pair = mprGetNextItem(formData, &next)) != 0; ) { key = ssplit(sclone(pair), "=", &value); rc += httpWrite(conn->writeq, "%s\r\nContent-Disposition: form-data; name=\"%s\";\r\n", conn->boundary, key); rc += httpWrite(conn->writeq, "Content-Type: application/x-www-form-urlencoded\r\n\r\n%s\r\n", value); } } if (fileData) { for (rc = next = 0; rc >= 0 && (path = mprGetNextItem(fileData, &next)) != 0; ) { if (!mprPathExists(path, R_OK)) { httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot open %s", path); return MPR_ERR_CANT_OPEN; } name = mprGetPathBase(path); rc += httpWrite(conn->writeq, "%s\r\nContent-Disposition: form-data; name=\"file%d\"; filename=\"%s\"\r\n", conn->boundary, next - 1, name); if ((type = mprLookupMime(MPR->mimeTypes, path)) != 0) { rc += httpWrite(conn->writeq, "Content-Type: %s\r\n", mprLookupMime(MPR->mimeTypes, path)); } httpWrite(conn->writeq, "\r\n"); if (blockingFileCopy(conn, path) < 0) { return MPR_ERR_CANT_WRITE; } rc += httpWrite(conn->writeq, "\r\n"); } } rc += httpWrite(conn->writeq, "%s--\r\n--", conn->boundary); return rc; }
/* Find a qualifying cache control entry. Any configured uri,method,extension,type must match. */ static HttpCache *lookupCacheControl(HttpConn *conn) { HttpRx *rx; HttpTx *tx; HttpCache *cache; cchar *mimeType, *ukey; int next; rx = conn->rx; tx = conn->tx; /* Find first qualifying cache control entry. Any configured uri,method,extension,type must match. */ for (next = 0; (cache = mprGetNextItem(rx->route->caching, &next)) != 0; ) { if (cache->uris) { if (cache->flags & HTTP_CACHE_ONLY) { ukey = sfmt("%s?%s", rx->pathInfo, httpGetParamsString(conn)); } else { ukey = rx->pathInfo; } if (!mprLookupKey(cache->uris, ukey)) { continue; } } if (cache->methods && !mprLookupKey(cache->methods, rx->method)) { continue; } if (cache->extensions && !mprLookupKey(cache->extensions, tx->ext)) { continue; } if (cache->types) { if ((mimeType = (char*) mprLookupMime(rx->route->mimeTypes, tx->ext)) == 0) { continue; } if (!mprLookupKey(cache->types, mimeType)) { continue; } } /* All match */ break; } return cache; }
static void outputLine(HttpQueue *q, MprDirEntry *ep, cchar *path, int nameSize) { MprPath info; MprTime when; Dir *dir; char *newPath, sizeBuf[16], timeBuf[48], *icon; struct tm tm; bool isDir; int len; cchar *ext, *mimeType; char *dirSuffix; char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; path = mprEscapeHtml(path); dir = q->conn->data; if (ep->size >= (1024 * 1024 * 1024)) { fmtNum(sizeBuf, sizeof(sizeBuf), (int) ep->size, 1024 * 1024 * 1024, "G"); } else if (ep->size >= (1024 * 1024)) { fmtNum(sizeBuf, sizeof(sizeBuf), (int) ep->size, 1024 * 1024, "M"); } else if (ep->size >= 1024) { fmtNum(sizeBuf, sizeof(sizeBuf), (int) ep->size, 1024, "K"); } else { fmt(sizeBuf, sizeof(sizeBuf), "%6d", (int) ep->size); } newPath = mprJoinPath(path, ep->name); if (mprGetPathInfo(newPath, &info) < 0) { when = mprGetTime(); isDir = 0; } else { isDir = info.isDir ? 1 : 0; when = (MprTime) info.mtime * MPR_TICKS_PER_SEC; } if (isDir) { icon = "folder"; dirSuffix = "/"; } else { ext = mprGetPathExt(ep->name); if (ext && (mimeType = mprLookupMime(q->conn->rx->route->mimeTypes, ext)) != 0) { if (strcmp(ext, "es") == 0 || strcmp(ext, "ejs") == 0 || strcmp(ext, "php") == 0) { icon = "text"; } else if (strstr(mimeType, "text") != 0) { icon = "text"; } else { icon = "compressed"; } } else { icon = "compressed"; } dirSuffix = ""; } mprDecodeLocalTime(&tm, when); fmt(timeBuf, sizeof(timeBuf), "%02d-%3s-%4d %02d:%02d", tm.tm_mday, months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour, tm.tm_min); len = (int) strlen(ep->name) + (int) strlen(dirSuffix); if (dir->fancyIndexing == 2) { httpWrite(q, "<tr><td valign=\"top\">"); httpWrite(q, "<img src=\"/icons/%s.gif\" alt=\"[ ]\", /></td>", icon); httpWrite(q, "<td><a href=\"%s%s\">%s%s</a></td>", ep->name, dirSuffix, ep->name, dirSuffix); httpWrite(q, "<td>%s</td><td>%s</td></tr>\r\n", timeBuf, sizeBuf); } else if (dir->fancyIndexing == 1) { httpWrite(q, "<img src=\"/icons/%s.gif\" alt=\"[ ]\", /> ", icon); httpWrite(q, "<a href=\"%s%s\">%s%s</a>%-*s %17s %4s\r\n", ep->name, dirSuffix, ep->name, dirSuffix, nameSize - len, "", timeBuf, sizeBuf); } else { httpWrite(q, "<li><a href=\"%s%s\"> %s%s</a></li>\r\n", ep->name, dirSuffix, ep->name, dirSuffix); } }
PUBLIC int espEmail(HttpConn *conn, cchar *to, cchar *from, cchar *subject, MprTime date, cchar *mime, cchar *message, MprList *files) { MprList *lines; MprCmd *cmd; cchar *body, *boundary, *contents, *encoded, *file; char *out, *err; ssize length; int i, next, status; if (!from || !*from) { from = "anonymous"; } if (!subject || !*subject) { subject = "Mail message"; } if (!mime || !*mime) { mime = "text/plain"; } if (!date) { date = mprGetTime(); } boundary = sjoin("esp.mail=", mprGetMD5("BOUNDARY"), NULL); lines = mprCreateList(0, 0); mprAddItem(lines, sfmt("To: %s", to)); mprAddItem(lines, sfmt("From: %s", from)); mprAddItem(lines, sfmt("Date: %s", mprFormatLocalTime(0, date))); mprAddItem(lines, sfmt("Subject: %s", subject)); mprAddItem(lines, "MIME-Version: 1.0"); mprAddItem(lines, sfmt("Content-Type: multipart/mixed; boundary=%s", boundary)); mprAddItem(lines, ""); boundary = sjoin("--", boundary, NULL); mprAddItem(lines, boundary); mprAddItem(lines, sfmt("Content-Type: %s", mime)); mprAddItem(lines, ""); mprAddItem(lines, ""); mprAddItem(lines, message); for (ITERATE_ITEMS(files, file, next)) { mprAddItem(lines, boundary); if ((mime = mprLookupMime(NULL, file)) == 0) { mime = "application/octet-stream"; } mprAddItem(lines, "Content-Transfer-Encoding: base64"); mprAddItem(lines, sfmt("Content-Disposition: inline; filename=\"%s\"", mprGetPathBase(file))); mprAddItem(lines, sfmt("Content-Type: %s; name=\"%s\"", mime, mprGetPathBase(file))); mprAddItem(lines, ""); contents = mprReadPathContents(file, &length); encoded = mprEncode64Block(contents, length); for (i = 0; i < length; i += 76) { mprAddItem(lines, snclone(&encoded[i], i + 76)); } } mprAddItem(lines, sfmt("%s--", boundary)); body = mprListToString(lines, "\n"); httpTraceContent(conn, "esp.email", "context", body, slen(body), 0); cmd = mprCreateCmd(conn->dispatcher); if (mprRunCmd(cmd, "sendmail -t", NULL, body, &out, &err, -1, 0) < 0) { mprDestroyCmd(cmd); return MPR_ERR_CANT_OPEN; } if (mprWaitForCmd(cmd, ME_ESP_EMAIL_TIMEOUT) < 0) { httpTrace(conn, "esp.email.error", "error", "msg=\"Timeout waiting for command to complete\", timeout=%d, command=\"%s\"", ME_ESP_EMAIL_TIMEOUT, cmd->argv[0]); mprDestroyCmd(cmd); return MPR_ERR_CANT_COMPLETE; } if ((status = mprGetCmdExitStatus(cmd)) != 0) { httpTrace(conn, "esp.email.error", "error", "msg=\"Sendmail failed\", status=%d, error=\"%s\"", status, err); mprDestroyCmd(cmd); return MPR_ERR_CANT_WRITE; } mprDestroyCmd(cmd); return 0; }
/* Set headers for httpWriteHeaders. This defines standard headers. */ static void setHeaders(HttpConn *conn, HttpPacket *packet) { HttpRx *rx; HttpTx *tx; HttpRoute *route; HttpRange *range; MprKeyValue *item; MprOff length; cchar *mimeType; int next; assert(packet->flags == HTTP_PACKET_HEADER); rx = conn->rx; tx = conn->tx; route = rx->route; /* Mandatory headers that must be defined here use httpSetHeader which overwrites existing values. */ httpAddHeaderString(conn, "Date", conn->http->currentDate); if (tx->ext && route) { if ((mimeType = (char*) mprLookupMime(route->mimeTypes, tx->ext)) != 0) { if (conn->error) { httpAddHeaderString(conn, "Content-Type", "text/html"); } else { httpAddHeaderString(conn, "Content-Type", mimeType); } } } if (tx->etag) { httpAddHeader(conn, "ETag", "%s", tx->etag); } length = tx->length > 0 ? tx->length : 0; if (rx->flags & HTTP_HEAD) { conn->tx->flags |= HTTP_TX_NO_BODY; httpDiscardData(conn, HTTP_QUEUE_TX); if (tx->chunkSize <= 0) { httpAddHeader(conn, "Content-Length", "%Ld", length); } } else if (tx->length < 0 && tx->chunkSize > 0) { httpSetHeaderString(conn, "Transfer-Encoding", "chunked"); } else if (conn->endpoint) { /* Server must not emit a content length header for 1XX, 204 and 304 status */ if (!((100 <= tx->status && tx->status <= 199) || tx->status == 204 || tx->status == 304 || tx->flags & HTTP_TX_NO_LENGTH)) { httpAddHeader(conn, "Content-Length", "%Ld", length); } } else if (tx->length > 0) { /* client with body */ httpAddHeader(conn, "Content-Length", "%Ld", length); } if (tx->outputRanges) { if (tx->outputRanges->next == 0) { range = tx->outputRanges; if (tx->entityLength > 0) { httpSetHeader(conn, "Content-Range", "bytes %Ld-%Ld/%Ld", range->start, range->end - 1, tx->entityLength); } else { httpSetHeader(conn, "Content-Range", "bytes %Ld-%Ld/*", range->start, range->end - 1); } } else { httpSetHeader(conn, "Content-Type", "multipart/byteranges; boundary=%s", tx->rangeBoundary); } httpSetHeader(conn, "Accept-Ranges", "bytes"); } if (conn->endpoint) { if (!(route->flags & HTTP_ROUTE_STEALTH)) { httpAddHeaderString(conn, "Server", conn->http->software); } /* If keepAliveCount == 1 */ if (--conn->keepAliveCount > 0) { assert(conn->keepAliveCount >= 1); httpAddHeaderString(conn, "Connection", "Keep-Alive"); httpAddHeader(conn, "Keep-Alive", "timeout=%Ld, max=%d", conn->limits->inactivityTimeout / 1000, conn->keepAliveCount); } else { /* Tell the peer to close the connection */ httpAddHeaderString(conn, "Connection", "close"); } if (route->flags & HTTP_ROUTE_CORS) { setCorsHeaders(conn); } /* Apply response headers */ for (ITERATE_ITEMS(route->headers, item, next)) { if (item->flags == HTTP_ROUTE_ADD_HEADER) { httpAddHeaderString(conn, item->key, item->value); } else if (item->flags == HTTP_ROUTE_APPEND_HEADER) { httpAppendHeaderString(conn, item->key, item->value); } else if (item->flags == HTTP_ROUTE_REMOVE_HEADER) { httpRemoveHeader(conn, item->key); } else if (item->flags == HTTP_ROUTE_SET_HEADER) { httpSetHeaderString(conn, item->key, item->value); } } } }