static int PUT_file(SLNRepoRef const repo, SLNSessionRef const session, HTTPConnectionRef const conn, HTTPMethod const method, strarg_t const URI, HTTPHeadersRef const headers) { // TODO: This is pretty much copy and pasted from above. if(HTTP_PUT != method) return -1; int len = 0; str_t algo[SLN_ALGO_SIZE]; str_t hash[SLN_HASH_SIZE]; algo[0] = '\0'; hash[0] = '\0'; sscanf(URI, "/sln/file/" SLN_ALGO_FMT "/" SLN_HASH_FMT "%n", algo, hash, &len); if(!algo[0] || !hash[0]) return -1; if('\0' != URI[len] && '?' != URI[len]) return -1; str_t *knownURI = SLNFormatURI(algo, hash); if(!knownURI) return 500; int rc = SLNSessionGetFileInfo(session, knownURI, NULL); if(DB_NOTFOUND == rc) { int status = accept_sub(session, knownURI, conn, headers); FREE(&knownURI); return status; } if(rc < 0) goto cleanup; rc = HTTPConnectionDrainMessage(conn); if(rc < 0) goto cleanup; created(knownURI, conn); cleanup: FREE(&knownURI); if(UV_EACCES == rc) return 403; if(UV_ECONNABORTED == rc) return 400; if(rc < 0) return 500; return 0; }
int SLNFilterCopyURI(SLNFilterRef const filter, uint64_t const fileID, bool const meta, DB_txn *const txn, str_t **const out) { DB_val fileID_key[1], file_val[1]; SLNFileByIDKeyPack(fileID_key, txn, fileID); int rc = db_get(txn, fileID_key, file_val); if(rc < 0) return rc; strarg_t const hash = db_read_string(file_val, txn); db_assert(hash); str_t *URI = NULL; if(!meta) { URI = SLNFormatURI(SLN_INTERNAL_ALGO, hash); if(!URI) return DB_ENOMEM; } else { DB_val key[1], val[1]; SLNMetaFileByIDKeyPack(key, txn, fileID); rc = db_get(txn, key, val); if(rc < 0) return rc; uint64_t f; strarg_t target = NULL; SLNMetaFileByIDValUnpack(val, txn, &f, &target); db_assert(target); URI = aasprintf("hash://%s/%s -> %s", SLN_INTERNAL_ALGO, hash, target); if(!URI) return DB_ENOMEM; } *out = URI; URI = NULL; return 0; }
strarg_t SLNSubmissionGetPrimaryURI(SLNSubmissionRef const sub) { if(!sub) return NULL; if(!sub->primaryURI) { if(!sub->internalHash) return NULL; sub->primaryURI = SLNFormatURI(SLN_INTERNAL_ALGO, sub->internalHash); } return sub->primaryURI; }
static int GET_query(BlogRef const blog, SLNSessionRef const session, HTTPConnectionRef const conn, HTTPMethod const method, strarg_t const URI, HTTPHeadersRef const headers) { if(HTTP_GET != method) return -1; strarg_t qs = NULL; if(!URIPath(URI, "/", &qs)) return -1; // TODO: This is the most complicated function in the whole program. // It's unbearable. str_t *query = NULL; str_t *query_HTMLSafe = NULL; SLNFilterRef filter = NULL; int rc; static strarg_t const fields[] = { "q", }; str_t *values[numberof(fields)] = {}; QSValuesParse(qs, values, fields, numberof(fields)); query = values[0] ? strdup(values[0]) : NULL; query_HTMLSafe = htmlenc(values[0]); rc = SLNUserFilterParse(session, values[0], &filter); QSValuesCleanup(values, numberof(values)); if(DB_EACCES == rc) { FREE(&query); FREE(&query_HTMLSafe); return 403; } if(DB_EINVAL == rc) rc = SLNFilterCreate(session, SLNVisibleFilterType, &filter); if(rc < 0) { FREE(&query); FREE(&query_HTMLSafe); return 500; } str_t tmp[URI_MAX]; SLNFilterToUserFilterString(filter, tmp, sizeof(tmp), 0); str_t *parsed_HTMLSafe = htmlenc(tmp); str_t *primaryURI = NULL; SLNFilterRef core = SLNFilterUnwrap(filter); SLNFilterType const filtertype = SLNFilterGetType(core); if(SLNURIFilterType == filtertype) { primaryURI = strdup(SLNFilterGetStringArg(core, 0)); assert(primaryURI); // TODO SLNFilterRef alt; rc = SLNFilterCreate(session, SLNLinksToFilterType, &alt); assert(rc >= 0); // TODO SLNFilterAddStringArg(alt, primaryURI, -1); SLNFilterFree(&filter); filter = alt; alt = NULL; } core = NULL; // SLNFilterPrint(filter, 0); // DEBUG SLNFilterPosition pos[1] = {{ .dir = -1 }}; uint64_t max = RESULTS_MAX; int outdir = -1; SLNFilterParseOptions(qs, pos, &max, &outdir, NULL); if(max < 1) max = 1; if(max > RESULTS_MAX) max = RESULTS_MAX; uint64_t const t1 = uv_hrtime(); str_t *URIs[RESULTS_MAX]; ssize_t const count = SLNFilterCopyURIs(filter, session, pos, outdir, false, URIs, (size_t)max); SLNFilterPositionCleanup(pos); if(count < 0) { fprintf(stderr, "Filter error: %s\n", sln_strerror(count)); FREE(&query); FREE(&query_HTMLSafe); SLNFilterFree(&filter); return 500; } SLNFilterFree(&filter); uint64_t const t2 = uv_hrtime(); str_t *reponame_HTMLSafe = htmlenc(SLNRepoGetName(blog->repo)); snprintf(tmp, sizeof(tmp), "Queried in %.3f seconds", (t2-t1) / 1e9); str_t *querytime_HTMLSafe = htmlenc(tmp); str_t *account_HTMLSafe; if(0 == SLNSessionGetUserID(session)) { account_HTMLSafe = htmlenc("Log In"); } else { strarg_t const user = SLNSessionGetUsername(session); snprintf(tmp, sizeof(tmp), "Account: %s", user); account_HTMLSafe = htmlenc(tmp); } // TODO: Write a real function for building query strings // Don't use ?: GNUism // Preserve other query parameters like `dir` str_t *query_encoded = !query ? NULL : QSEscape(query, strlen(query), true); FREE(&query); str_t *firstpage_HTMLSafe = NULL; str_t *prevpage_HTMLSafe = NULL; str_t *nextpage_HTMLSafe = NULL; str_t *lastpage_HTMLSafe = NULL; snprintf(tmp, sizeof(tmp), "?q=%s&start=-", query_encoded ?: ""); firstpage_HTMLSafe = htmlenc(tmp); str_t *p = !count ? NULL : URIs[outdir > 0 ? 0 : count-1]; str_t *n = !count ? NULL : URIs[outdir > 0 ? count-1 : 0]; if(p) p = QSEscape(p, strlen(p), 1); if(n) n = QSEscape(n, strlen(n), 1); snprintf(tmp, sizeof(tmp), "?q=%s&start=%s", query_encoded ?: "", p ?: ""); prevpage_HTMLSafe = htmlenc(tmp); snprintf(tmp, sizeof(tmp), "?q=%s&start=-%s", query_encoded ?: "", n ?: ""); nextpage_HTMLSafe = htmlenc(tmp); snprintf(tmp, sizeof(tmp), "?q=%s", query_encoded ?: ""); lastpage_HTMLSafe = htmlenc(tmp); FREE(&query_encoded); FREE(&p); FREE(&n); TemplateStaticArg const args[] = { {"reponame", reponame_HTMLSafe}, {"querytime", querytime_HTMLSafe}, {"account", account_HTMLSafe}, {"query", query_HTMLSafe}, {"parsed", parsed_HTMLSafe}, {"firstpage", firstpage_HTMLSafe}, {"prevpage", prevpage_HTMLSafe}, {"nextpage", nextpage_HTMLSafe}, {"lastpage", lastpage_HTMLSafe}, {NULL, NULL}, }; HTTPConnectionWriteResponse(conn, 200, "OK"); HTTPConnectionWriteHeader(conn, "Content-Type", "text/html; charset=utf-8"); HTTPConnectionWriteHeader(conn, "Transfer-Encoding", "chunked"); if(0 == SLNSessionGetUserID(session)) { HTTPConnectionWriteHeader(conn, "Cache-Control", "no-cache, public"); } else { HTTPConnectionWriteHeader(conn, "Cache-Control", "no-cache, private"); } HTTPConnectionBeginBody(conn); TemplateWriteHTTPChunk(blog->header, &TemplateStaticCBs, args, conn); if(primaryURI) { SLNFileInfo info[1]; rc = SLNSessionGetFileInfo(session, primaryURI, info); if(rc >= 0) { str_t *preferredURI = SLNFormatURI(SLN_INTERNAL_ALGO, info->hash); str_t *previewPath = BlogCopyPreviewPath(blog, info->hash); send_preview(blog, conn, session, preferredURI, previewPath); FREE(&preferredURI); FREE(&previewPath); SLNFileInfoCleanup(info); } else if(DB_NOTFOUND == rc) { TemplateWriteHTTPChunk(blog->notfound, &TemplateStaticCBs, args, conn); } if(count) { TemplateWriteHTTPChunk(blog->backlinks, &TemplateStaticCBs, args, conn); } } bool const broadest_possible_filter = SLNVisibleFilterType == filtertype || SLNAllFilterType == filtertype; if(0 == count && !primaryURI && !broadest_possible_filter) { TemplateWriteHTTPChunk(blog->noresults, &TemplateStaticCBs, args, conn); } for(size_t i = 0; i < count; i++) { str_t algo[SLN_ALGO_SIZE]; // SLN_INTERNAL_ALGO str_t hash[SLN_HASH_SIZE]; SLNParseURI(URIs[i], algo, hash); str_t *previewPath = BlogCopyPreviewPath(blog, hash); rc = send_preview(blog, conn, session, URIs[i], previewPath); FREE(&previewPath); if(rc < 0) break; } FREE(&primaryURI); TemplateWriteHTTPChunk(blog->footer, &TemplateStaticCBs, args, conn); FREE(&reponame_HTMLSafe); FREE(&querytime_HTMLSafe); FREE(&account_HTMLSafe); FREE(&query_HTMLSafe); FREE(&parsed_HTMLSafe); FREE(&firstpage_HTMLSafe); FREE(&prevpage_HTMLSafe); FREE(&nextpage_HTMLSafe); FREE(&lastpage_HTMLSafe); HTTPConnectionWriteChunkEnd(conn); HTTPConnectionEnd(conn); for(size_t i = 0; i < count; i++) FREE(&URIs[i]); assert_zeroed(URIs, count); return 0; }