Example #1
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;
}
Example #2
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;
}
Example #3
0
static int parse_file(BlogRef const blog,
                      SLNSessionRef const session,
                      MultipartFormRef const form,
                      SLNSubmissionRef *const outfile,
                      SLNSubmissionRef *const outmeta,
                      str_t **const outname)
{
	assert(form);

	SLNSubmissionRef file = NULL;
	SLNSubmissionRef meta = NULL;
	SLNFileInfo src[1] = {};
	str_t *htmlpath = NULL;
	int rc = 0;

	strarg_t const fields[] = {
		"content-type",
		"content-disposition",
	};
	char content_type[100];
	char content_disposition[1024];
	uv_buf_t values[] = {
		uv_buf_init(BUF_LEN(content_type)),
		uv_buf_init(BUF_LEN(content_disposition)),
	};
	assert(numberof(fields) == numberof(values));
	rc = MultipartFormReadHeadersStatic(form, values, fields, numberof(values));
	if(rc < 0) goto cleanup;

	strarg_t type;
	if(0 == strcmp("form-data; name=\"markdown\"", content_disposition)) {
		type = "text/markdown; charset=utf-8";
	} else {
		type = content_type;
		static strarg_t const f[] = { "filename", "filename*" };
		str_t *v[numberof(f)] = {};
		ContentDispositionParse(content_disposition, NULL, v, f, numberof(f));
		if(v[1]) {
			*outname = v[1]; v[1] = NULL;
		} else {
			*outname = v[0]; v[0] = NULL;
		}
		for(size_t i = 0; i < numberof(v); i++) FREE(&v[i]);
	}

	rc = SLNSubmissionCreate(session, NULL, NULL, &file);
	if(rc < 0) goto cleanup;
	rc = SLNSubmissionSetType(file, type);
	if(rc < 0) goto cleanup;
	for(;;) {
		uv_buf_t buf[1];
		rc = MultipartFormReadData(form, buf);
		if(rc < 0) goto cleanup;
		if(0 == buf->len) break;
		rc = SLNSubmissionWrite(file, (byte_t const *)buf->base, buf->len);
		if(rc < 0) goto cleanup;
	}
	rc = SLNSubmissionEnd(file);
	if(rc < 0) goto cleanup;

	rc = SLNSubmissionGetFileInfo(file, src);
	if(rc < 0) goto cleanup;

	htmlpath = BlogCopyPreviewPath(blog, src->hash);
	if(!htmlpath) rc = UV_ENOMEM;
	if(rc < 0) goto cleanup;

	strarg_t const URI = SLNSubmissionGetPrimaryURI(file);
	(void)BlogConvert(blog, session, htmlpath, &meta, URI, src);
	// We don't actually care about failure here?
	// Even if no preview and no meta-file can be generated, that's fine.

	*outfile = file; file = NULL;
	*outmeta = meta; meta = NULL;

cleanup:
	SLNSubmissionFree(&file);
	SLNSubmissionFree(&meta);
	SLNFileInfoCleanup(src);
	FREE(&htmlpath);

	return rc;
}