Beispiel #1
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;
}
Beispiel #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) 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;
}
Beispiel #3
0
static int accept_sub(SLNSessionRef const session, strarg_t const knownURI, HTTPConnectionRef const conn, HTTPHeadersRef const headers) {
	strarg_t const type = HTTPHeadersGet(headers, "Content-Type");
	if(!type) return 415; // Unsupported Media Type

	SLNSubmissionRef sub = NULL;
	int rc = SLNSubmissionCreate(session, knownURI, type, &sub);
	if(rc < 0) goto cleanup;
	for(;;) {
		uv_buf_t buf[1] = {};
		rc = HTTPConnectionReadBody(conn, buf);
		if(rc < 0) goto cleanup;
		if(0 == buf->len) break;
		rc = SLNSubmissionWrite(sub, (byte_t const *)buf->base, buf->len);
		if(rc < 0) goto cleanup;
	}
	rc = SLNSubmissionEnd(sub);
	if(rc < 0) goto cleanup;
	rc = SLNSubmissionStoreBatch(&sub, 1);
	if(rc < 0) goto cleanup;
	strarg_t const location = SLNSubmissionGetPrimaryURI(sub);
	if(!location) rc = UV_ENOMEM;
	if(rc < 0) goto cleanup;

	created(location, conn);

cleanup:
	SLNSubmissionFree(&sub);
	if(UV_EACCES == rc) return 403; // Forbidden
	if(UV_UNKNOWN == rc) return 400; // Bad Request
	if(SLN_HASHMISMATCH == rc) return 409; // Conflict
	if(rc < 0) return 500;
	return 0;
}
Beispiel #4
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;
}
Beispiel #5
0
static int import(SLNPullRef const pull, strarg_t const URI, size_t const pos, HTTPConnectionRef *const conn) {
	if(!pull) return 0;

	// TODO: Even if there's nothing to do, we have to enqueue something to fill up our reserved slots. I guess it's better than doing a lot of work inside the connection lock, but there's got to be a better way.
	SLNSubmissionRef sub = NULL;
	HTTPHeadersRef headers = NULL;

	if(!URI) goto enqueue;

	str_t algo[SLN_ALGO_SIZE];
	str_t hash[SLN_HASH_SIZE];
	if(SLNParseURI(URI, algo, hash) < 0) goto enqueue;

	int rc = SLNSessionGetFileInfo(pull->session, URI, NULL);
	if(rc >= 0) goto enqueue;
	db_assertf(DB_NOTFOUND == rc, "Database error: %s", sln_strerror(rc));

	// TODO: We're logging out of order when we do it like this...
//	alogf("Pulling %s\n", URI);

	if(!*conn) {
		rc = HTTPConnectionCreateOutgoing(pull->host, 0, conn);
		if(rc < 0) {
			alogf("Pull import connection error: %s\n", sln_strerror(rc));
			goto fail;
		}
	}

	str_t *path = aasprintf("/sln/file/%s/%s", algo, hash);
	if(!path) {
		alogf("Pull aasprintf error\n");
		goto fail;
	}
	rc = HTTPConnectionWriteRequest(*conn, HTTP_GET, path, pull->host);
	assert(rc >= 0); // TODO
	FREE(&path);

	HTTPConnectionWriteHeader(*conn, "Cookie", pull->cookie);
	HTTPConnectionBeginBody(*conn);
	rc = HTTPConnectionEnd(*conn);
	if(rc < 0) {
		alogf("Pull import request error: %s\n", sln_strerror(rc));
		goto fail;
	}
	int const status = HTTPConnectionReadResponseStatus(*conn);
	if(status < 0) {
		alogf("Pull import response error: %s\n", sln_strerror(status));
		goto fail;
	}
	if(status < 200 || status >= 300) {
		alogf("Pull import status error: %d\n", status);
		goto fail;
	}

	rc = HTTPHeadersCreateFromConnection(*conn, &headers);
	assert(rc >= 0); // TODO
/*	if(rc < 0) {
		alogf("Pull import headers error %s\n", sln_strerror(rc));
		goto fail;
	}*/
	strarg_t const type = HTTPHeadersGet(headers, "content-type");

	rc = SLNSubmissionCreate(pull->session, URI, &sub);
	if(rc < 0) {
		alogf("Pull submission error: %s\n", sln_strerror(rc));
		goto fail;
	}
	rc = SLNSubmissionSetType(sub, type);
	if(rc < 0) {
		alogf("Pull submission type error: %s\n", sln_strerror(rc));
		goto fail;
	}
	for(;;) {
		if(pull->stop) goto fail;
		uv_buf_t buf[1] = {};
		rc = HTTPConnectionReadBody(*conn, buf);
		if(rc < 0) {
			alogf("Pull download error: %s\n", sln_strerror(rc));
			goto fail;
		}
		if(0 == buf->len) break;
		rc = SLNSubmissionWrite(sub, (byte_t *)buf->base, buf->len);
		if(rc < 0) {
			alogf("Pull write error\n");
			goto fail;
		}
	}
	rc = SLNSubmissionEnd(sub);
	if(rc < 0) {
		alogf("Pull submission error: %s\n", sln_strerror(rc));
		goto fail;
	}

enqueue:
	HTTPHeadersFree(&headers);
	async_mutex_lock(pull->mutex);
	pull->queue[pos] = sub; sub = NULL;
	pull->filled[pos] = true;
	async_cond_broadcast(pull->cond);
	async_mutex_unlock(pull->mutex);
	return 0;

fail:
	HTTPHeadersFree(&headers);
	SLNSubmissionFree(&sub);
	HTTPConnectionFree(conn);
	return -1;
}