PUBLIC void httpSetAuthForm(HttpRoute *parent, cchar *loginPage, cchar *loginService, cchar *logoutService, cchar *loggedIn) { HttpAuth *auth; HttpRoute *route; bool secure; secure = 0; auth = parent->auth; auth->loginPage = sclone(loginPage); if (loggedIn) { auth->loggedIn = sclone(loggedIn); } /* Create routes without auth for the loginPage, loginService and logoutService */ if ((route = httpCreateInheritedRoute(parent)) != 0) { if (sstarts(loginPage, "https:///")) { loginPage = &loginPage[8]; secure = 1; } httpSetRoutePattern(route, loginPage, 0); route->auth->type = 0; if (secure) { httpAddRouteCondition(route, "secure", 0, 0); } httpFinalizeRoute(route); } if (loginService && *loginService) { if (sstarts(loginService, "https:///")) { loginService = &loginService[8]; secure = 1; } route = httpCreateActionRoute(parent, loginService, loginServiceProc); httpSetRouteMethods(route, "POST"); route->auth->type = 0; if (secure) { httpAddRouteCondition(route, "secure", 0, 0); } } if (logoutService && *logoutService) { if (sstarts(logoutService, "https://")) { logoutService = &logoutService[8]; secure = 1; } httpSetRouteMethods(route, "POST"); route = httpCreateActionRoute(parent, logoutService, logoutServiceProc); route->auth->type = 0; if (secure) { httpAddRouteCondition(route, "secure", 0, 0); } } }
/* function WebSocket(uri: Uri, protocols = null, options) options = { certificate: Path, verify: Boolean, } */ static EjsWebSocket *wsConstructor(Ejs *ejs, EjsWebSocket *ws, int argc, EjsObj **argv) { EjsAny *certificate; bool verify; assert(ejsIsPot(ejs, ws)); ejsLoadHttpService(ejs); ws->ejs = ejs; verify = 0; ws->uri = httpUriToString(((EjsUri*) argv[0])->uri, 0); if (argc >= 2) { if (ejsIs(ejs, argv[1], Array)) { ws->protocols = sclone((ejsToString(ejs, argv[1]))->value); } else if (ejsIs(ejs, argv[1], String)) { ws->protocols = sclone(((EjsString*) argv[1])->value); } else { ws->protocols = sclone("chat"); } } else { ws->protocols = sclone("chat"); } if (*ws->protocols == '\0') { ejsThrowArgError(ejs, "Bad protocol"); return 0; } if (argc >= 3) { ws->frames = ejsGetPropertyByName(ejs, argv[2], EN("frames")) == ESV(true); verify = ejsGetPropertyByName(ejs, argv[2], EN("verify")) == ESV(true); if ((certificate = ejsGetPropertyByName(ejs, argv[2], EN("certificate"))) != 0) { ws->certFile = ejsToMulti(ejs, argv[0]); } } if ((ws->conn = httpCreateConn(MPR->httpService, NULL, ejs->dispatcher)) == 0) { ejsThrowMemoryError(ejs); return 0; } httpSetAsync(ws->conn, 1); httpPrepClientConn(ws->conn, 0); httpSetConnNotifier(ws->conn, webSocketNotify); httpSetWebSocketProtocols(ws->conn, ws->protocols); httpSetConnContext(ws->conn, ws); if (sstarts(ws->uri, "wss")) { ws->ssl = mprCreateSsl(0); mprVerifySslIssuer(ws->ssl, verify); mprVerifySslPeer(ws->ssl, verify); #if FUTURE if (!hp->caFile) { //MOB - Some define for this. hp->caFile = mprJoinPath(mprGetAppDir(), "http-ca.crt"); } mprSetSslCaFile(hp->ssl, hp->caFile); mprSetSslCaFile(hp->ssl, mprJoinPath(mprGetAppDir(), "http-ca.crt")); #endif } startWebSocketRequest(ejs, ws); return ws; }
/* 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; } }
PUBLIC int maSetPlatform(cchar *platformPath) { MaAppweb *appweb; MprDirEntry *dp; cchar *platform, *dir, *junk, *appwebExe; int next, i, notrace; appweb = MPR->appwebService; notrace = !platformPath; if (!platformPath) { platformPath = appweb->localPlatform; } appweb->platform = appweb->platformDir = 0; platform = mprGetPathBase(platformPath); if (mprPathExists(platformPath, X_OK) && mprIsPathDir(platformPath)) { appweb->platform = platform; appweb->platformDir = sclone(platformPath); } else if (smatch(platform, appweb->localPlatform)) { /* If running inside an appweb source tree, locate the platform directory */ appwebExe = mprJoinPath(mprGetAppDir(), "appweb" BIT_EXE); if (mprPathExists(appwebExe, R_OK)) { appweb->platform = appweb->localPlatform; appweb->platformDir = mprGetPathParent(mprGetAppDir()); } else { /* Check installed appweb */ appwebExe = BIT_VAPP_PREFIX "/bin/appweb" BIT_EXE; if (mprPathExists(appwebExe, R_OK)) { appweb->platform = appweb->localPlatform; appweb->platformDir = sclone(BIT_VAPP_PREFIX); } } } /* Last chance. Search up the tree for a similar platform directory. This permits specifying a partial platform like "vxworks" without architecture and profile. */ if (!appweb->platformDir) { dir = mprGetCurrentPath(); for (i = 0; !mprSamePath(dir, "/") && i < 64; i++) { for (ITERATE_ITEMS(mprGetPathFiles(dir, 0), dp, next)) { if (dp->isDir && sstarts(mprGetPathBase(dp->name), platform)) { appweb->platform = mprGetPathBase(dp->name); appweb->platformDir = mprJoinPath(dir, dp->name); break; } } dir = mprGetPathParent(dir); } }
/* Common controller run for every action invoked This tests if the user is logged in and authenticated. Access to certain pages are permitted without authentication so the user can login */ static void commonController(HttpConn *conn) { cchar *uri; if (!httpLoggedIn(conn)) { uri = getUri(); if (sstarts(uri, "/public/") || smatch(uri, "/user/login") || smatch(uri, "/user/logout")) { return; } httpError(conn, HTTP_CODE_UNAUTHORIZED, "Access Denied. Login required"); } }
PUBLIC cchar *espGetConfig(HttpRoute *route, cchar *key, cchar *defaultValue) { cchar *value; if (sstarts(key, "app.")) { mprLog("warn esp", 0, "Using legacy \"app\" configuration property"); } if ((value = mprGetJson(route->config, key)) != 0) { return value; } return defaultValue; }
static int blendEnv(MprCmd *cmd, cchar **env, int flags) { cchar **ep, *prior; int next; cmd->env = 0; if ((cmd->env = mprCreateList(128, MPR_LIST_STATIC_VALUES | MPR_LIST_STABLE)) == 0) { return MPR_ERR_MEMORY; } #if !VXWORKS /* Add prior environment to the list */ if (!(flags & MPR_CMD_EXACT_ENV)) { for (ep = (cchar**) environ; ep && *ep; ep++) { #if MACOSX if (sstarts(*ep, "DYLD_LIBRARY_PATH=")) { continue; } #endif mprAddItem(cmd->env, *ep); } } #endif /* Add new env keys. Detect and overwrite duplicates */ for (ep = env; ep && *ep; ep++) { prior = 0; for (ITERATE_ITEMS(cmd->env, prior, next)) { if (matchEnvKey(*ep, prior)) { mprSetItem(cmd->env, next - 1, *ep); break; } } if (prior == 0) { mprAddItem(cmd->env, *ep); } } #if ME_WIN_LIKE /* Windows requires a caseless sort with two trailing nulls */ mprSortList(cmd->env, (MprSortProc) sortEnv, 0); #endif mprAddItem(cmd->env, NULL); return 0; }
static HttpRoute *createLoginRoute(HttpRoute *route, cchar *pattern, HttpAction action) { bool secure; secure = 0; if (sstarts(pattern, "https:///")) { pattern = &pattern[8]; secure = 1; } else if (sstarts(pattern, "http:///")) { pattern = &pattern[7]; } if ((route = httpCreateInheritedRoute(route)) != 0) { httpSetRoutePattern(route, sjoin("^", pattern, "$", NULL), 0); if (secure) { httpAddRouteCondition(route, "secure", "https://", HTTP_ROUTE_REDIRECT); } if (action) { route->handler = route->http->actionHandler; httpDefineAction(pattern, action); } httpFinalizeRoute(route); } return route; }
/* Common base run for every request. */ static void commonBase(HttpStream *stream) { cchar *uri; if (!httpIsAuthenticated(stream)) { /* Access to certain pages are permitted without authentication so the user can login and logout. */ uri = getUri(); if (sstarts(uri, "/public/") || smatch(uri, "/user/login") || smatch(uri, "/user/logout")) { return; } feedback("error", "Access Denied. Login required."); redirect("/public/login.esp"); } }
/* Append a header. If already defined, the value is catenated to the pre-existing value after a ", " separator. As per the HTTP/1.1 spec. Except for Set-Cookie which HTTP permits multiple headers but not of the same cookie. Ugh! */ PUBLIC void httpAppendHeader(HttpConn *conn, cchar *key, cchar *fmt, ...) { va_list vargs; MprKey *kp; char *value; cchar *cookie; if (!conn->tx) { return; } assert(key && *key); assert(fmt && *fmt); va_start(vargs, fmt); value = sfmtv(fmt, vargs); va_end(vargs); /* HTTP permits Set-Cookie to have multiple cookies. Other headers must comma separate multiple values. For Set-Cookie, must allow duplicates but not of the same cookie. */ kp = mprLookupKeyEntry(conn->tx->headers, key); if (kp) { if (scaselessmatch(key, "Set-Cookie")) { cookie = stok(sclone(value), "=", NULL); while (kp) { if (scaselessmatch(kp->key, "Set-Cookie")) { if (sstarts(kp->data, cookie)) { kp->data = value; break; } } kp = kp->next; } if (!kp) { mprAddDuplicateKey(conn->tx->headers, key, value); } } else { addHdr(conn, key, sfmt("%s, %s", kp->data, value)); } } else { addHdr(conn, key, value); } }
static void errorRedirect(HttpConn *conn, cchar *uri) { HttpTx *tx; /* If the response has started or it is an external redirect ... do a redirect */ tx = conn->tx; if (sstarts(uri, "http") || tx->flags & HTTP_TX_HEADERS_CREATED) { httpRedirect(conn, HTTP_CODE_MOVED_PERMANENTLY, uri); } else { /* No response started and it is an internal redirect, so we can rerun the request. Set finalized to "cap" any output. processCompletion() in rx.c will rerun the request using the errorDocument. */ tx->errorDocument = uri; tx->finalized = tx->finalizedOutput = tx->finalizedConnector = 1; } }
/* Start the user's default browser */ static int runBrowser(char *page) { PROCESS_INFORMATION procInfo; STARTUPINFO startInfo; char cmdBuf[ME_MAX_BUFFER]; char *path; char *pathArg; int port; port = getAppwebPort(); if (port < 0) { mprError("appweb monitor", "Cannot get Appweb listening port"); return -1; } path = getBrowserPath(ME_MAX_BUFFER); if (path == 0) { mprError("appweb monitor", "Cannot get browser startup command"); return -1; } pathArg = strstr(path, "\"%1\""); if (*page == '/') { page++; } if (sstarts(page, "http")) { fmt(cmdBuf, ME_MAX_BUFFER, "%s %s", path, page); } else if (pathArg == 0) { fmt(cmdBuf, ME_MAX_BUFFER, "%s http://localhost:%d/%s", path, port, page); } else { *pathArg = '\0'; fmt(cmdBuf, ME_MAX_BUFFER, "%s \"http://localhost:%d/%s\"", path, port, page); } mprLog("appweb monitor", 4, "Running %s\n", cmdBuf); memset(&startInfo, 0, sizeof(startInfo)); startInfo.cb = sizeof(startInfo); if (! CreateProcess(0, cmdBuf, 0, 0, FALSE, 0, 0, 0, &startInfo, &procInfo)) { mprError("appweb monitor", "Cannot create process: %s, %d", cmdBuf, mprGetOsError()); return -1; } CloseHandle(procInfo.hProcess); return 0; }
/* Limited expansion of route names. Support ~ and ${app} at the start of the route name */ static cchar *expandRouteName(HttpConn *conn, cchar *routeName) { HttpRoute *route; route = conn->rx->route; if (routeName[0] == '~') { return sjoin(httpGetRouteTop(conn), &routeName[1], NULL); } if (sstarts(routeName, "${app}")) { return sjoin(httpGetRouteTop(conn), &routeName[6], NULL); } #if DEPRECATED || 1 // DEPRECATED in version 6 if (routeName[0] == '|') { assert(routeName[0] != '|'); return sjoin(route->prefix, &routeName[1], NULL); } #endif return routeName; }
/* Internal convenience: Used for incoming and outgoing packets. */ PUBLIC bool httpTraceBody(HttpConn *conn, bool outgoing, HttpPacket *packet, ssize len) { cchar *event, *type; if (!conn) { return 0; } if (len < 0) { len = httpGetPacketLength(packet); } if (outgoing) { if (conn->endpoint) { type = "body"; event = "tx.body.data"; } else { if (sstarts(conn->tx->mimeType, "application/x-www-form-urlencoded")) { type = "form"; event = "tx.body.form"; } else { type = "body"; event = "tx.body.data"; } } } else { if (conn->endpoint) { if (conn->rx->form) { type = "form"; event = "rx.body.form"; } else { type = "body"; event = "rx.body.data"; } } else { type = "body"; event = "rx.body.data"; } } return httpTracePacket(conn, event, type, packet, "length: %zd", len); }
static void testRelPath(MprTestGroup *gp) { char *path, *absPath; path = mprGetRelPath("Makefile", 0); assert(strcmp(path, "Makefile") == 0); path = mprNormalizePath("../a.b"); assert(strcmp(path, "../a.b") == 0); path = mprGetRelPath("/", 0); assert(mprIsPathRel(path)); assert(strncmp(path, "../", 3) == 0); path = mprGetRelPath("//", 0); assert(mprIsPathRel(path)); assert(strncmp(path, "../", 3) == 0); path = mprGetRelPath("/tmp", 0); assert(mprIsPathRel(path)); assert(strncmp(path, "../", 3) == 0); path = mprGetRelPath("/Unknown/someone/junk", 0); assert(mprIsPathRel(path)); assert(strncmp(path, "../", 3) == 0); path = mprGetRelPath("/Users/mob/junk", 0); assert(mprIsPathRel(path)); assert(strncmp(path, "../", 3) == 0); path = mprGetRelPath("/Users/mob/././../mob/junk", 0); assert(mprIsPathRel(path)); assert(strncmp(path, "../", 3) == 0); path = mprGetRelPath(".", 0); assert(strcmp(path, ".") == 0); path = mprGetRelPath("..", 0); assert(strcmp(path, "..") == 0); path = mprGetRelPath("/Users/mob/github/admin", 0); assert(sstarts(path, "..")); path = mprGetRelPath("/Users/mob/git", 0); path = mprGetRelPath("/Users/mob/git/mpr/test", 0); /* Can't really test the result of this */ absPath = mprGetAbsPath("Makefile"); assert(mprIsPathAbs(absPath)); path = mprGetRelPath(absPath, 0); assert(!mprIsPathAbs(path)); assert(strcmp(path, "Makefile") == 0); #if FUTURE // MOB - problem in that we don't know the cwd when testMpr runs // Test relative to an origin out = mprGetAbsPath("../../out"); cwd = mprGetCurrentPath(); assert(smatch(mprGetRelPath(cwd, out), "../src/test")); #endif }