Ejemplo n.º 1
0
int BlogDispatch(BlogRef const blog, SLNSessionRef const session, HTTPConnectionRef const conn, HTTPMethod const method, strarg_t const URI, HTTPHeadersRef const headers) {
	int rc = -1;
	rc = rc >= 0 ? rc : GET_query(blog, session, conn, method, URI, headers);
	rc = rc >= 0 ? rc : GET_compose(blog, session, conn, method, URI, headers);
	rc = rc >= 0 ? rc : GET_upload(blog, session, conn, method, URI, headers);
	rc = rc >= 0 ? rc : POST_post(blog, session, conn, method, URI, headers);
	rc = rc >= 0 ? rc : GET_account(blog, session, conn, method, URI, headers);
	rc = rc >= 0 ? rc : POST_auth(blog, session, conn, method, URI, headers);

	if(403 == rc) {
		HTTPConnectionSendRedirect(conn, 303, "/account");
		return 0;
	}
	if(rc >= 0) return rc; // TODO: Pretty 404 pages, etc.

	if(HTTP_GET != method && HTTP_HEAD != method) return -1;

	str_t path[PATH_MAX];
	size_t const len = strlen(URI);
	if(0 == len) return 400;

	strarg_t const qs = strchr(URI, '?');
	size_t const pathlen = qs ? qs-URI : len;
	str_t *URI_raw = QSUnescape(URI, pathlen, false);
	if('/' == URI[len-1]) {
		str_t const index[] = "index.html";
		rc = snprintf(path, sizeof(path), "%s/static/%s%s", blog->dir, URI_raw, index);
	} else {
		rc = snprintf(path, sizeof(path), "%s/static/%s", blog->dir, URI_raw);
	}
	FREE(&URI_raw);
	if(rc >= sizeof(path)) return 414; // Request-URI Too Large
	if(rc < 0) return 500;

	if(strstr(path, "..")) return 403; // Security critical.

	strarg_t const ext = strrchr(path, '.');
	strarg_t const type = exttype(ext);
	rc = HTTPConnectionSendFile(conn, path, type, -1);
	if(UV_EISDIR == rc) {
		str_t location[URI_MAX];
		rc = snprintf(location, sizeof(location), "%s/", URI);
		if(rc >= sizeof(location)) return 414; // Request-URI Too Large
		if(rc < 0) return 500;
		HTTPConnectionSendRedirect(conn, 301, location);
		return 0;
	}
	if(rc < 0 && UV_EPIPE != rc) {
		alogf("Error sending file %s: %s\n", URI, uv_strerror(rc));
	}

	return 0;
}
Ejemplo n.º 2
0
static int listener0(void *ctx, HTTPServerRef const server, HTTPConnectionRef const conn) {
	HTTPMethod method;
	str_t URI[URI_MAX];
	ssize_t len = HTTPConnectionReadRequest(conn, &method, URI, sizeof(URI));
	if(UV_EOF == len) {
		// HACK: Force the connection to realize it's dead.
		// Otherwise there is a timeout period of like 15-20 seconds
		// and we can run out of file descriptors. I suspect this
		// is a bug with libuv, but I'm not sure.
		HTTPConnectionWrite(conn, (byte_t const *)STR_LEN("x"));
		HTTPConnectionFlush(conn);
		return 0;
	}
	if(UV_EMSGSIZE == len) return 414; // Request-URI Too Large
	if(len < 0) {
		fprintf(stderr, "Request error: %s\n", uv_strerror(len));
		return 500;
	}

	HTTPHeadersRef headers;
	int rc = HTTPHeadersCreateFromConnection(conn, &headers);
	if(UV_EMSGSIZE == rc) return 431; // Request Header Fields Too Large
	if(rc < 0) return 500;

	strarg_t const host = HTTPHeadersGet(headers, "host");
	str_t domain[1023+1]; domain[0] = '\0';
	if(host) sscanf(host, "%1023[^:]", domain);
	// TODO: Verify Host header to prevent DNS rebinding.

	if(SERVER_PORT_TLS && server == server_raw) {
		// Redirect from HTTP to HTTPS
		if('\0' == domain[0]) return 400;
		strarg_t const port = SERVER_PORT_TLS;
		str_t loc[URI_MAX];
		rc = snprintf(loc, sizeof(loc), "https://%s:%s%s", domain, port, URI);
		if(rc >= sizeof(loc)) 414; // Request-URI Too Large
		if(rc < 0) return 500;
		HTTPConnectionSendRedirect(conn, 301, loc);
		return 0;
	}

	strarg_t const cookie = HTTPHeadersGet(headers, "cookie");
	SLNSessionCacheRef const cache = SLNRepoGetSessionCache(repo);
	SLNSessionRef session = NULL;
	rc = SLNSessionCacheCopyActiveSession(cache, cookie, &session);
	if(rc < 0) return 500;
	// Note: null session is valid (zero permissions).

	rc = -1;
	rc = rc >= 0 ? rc : SLNServerDispatch(repo, session, conn, method, URI, headers);
	rc = rc >= 0 ? rc : BlogDispatch(blog, session, conn, method, URI, headers);

	SLNSessionRelease(&session);
	HTTPHeadersFree(&headers);
	return rc;
}
Ejemplo n.º 3
0
static int POST_auth(BlogRef const blog, SLNSessionRef const session, HTTPConnectionRef const conn, HTTPMethod const method, strarg_t const URI, HTTPHeadersRef const headers) {
	if(HTTP_POST != method) return -1;
	if(0 != uripathcmp("/auth", URI, NULL)) return -1;

	// TODO: Check that Content-Type is application/x-www-form-urlencoded.

	str_t formdata[AUTH_FORM_MAX];
	ssize_t len = HTTPConnectionReadBodyStatic(conn, (byte_t *)formdata, sizeof(formdata)-1);
	if(UV_EMSGSIZE == len) return 413; // Request Entity Too Large
	if(len < 0) return 500;
	formdata[len] = '\0';

	SLNSessionCacheRef const cache = SLNRepoGetSessionCache(blog->repo);
	static strarg_t const fields[] = {
		"action-login",
		"action-register",
		"user",
		"pass",
		"token", // TODO: CSRF protection
	};
	str_t *values[numberof(fields)] = {};
	QSValuesParse(formdata, values, fields, numberof(fields));
	if(values[1]) {
		QSValuesCleanup(values, numberof(values));
		return 501; // TODO: Not Implemented
	}
	if(!values[0]) {
		QSValuesCleanup(values, numberof(values));
		return 400; // Not login?
	}
	SLNSessionRef s;
	int rc = SLNSessionCacheCreateSession(cache, values[2], values[3], &s); // TODO
	QSValuesCleanup(values, numberof(values));

	if(rc < 0) {
		HTTPConnectionSendRedirect(conn, 303, "/account?err=1");
		return 0;
	}

	str_t *cookie = SLNSessionCopyCookie(s);
	SLNSessionRelease(&s);
	if(!cookie) return 500;

	HTTPConnectionWriteResponse(conn, 303, "See Other");
	HTTPConnectionWriteHeader(conn, "Location", "/");
	HTTPConnectionWriteSetCookie(conn, cookie, "/", 60 * 60 * 24 * 365);
	HTTPConnectionWriteContentLength(conn, 0);
	HTTPConnectionBeginBody(conn);
	HTTPConnectionEnd(conn);

	FREE(&cookie);
	return 0;
}
Ejemplo n.º 4
0
static int listener0(void *ctx, HTTPServerRef const server, HTTPConnectionRef const conn) {
    HTTPMethod method;
    str_t URI[URI_MAX];
    ssize_t len = HTTPConnectionReadRequest(conn, &method, URI, sizeof(URI));
    if(UV_EOF == len) return 0;
    if(UV_ECONNRESET == len) return 0;
    if(UV_EMSGSIZE == len) return 414; // Request-URI Too Large
    if(len < 0) {
        alogf("Request error: %s\n", uv_strerror(len));
        return 500;
    }

    HTTPHeadersRef headers;
    int rc = HTTPHeadersCreateFromConnection(conn, &headers);
    if(UV_EMSGSIZE == rc) return 431; // Request Header Fields Too Large
    if(rc < 0) return 500;

    strarg_t const host = HTTPHeadersGet(headers, "host");
    str_t domain[1023+1];
    domain[0] = '\0';
    if(host) sscanf(host, "%1023[^:]", domain);
    // TODO: Verify Host header to prevent DNS rebinding.

    if(SERVER_PORT_TLS && server == server_raw) {
        // Redirect from HTTP to HTTPS
        if('\0' == domain[0]) return 400;
        strarg_t const port = SERVER_PORT_TLS;
        str_t loc[URI_MAX];
        rc = snprintf(loc, sizeof(loc), "https://%s:%s%s", domain, port, URI);
        if(rc >= sizeof(loc)) 414; // Request-URI Too Large
        if(rc < 0) return 500;
        HTTPConnectionSendRedirect(conn, 301, loc);
        return 0;
    }

    strarg_t const cookie = HTTPHeadersGet(headers, "cookie");
    SLNSessionCacheRef const cache = SLNRepoGetSessionCache(repo);
    SLNSessionRef session = NULL;
    rc = SLNSessionCacheCopyActiveSession(cache, cookie, &session);
    if(rc < 0) return 500;
    // Note: null session is valid (zero permissions).

    rc = -1;
    rc = rc >= 0 ? rc : SLNServerDispatch(repo, session, conn, method, URI, headers);
    rc = rc >= 0 ? rc : BlogDispatch(blog, session, conn, method, URI, headers);

    SLNSessionRelease(&session);
    HTTPHeadersFree(&headers);
    return rc;
}
Ejemplo n.º 5
0
static int POST_post(BlogRef const blog,
                     SLNSessionRef const session,
                     HTTPConnectionRef const conn,
                     HTTPMethod const method,
                     strarg_t const URI,
                     HTTPHeadersRef const headers)
{
	if(HTTP_POST != method) return -1;
	if(0 != uripathcmp("/post", URI, NULL)) return -1;

	// TODO: CSRF token
	strarg_t const formtype = HTTPHeadersGet(headers, "content-type"); 
	uv_buf_t boundary[1];
	int rc = MultipartBoundaryFromType(formtype, boundary);
	if(rc < 0) return 400;

	MultipartFormRef form = NULL;
	rc = MultipartFormCreate(conn, boundary, &form);
	if(rc < 0) {
		return 500;
	}

	SLNSubmissionRef sub = NULL;
	SLNSubmissionRef meta = NULL;
	str_t *title = NULL;
	rc = parse_file(blog, session, form, &sub, &meta, &title);
	if(UV_EACCES == rc) {
		MultipartFormFree(&form);
		return 403;
	}
	if(rc < 0) {
		MultipartFormFree(&form);
		return 500;
	}


	SLNSubmissionRef extra = NULL;
	yajl_gen json = NULL;
	str_t *target_QSEscaped = NULL;
	str_t *location = NULL;

	strarg_t const target = SLNSubmissionGetPrimaryURI(sub);
	if(!target) rc = UV_ENOMEM;
	if(rc < 0) goto cleanup;
	target_QSEscaped = QSEscape(target, strlen(target), true);
	if(!target_QSEscaped) rc = UV_ENOMEM;
	if(rc < 0) goto cleanup;

	rc = SLNSubmissionCreate(session, NULL, target, &extra);
	if(rc < 0) goto cleanup;
	rc = SLNSubmissionSetType(extra, SLN_META_TYPE);
	if(rc < 0) goto cleanup;

	SLNSubmissionWrite(extra, (byte_t const *)target, strlen(target));
	SLNSubmissionWrite(extra, (byte_t const *)STR_LEN("\n\n"));

	json = yajl_gen_alloc(NULL);
	if(!json) rc = UV_ENOMEM;
	if(rc < 0) goto cleanup;
	yajl_gen_config(json, yajl_gen_print_callback, (void (*)())SLNSubmissionWrite, extra);
	yajl_gen_config(json, yajl_gen_beautify, (int)true);

	yajl_gen_map_open(json);

	if(title) {
		yajl_gen_string(json, (unsigned char const *)STR_LEN("title"));
		yajl_gen_string(json, (unsigned char const *)title, strlen(title));
	}

	// TODO: Comment or description?

	strarg_t const username = SLNSessionGetUsername(session);
	if(username) {
		yajl_gen_string(json, (unsigned char const *)STR_LEN("submitter-name"));
		yajl_gen_string(json, (unsigned char const *)username, strlen(username));
	}

	strarg_t const reponame = SLNRepoGetName(blog->repo);
	if(reponame) {
		yajl_gen_string(json, (unsigned char const *)STR_LEN("submitter-repo"));
		yajl_gen_string(json, (unsigned char const *)reponame, strlen(reponame));
	}

	time_t const now = time(NULL);
	struct tm t[1];
	gmtime_r(&now, t); // TODO: Error checking?
	str_t tstr[31+1];
	size_t const tlen = strftime(tstr, sizeof(tstr), "%FT%TZ", t); // ISO 8601
	if(tlen) {
		yajl_gen_string(json, (unsigned char const *)STR_LEN("submission-time"));
		yajl_gen_string(json, (unsigned char const *)tstr, tlen);
	}

	yajl_gen_string(json, (unsigned char const *)STR_LEN("submission-software"));
	yajl_gen_string(json, (unsigned char const *)STR_LEN("StrongLink Blog"));

	str_t *fulltext = aasprintf("%s\n%s",
		title ?: "",
		NULL ?: ""); // TODO: Description, GNU-ism
	if(fulltext) {
		yajl_gen_string(json, (unsigned char const *)STR_LEN("fulltext"));
		yajl_gen_string(json, (unsigned char const *)fulltext, strlen(fulltext));
	}
	FREE(&fulltext);

	yajl_gen_map_close(json);

	rc = SLNSubmissionEnd(extra);
	if(rc < 0) goto cleanup;


	SLNSubmissionRef subs[] = { sub, meta, extra };
	rc = SLNSubmissionStoreBatch(subs, numberof(subs));

	location = aasprintf("/?q=%s", target_QSEscaped);
	if(!location) rc = UV_ENOMEM;
	if(rc < 0) goto cleanup;

	HTTPConnectionSendRedirect(conn, 303, location);

cleanup:
	if(json) { yajl_gen_free(json); json = NULL; }
	FREE(&title);
	SLNSubmissionFree(&sub);
	SLNSubmissionFree(&meta);
	SLNSubmissionFree(&extra);
	MultipartFormFree(&form);
	FREE(&target_QSEscaped);
	FREE(&location);

	if(rc < 0) return 500;
	return 0;
}