/* Populate a packet with file data. Return the number of bytes read or a negative error code. Will not return with a short read. */ static ssize readFileData(HttpQueue *q, HttpPacket *packet, MprOff pos, ssize size) { HttpConn *conn; HttpTx *tx; ssize nbytes; // int btint = 0; conn = q->conn; tx = conn->tx; if (packet->content == 0 && (packet->content = mprCreateBuf(size, -1)) == 0) { return MPR_ERR_MEMORY; } assure(size <= mprGetBufSpace(packet->content)); mprLog(7, "readFileData size %d, pos %Ld", size, pos); if (pos >= 0) { mprSeekFile(tx->file, SEEK_SET, pos); } if ((nbytes = mprReadFile(tx->file, mprGetBufStart(packet->content), size)) != size) { /* As we may have sent some data already to the client, the only thing we can do is abort and hope the client notices the short data. */ httpError(conn, HTTP_CODE_SERVICE_UNAVAILABLE, "Can't read file %s", tx->filename); return MPR_ERR_CANT_READ; } mprAdjustBufEnd(packet->content, nbytes); packet->esize -= nbytes; assure(packet->esize == 0); return nbytes; }
static int blockingFileCopy(HttpConn *conn, cchar *path) { MprFile *file; char buf[ME_MAX_BUFFER]; ssize bytes, nbytes, offset; file = mprOpenFile(path, O_RDONLY | O_BINARY, 0); if (file == 0) { mprLog("error http client", 0, "Cannot open %s", path); return MPR_ERR_CANT_OPEN; } mprAddRoot(file); while ((bytes = mprReadFile(file, buf, sizeof(buf))) > 0) { offset = 0; while (bytes > 0) { if ((nbytes = httpWriteBlock(conn->writeq, &buf[offset], bytes, HTTP_BLOCK)) < 0) { mprCloseFile(file); mprRemoveRoot(file); return MPR_ERR_CANT_WRITE; } bytes -= nbytes; offset += nbytes; assert(bytes >= 0); } } httpFlushQueue(conn->writeq, HTTP_BLOCK); mprCloseFile(file); mprRemoveRoot(file); return 0; }
PUBLIC int ecOpenFileStream(EcCompiler *cp, cchar *path) { EcFileStream *fs; MprPath info; char *contents; if ((fs = ecCreateStream(cp, sizeof(EcFileStream), path, manageFileStream)) == 0) { return MPR_ERR_MEMORY; } if ((fs->file = mprOpenFile(path, O_RDONLY | O_BINARY, 0666)) == 0) { return MPR_ERR_CANT_OPEN; } if (mprGetPathInfo(path, &info) < 0 || info.size < 0) { mprCloseFile(fs->file); return MPR_ERR_CANT_ACCESS; } if ((contents = mprAlloc((int) info.size + 1)) == 0) { mprCloseFile(fs->file); return MPR_ERR_MEMORY; } if (mprReadFile(fs->file, contents, (int) info.size) != (int) info.size) { mprCloseFile(fs->file); return MPR_ERR_CANT_READ; } contents[info.size] = '\0'; ecSetStreamBuf((EcStream*) fs, contents, (ssize) info.size); mprCloseFile(fs->file); fs->file = 0; return 0; }
static int blockingFileCopy(HttpConn *conn, cchar *path) { MprFile *file; char buf[MPR_BUFSIZE]; ssize bytes, nbytes, offset; int oldMode; file = mprOpenFile(path, O_RDONLY | O_BINARY, 0); if (file == 0) { mprError("Can't open %s", path); return MPR_ERR_CANT_OPEN; } mprAddRoot(file); oldMode = mprSetSocketBlockingMode(conn->sock, 1); while ((bytes = mprReadFile(file, buf, sizeof(buf))) > 0) { offset = 0; while (bytes > 0) { if ((nbytes = httpWriteBlock(conn->writeq, &buf[offset], bytes)) < 0) { mprCloseFile(file); mprRemoveRoot(file); return MPR_ERR_CANT_WRITE; } bytes -= nbytes; offset += nbytes; mprAssert(bytes >= 0); } mprYield(0); } httpFlushQueue(conn->writeq, 1); mprSetSocketBlockingMode(conn->sock, oldMode); mprCloseFile(file); mprRemoveRoot(file); return 0; }
static ssize readFileData(MprXml *xp, void *data, char *buf, ssize size) { assert(xp); assert(data); assert(buf); assert(size > 0); return mprReadFile((MprFile*) data, buf, size); }
/* Event callback. Invoked for incoming web socket messages and other events of interest. We're interested in the WRITABLE event. */ static void output_callback(HttpConn *conn, int event, int arg) { Output *output; ssize len, wrote; int flags, type; char buf[MPR_BUFSIZE]; /* Get a writable event when the socket can absorb more data */ if (event == HTTP_EVENT_WRITABLE) { output = getData(); do { if ((len = mprReadFile(output->file, buf, sizeof(buf))) > 0) { /* Set the HTTP_MORE flag on every write except the last. This means each write is sent as a separate frame. The first frame has the type of WS_MSG_TEXT, all others must be continuation frames. */ flags = HTTP_NON_BLOCK; if ((output->written + len) < output->info.size) { flags |= HTTP_MORE; } type = output->written == 0 ? WS_MSG_TEXT : WS_MSG_CONT; /* Send the next chunk as a WebSockets frame using a non-blocking write. This may return having written only a portion of the requested data. */ if ((wrote = httpSendBlock(conn, type, buf, len, flags)) < 0) { httpError(conn, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot send message of %d bytes", len); return; } output->written += wrote; if (wrote < len) { /* Reposition if the send returned having written less than requested */ mprSeekFile(output->file, SEEK_CUR, wrote - len); break; } } else { httpSendClose(conn, WS_STATUS_OK, "OK"); break; } } while (len > 0); } else if (event == HTTP_EVENT_APP_CLOSE) { mprLog(0, "output.c: close event. Status status %d, orderly closed %d, reason %s", arg, httpWebSocketOrderlyClosed(conn), httpGetWebSocketCloseReason(conn)); } else if (event == HTTP_EVENT_ERROR) { mprLog(0, "output.c: error event"); } }
/* Read data as a string function readString(count: Number = -1): String */ static EjsString *readFileString(Ejs *ejs, EjsFile *fp, int argc, EjsObj **argv) { EjsString *result; MprPath info; ssize totalRead; int count; if (argc == 0) { count = -1; } else if (argc != 1) { count = 0; ejsThrowArgError(ejs, "Bad args"); return 0; } else { assert(argc == 1 && ejsIs(ejs, argv[0], Number)); count = ejsGetInt(ejs, argv[0]); } if (fp->file == 0) { ejsThrowStateError(ejs, "File not open"); return 0; } if (!(fp->mode & EJS_FILE_READ)) { ejsThrowStateError(ejs, "File not opened for reading"); return 0; } if (count < 0) { // TODO OPT could this be cached in fp->info if (mprGetPathInfo(fp->path, &info) == 0) { count = (int) info.size; count -= (int) mprGetFilePosition(fp->file); } else { count = ME_MAX_BUFFER; } assert(count >= 0); } if ((result = ejsCreateBareString(ejs, count)) == NULL) { ejsThrowMemoryError(ejs); return 0; } totalRead = mprReadFile(fp->file, result->value, count); if (totalRead != count) { ejsThrowIOError(ejs, "Cannot read from file: %s", fp->path); return 0; } return ejsInternString(result); }
ssize espRenderFile(HttpConn *conn, cchar *path) { MprFile *from; ssize count, written, nbytes; char buf[MPR_BUFSIZE]; if ((from = mprOpenFile(path, O_RDONLY | O_BINARY, 0)) == 0) { return MPR_ERR_CANT_OPEN; } written = 0; while ((count = mprReadFile(from, buf, sizeof(buf))) > 0) { if ((nbytes = espRenderBlock(conn, buf, count)) < 0) { return nbytes; } written += nbytes; } mprCloseFile(from); return written; }
/* Read the specified count of bytes into the byte array. Grow the array if required and growable */ static ssize readData(Ejs *ejs, EjsFile *fp, EjsByteArray *ap, ssize offset, ssize count) { ssize room, bytes; if (count <= 0) { return 0; } room = ap->size - offset; if (room < count) { if (ap->resizable) { ejsGrowByteArray(ejs, ap, ap->size + (count - room)); } else { count = min(room, count); } } bytes = mprReadFile(fp->file, &ap->value[offset], count); if (bytes < 0) { ejsThrowIOError(ejs, "Error reading from %s", fp->path); } return bytes; }
/* compress(src: Path, dest: Path = null) */ static EjsObj *zlib_compress(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv) { MprFile *in; gzFile out; cchar *src, *dest; uchar inbuf[MPR_BUFSIZE]; ssize nbytes; src = ((EjsPath*) argv[0])->value; dest = (argc >= 2) ? ejsToMulti(ejs, argv[1]) : 0; if (!dest) { dest = sjoin(src, ".gz", NULL); } if ((in = mprOpenFile(src, O_RDONLY | O_BINARY, 0)) == 0) { ejsThrowIOError(ejs, "Cannot open from %s", src); return 0; } if ((out = gzopen(dest, "wb")) == 0) { ejsThrowIOError(ejs, "Cannot open destination %s", dest); return 0; } while (1) { if ((nbytes = mprReadFile(in, inbuf, sizeof(inbuf))) < 0) { ejsThrowIOError(ejs, "Cannot read from %s", src); return 0; } else if (nbytes == 0) { break; } if (gzwrite(out, inbuf, (int) nbytes) != nbytes) { ejsThrowIOError(ejs, "Cannot write to %s", dest); return 0; } } mprCloseFile(in); gzclose(out); return 0; }
/* Determine the windows program to invoke. Support UNIX style "#!/program" bang directives on windows. Also supports ".cmd" and ".bat" alternatives. */ static void prepWinProgram(MprCmd *cmd) { #if ME_WIN_LIKE MprFile *file; cchar *bat, *ext, *shell, *cp, *start; char bang[ME_MAX_FNAME + 1], *path, *pp; /* Map separators, convert carriage-returns and newlines to spaces and remove quotes on the command */ path = mprAlloc(slen(cmd->argv[0]) * 2 + 1); strcpy(path, cmd->argv[0]); for (pp = path; *pp; pp++) { if (*pp == '/') { *pp = '\\'; } else if (*pp == '\r' || *pp == '\n') { *pp = ' '; } } if (*path == '\"') { if ((pp = strrchr(++path, '"')) != 0) { *pp = '\0'; } path = sclone(path); } cmd->argv[0] = path; /* Support ".cmd" and ".bat" files that take precedence */ if ((ext = mprGetPathExt(path)) == 0) { if ((bat = mprSearchPath(mprJoinPathExt(path, ".cmd"), MPR_SEARCH_EXE, cmd->searchPath, NULL)) == 0) { bat = mprSearchPath(mprJoinPathExt(path, ".bat"), MPR_SEARCH_EXE, cmd->searchPath, NULL); } if (bat) { if ((shell = getenv("COMSPEC")) == 0) { shell = "cmd.exe"; } cmd->argv = mprRealloc((void*) cmd->argv, (cmd->argc + 4) * sizeof(char*)); memmove((void*) &cmd->argv[3], (void*) cmd->argv, sizeof(char*) * (cmd->argc + 1)); cmd->argv[0] = sclone(shell); cmd->argv[1] = sclone("/Q"); cmd->argv[2] = sclone("/C"); cmd->argv[3] = bat; cmd->argc += 3; cmd->argv[cmd->argc] = 0; return; } } if ((file = mprOpenFile(path, O_RDONLY, 0)) != 0) { if (mprReadFile(file, bang, ME_MAX_FNAME) > 0) { mprCloseFile(file); bang[ME_MAX_FNAME] = '\0'; if (bang[0] == '#' && bang[1] == '!') { cp = start = &bang[2]; shell = ssplit(&bang[2], "\r\n", NULL); if (!mprIsPathAbs(shell)) { /* If we cannot access the command shell and the command is not an absolute path, look in the same directory as the script. */ if (mprPathExists(shell, X_OK)) { shell = mprJoinPath(mprGetPathDir(path), shell); } } /* Get length of argv with NULL and add one */ assert(cmd->argv[cmd->argc] == 0); cmd->argv = mprRealloc((void*) cmd->argv, (cmd->argc + 2) * sizeof(char*)); cmd->argv[cmd->argc + 1] = 0; /* Copy up to make room to insert the shell argument. This copies the original NULL */ memmove((void*) &cmd->argv[1], (void*) cmd->argv, sizeof(char*) * cmd->argc); cmd->argv[0] = sclone(shell); cmd->argv[1] = path; cmd->argc += 1; assert(cmd->argv[cmd->argc] == 0); } } else { mprCloseFile(file); } } #endif }
static ssize writeBody(HttpConn *conn, MprList *files) { MprFile *file; char buf[HTTP_BUFSIZE], *path, *pair; ssize bytes, len, count, nbytes, sofar; int next, oldMode; if (app->upload) { if (httpWriteUploadData(conn, app->files, app->formData) < 0) { mprError("Can't write upload data %s", httpGetError(conn)); return MPR_ERR_CANT_WRITE; } } else { if (app->formData) { count = mprGetListLength(app->formData); for (next = 0; (pair = mprGetNextItem(app->formData, &next)) != 0; ) { len = strlen(pair); if (next < count) { len = slen(pair); if (httpWrite(conn->writeq, pair, len) != len || httpWrite(conn->writeq, "&", 1) != 1) { return MPR_ERR_CANT_WRITE; } } else { if (httpWrite(conn->writeq, pair, len) != len) { return MPR_ERR_CANT_WRITE; } } } } if (files) { mprAssert(mprGetListLength(files) == 1); for (next = 0; (path = mprGetNextItem(files, &next)) != 0; ) { if (strcmp(path, "-") == 0) { file = mprAttachFileFd(0, "stdin", O_RDONLY | O_BINARY); } else { file = mprOpenFile(path, O_RDONLY | O_BINARY, 0); } if (file == 0) { mprError("Can't open \"%s\"", path); return MPR_ERR_CANT_OPEN; } app->inFile = file; if (app->verbose) { mprPrintf("uploading: %s\n", path); } oldMode = mprSetSocketBlockingMode(conn->sock, 1); while ((bytes = mprReadFile(file, buf, sizeof(buf))) > 0) { sofar = 0; while (bytes > 0) { if ((nbytes = httpWriteBlock(conn->writeq, &buf[sofar], bytes)) < 0) { mprCloseFile(file); return MPR_ERR_CANT_WRITE; } bytes -= nbytes; sofar += nbytes; mprAssert(bytes >= 0); } mprYield(0); } httpFlushQueue(conn->writeq, 1); mprSetSocketBlockingMode(conn->sock, oldMode); mprCloseFile(file); app->inFile = 0; } } if (app->bodyData) { mprAddNullToBuf(app->bodyData); len = mprGetBufLength(app->bodyData); if (httpWriteBlock(conn->writeq, mprGetBufStart(app->bodyData), len) != len) { return MPR_ERR_CANT_WRITE; } } } return 0; }
/* If the program has a UNIX style "#!/program" string at the start of the file that program will be selected and the original program will be passed as the first arg to that program with argv[] appended after that. If the program is not found, this routine supports a safe intelligent search for the command. If all else fails, we just return in program the fileName we were passed in. script will be set if we are modifying the program to run and we have extracted the name of the file to run as a script. */ static void findExecutable(HttpConn *conn, char **program, char **script, char **bangScript, cchar *fileName) { HttpRx *rx; HttpTx *tx; HttpRoute *route; MprKey *kp; MprFile *file; cchar *actionProgram, *ext, *cmdShell, *cp, *start, *path; char buf[ME_MAX_FNAME + 1]; rx = conn->rx; tx = conn->tx; route = rx->route; *bangScript = 0; *script = 0; *program = 0; path = 0; actionProgram = mprGetMimeProgram(rx->route->mimeTypes, rx->mimeType); ext = tx->ext; /* If not found, go looking for the fileName with the extensions defined in appweb.conf. NOTE: we don't use PATH deliberately!!! */ if (access(fileName, X_OK) < 0) { for (kp = 0; (kp = mprGetNextKey(route->extensions, kp)) != 0; ) { path = sjoin(fileName, ".", kp->key, NULL); if (access(path, X_OK) == 0) { break; } path = 0; } if (kp) { ext = kp->key; } else { path = fileName; } } else { path = fileName; } assert(path && *path); #if ME_WIN_LIKE if (ext && (strcmp(ext, ".bat") == 0 || strcmp(ext, ".cmd") == 0)) { /* Let a mime action override COMSPEC */ if (actionProgram) { cmdShell = actionProgram; } else { cmdShell = getenv("COMSPEC"); } if (cmdShell == 0) { cmdShell = "cmd.exe"; } *script = sclone(path); *program = sclone(cmdShell); return; } #endif if (actionProgram) { *program = sclone(actionProgram); } else if ((file = mprOpenFile(path, O_RDONLY, 0)) != 0) { if (mprReadFile(file, buf, ME_MAX_FNAME) > 0) { mprCloseFile(file); buf[ME_MAX_FNAME] = '\0'; if (buf[0] == '#' && buf[1] == '!') { cp = start = &buf[2]; cmdShell = ssplit(&buf[2], "\r\n", NULL); if (!mprIsPathAbs(cmdShell)) { /* If we cannot access the command shell and the command is not an absolute path, look in the same directory as the script. */ if (mprPathExists(cmdShell, X_OK)) { cmdShell = mprJoinPath(mprGetPathDir(path), cmdShell); } } *program = sclone(cmdShell); *bangScript = sclone(path); return; } } else { mprCloseFile(file); } } if (actionProgram) { *program = sclone(actionProgram); *bangScript = sclone(path); } else { *program = sclone(path); } return; }
/* Encode the files as C code */ static int binToC(MprList *files, char *romName, char *prefix) { MprPath info; MprFile *file; char buf[512]; char *filename, *cp, *sl, *p; ssize len; int next, i, j; mprPrintf("/*\n %s -- Compiled Files\n */\n", romName); mprPrintf("#include \"mpr.h\"\n\n"); mprPrintf("#if BIT_FEATURE_ROMFS\n"); /* Open each input file and compile */ for (next = 0; (filename = mprGetNextItem(files, &next)) != 0; ) { if (mprGetPathInfo(filename, &info) == 0 && info.isDir) { continue; } if ((file = mprOpenFile(filename, O_RDONLY | O_BINARY, 0666)) < 0) { mprError("Can't open file %s\n", filename); return -1; } mprPrintf("static uchar _file_%d[] = {\n", next); while ((len = mprReadFile(file, buf, sizeof(buf))) > 0) { p = buf; for (i = 0; i < len; ) { mprPrintf(" "); for (j = 0; p < &buf[len] && j < 16; j++, p++) { mprPrintf("%3d,", (unsigned char) *p); } i += j; mprPrintf("\n"); } } mprPrintf(" 0 };\n\n"); mprCloseFile(file); } /* Now output the page index */ mprPrintf("MprRomInode %s[] = {\n", romName); for (next = 0; (filename = mprGetNextItem(files, &next)) != 0; ) { /* Replace the prefix with a leading "/" */ if (strncmp(filename, prefix, strlen(prefix)) == 0) { cp = &filename[strlen(prefix)]; } else { cp = filename; } while((sl = strchr(filename, '\\')) != NULL) { *sl = '/'; } if (*cp == '/') { cp++; } if (*cp == '.' && cp[1] == '\0') { cp++; } if (mprGetPathInfo(filename, &info) == 0 && info.isDir) { mprPrintf(" { \"%s\", 0, 0, 0 },\n", cp); continue; } mprPrintf(" { \"%s\", _file_%d, %d, %d },\n", cp, next, (int) info.size, next); } mprPrintf(" { 0, 0, 0, 0 },\n"); mprPrintf("};\n"); mprPrintf("#endif /* BIT_FEATURE_ROMFS */\n"); return 0; }
static ssize writeBody(HttpConn *conn, MprList *files) { MprFile *file; char buf[ME_MAX_BUFFER], *path, *pair; ssize bytes, len, count, nbytes, sofar; int next; if (app->upload) { if (httpWriteUploadData(conn, app->files, app->formData) < 0) { return MPR_ERR_CANT_WRITE; } } else { if (app->formData) { count = mprGetListLength(app->formData); for (next = 0; (pair = mprGetNextItem(app->formData, &next)) != 0; ) { len = strlen(pair); if (next < count) { len = slen(pair); if (httpWriteString(conn->writeq, pair) != len || httpWriteString(conn->writeq, "&") != 1) { return MPR_ERR_CANT_WRITE; } } else { if (httpWrite(conn->writeq, pair, len) != len) { return MPR_ERR_CANT_WRITE; } } } } if (files) { assert(mprGetListLength(files) == 1); for (next = 0; (path = mprGetNextItem(files, &next)) != 0; ) { if (strcmp(path, "-") == 0) { file = mprAttachFileFd(0, "stdin", O_RDONLY | O_BINARY); } else { file = mprOpenFile(path, O_RDONLY | O_BINARY, 0); } if (file == 0) { mprLog("error http", 0, "Cannot open \"%s\"", path); return MPR_ERR_CANT_OPEN; } app->inFile = file; if (app->verbose) { mprPrintf("uploading: %s\n", path); } while ((bytes = mprReadFile(file, buf, sizeof(buf))) > 0) { sofar = 0; while (bytes > 0) { if ((nbytes = httpWriteBlock(conn->writeq, &buf[sofar], bytes, HTTP_BLOCK)) < 0) { mprCloseFile(file); return MPR_ERR_CANT_WRITE; } bytes -= nbytes; sofar += nbytes; assert(bytes >= 0); } } httpFlushQueue(conn->writeq, HTTP_BLOCK); mprCloseFile(file); app->inFile = 0; } } if (app->bodyData) { len = mprGetBufLength(app->bodyData); if (httpWriteBlock(conn->writeq, mprGetBufStart(app->bodyData), len, HTTP_BLOCK) != len) { return MPR_ERR_CANT_WRITE; } } } return 0; }