int SLNSessionGetFileInfo(SLNSessionRef const session, strarg_t const URI, SLNFileInfo *const info) { DB_env *db = NULL; int rc = SLNSessionDBOpen(session, SLN_RDONLY, &db); if(rc < 0) return rc; DB_txn *txn = NULL; rc = db_txn_begin(db, NULL, DB_RDONLY, &txn); if(rc < 0) { SLNSessionDBClose(session, &db); return rc; } DB_cursor *cursor; rc = db_txn_cursor(txn, &cursor); assert(!rc); DB_range fileIDs[1]; SLNURIAndFileIDRange1(fileIDs, txn, URI); DB_val URIAndFileID_key[1]; rc = db_cursor_firstr(cursor, fileIDs, URIAndFileID_key, NULL, +1); DB_val file_val[1]; if(rc >= 0) { strarg_t URI2; uint64_t fileID; SLNURIAndFileIDKeyUnpack(URIAndFileID_key, txn, &URI2, &fileID); assert(0 == strcmp(URI, URI2)); if(info) { DB_val fileID_key[1]; SLNFileByIDKeyPack(fileID_key, txn, fileID); rc = db_get(txn, fileID_key, file_val); } } if(rc < 0) { db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); return rc; } if(info) { // Clear padding for later assert_zeroed. memset(info, 0, sizeof(*info)); strarg_t const internalHash = db_read_string(file_val, txn); strarg_t const type = db_read_string(file_val, txn); uint64_t const size = db_read_uint64(file_val); info->hash = strdup(internalHash); info->path = SLNRepoCopyInternalPath(SLNSessionGetRepo(session), internalHash); info->type = strdup(type); info->size = size; if(!info->hash || !info->path || !info->type) { SLNFileInfoCleanup(info); db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); return DB_ENOMEM; } } db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); return 0; }
int SLNSessionGetNextMetaMapURI(SLNSessionRef const session, strarg_t const targetURI, uint64_t *const metaMapID, str_t *out, size_t const max) { // TODO: We should handle URI synonyms. // That might mean accepting a fileID instead of targetURI... assert(metaMapID); assert(out); uint64_t const sessionID = SLNSessionGetID(session); DB_env *db = NULL; DB_txn *txn = NULL; DB_cursor *cursor = NULL; int rc = 0; size_t count = 0; rc = SLNSessionDBOpen(session, SLN_RDONLY, &db); if(rc < 0) goto cleanup; rc = db_txn_begin(db, NULL, DB_RDONLY, &txn); if(rc < 0) goto cleanup; rc = db_cursor_open(txn, &cursor); if(rc < 0) goto cleanup; DB_range range[1]; DB_val key[1]; SLNTargetURISessionIDAndMetaMapIDRange2(range, txn, targetURI, sessionID); SLNTargetURISessionIDAndMetaMapIDKeyPack(key, txn, targetURI, sessionID, *metaMapID); rc = db_cursor_seekr(cursor, range, key, NULL, +1); if(rc < 0) goto cleanup; strarg_t u; uint64_t s; SLNTargetURISessionIDAndMetaMapIDKeyUnpack(key, txn, &u, &s, metaMapID); DB_val row[1], val[1]; SLNSessionIDAndMetaMapIDToMetaURIAndTargetURIKeyPack(row, txn, sessionID, *metaMapID); rc = db_get(txn, row, val); if(rc < 0) goto cleanup; strarg_t metaURI, t; SLNSessionIDAndMetaMapIDToMetaURIAndTargetURIValUnpack(val, txn, &metaURI, &t); db_assert(metaURI); strlcpy(out, metaURI, max); // TODO: Handle err cleanup: db_cursor_close(cursor); cursor = NULL; db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); if(rc < 0) return rc; return count; }
ssize_t SLNFilterCopyURIs(SLNFilterRef const filter, SLNSessionRef const session, SLNFilterPosition *const pos, int const dir, bool const meta, str_t *URIs[], size_t const max) { assert(URIs); if(!SLNSessionHasPermission(session, SLN_RDONLY)) return DB_EACCES; if(0 == pos->dir) return DB_EINVAL; if(0 == dir) return DB_EINVAL; if(0 == max) return 0; DB_env *db = NULL; DB_txn *txn = NULL; ssize_t rc = 0; rc = SLNSessionDBOpen(session, SLN_RDONLY, &db); if(rc < 0) goto cleanup; rc = db_txn_begin(db, NULL, DB_RDONLY, &txn); if(rc < 0) goto cleanup; rc = SLNFilterPrepare(filter, txn); if(rc < 0) goto cleanup; rc = SLNFilterSeekToPosition(filter, pos, txn); if(rc < 0) goto cleanup; int const stepdir = pos->dir * dir; size_t i = 0; for(; i < max; i++) { size_t const x = stepdir > 0 ? i : max-1-i; rc = SLNFilterGetPosition(filter, pos, txn); if(DB_NOTFOUND == rc) { rc = 0; break; } rc = SLNFilterCopyURI(filter, pos->fileID, meta, txn, &URIs[x]); if(rc < 0) goto cleanup; assert(URIs[x]); SLNFilterStep(filter, pos->dir); } // The results should always be in the first `i` slots, even when // filling them in reverse order. if(stepdir < 0) { memmove(URIs+0, URIs+(max-i), sizeof(*URIs) * i); } assert(rc >= 0); rc = i; cleanup: db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); return rc; }
int SLNSessionCreateSession(SLNSessionRef const session, SLNSessionRef *const out) { assert(out); if(!session) return DB_EACCES; SLNSessionCacheRef const cache = session->cache; uint64_t const userID = session->userID; SLNMode const mode = session->mode; strarg_t const username = session->username; DB_env *db = NULL; DB_txn *txn = NULL; SLNSessionRef alt = NULL; byte_t key_raw[SESSION_KEY_LEN]; int rc = async_random(key_raw, sizeof(key_raw)); if(rc < 0) goto cleanup; byte_t key_enc[SHA256_DIGEST_LENGTH]; SHA256(key_raw, sizeof(key_raw), key_enc); str_t key_str[SESSION_KEY_HEX+1]; tohex(key_str, key_enc, SESSION_KEY_LEN); key_str[SESSION_KEY_HEX] = '\0'; rc = SLNSessionDBOpen(session, SLN_RDWR, &db); // TODO: Custom permission? if(rc < 0) goto cleanup; rc = db_txn_begin(db, NULL, DB_RDWR, &txn); if(rc < 0) goto cleanup; uint64_t const sessionID = db_next_id(SLNSessionByID, txn); DB_val key[1], val[1]; SLNSessionByIDKeyPack(key, txn, sessionID); SLNSessionByIDValPack(val, txn, userID, key_str); rc = db_put(txn, key, val, DB_NOOVERWRITE_FAST); if(rc < 0) goto cleanup; rc = db_txn_commit(txn); txn = NULL; SLNSessionDBClose(session, &db); if(rc < 0) goto cleanup; rc = SLNSessionCreateInternal(cache, sessionID, key_raw, key_enc, userID, mode, username, &alt); if(rc < 0) goto cleanup; *out = alt; alt = NULL; cleanup: db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); SLNSessionRelease(&alt); return rc; }
int SLNSessionCopyLastSubmissionURIs(SLNSessionRef const session, str_t *const outFileURI, str_t *const outMetaURI) { DB_env *db = NULL; DB_txn *txn = NULL; int rc = SLNSessionDBOpen(session, SLN_RDWR, &db); if(rc < 0) goto cleanup; rc = db_txn_begin(db, NULL, DB_RDONLY, &txn); if(rc < 0) goto cleanup; if(outFileURI) { DB_val key[1], val[1]; DB_VAL_STORAGE(key, DB_VARINT_MAX*2); db_bind_uint64(key, SLNLastFileURIBySessionID); db_bind_uint64(key, session->sessionID); DB_VAL_STORAGE_VERIFY(key); rc = db_get(txn, key, val); if(rc >= 0) { strarg_t const URI = db_read_string(val, txn); strlcpy(outFileURI, URI, SLN_URI_MAX); } else if(DB_NOTFOUND == rc) { outFileURI[0] = '\0'; rc = 0; } else { goto cleanup; } } if(outMetaURI) { DB_val key[1], val[1]; DB_VAL_STORAGE(key, DB_VARINT_MAX*2); db_bind_uint64(key, SLNLastMetaURIBySessionID); db_bind_uint64(key, session->sessionID); DB_VAL_STORAGE_VERIFY(key); rc = db_get(txn, key, val); if(rc >= 0) { strarg_t const URI = db_read_string(val, txn); strlcpy(outMetaURI, URI, SLN_URI_MAX); } else if(DB_NOTFOUND == rc) { outMetaURI[0] = '\0'; rc = 0; } else { goto cleanup; } } cleanup: db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); return rc; }
int SLNSessionAddMetaMap(SLNSessionRef const session, strarg_t const metaURI, strarg_t const targetURI) { uint64_t const sessionID = SLNSessionGetID(session); DB_env *db = NULL; DB_txn *txn = NULL; int rc = SLNSessionDBOpen(session, SLN_RDWR, &db); if(rc < 0) goto cleanup; rc = db_txn_begin(db, NULL, DB_RDWR, &txn); if(rc < 0) goto cleanup; uint64_t nextID = SLNNextMetaMapID(txn, sessionID); if(!nextID) rc = DB_EIO; if(rc < 0) goto cleanup; DB_val mainkey[1], mainval[1]; SLNSessionIDAndMetaMapIDToMetaURIAndTargetURIKeyPack(mainkey, txn, sessionID, nextID); SLNSessionIDAndMetaMapIDToMetaURIAndTargetURIValPack(mainval, txn, metaURI, targetURI); rc = db_put(txn, mainkey, mainval, DB_NOOVERWRITE_FAST); if(rc < 0) goto cleanup; DB_val fwdkey[1], fwdval[1]; SLNMetaURIAndSessionIDToMetaMapIDKeyPack(fwdkey, txn, metaURI, sessionID); SLNMetaURIAndSessionIDToMetaMapIDValPack(fwdval, txn, nextID); rc = db_put(txn, fwdkey, fwdval, DB_NOOVERWRITE_FAST); if(rc < 0) goto cleanup; DB_val revkey[1], revval[1]; SLNTargetURISessionIDAndMetaMapIDKeyPack(revkey, txn, targetURI, sessionID, nextID); db_nullval(revval); rc = db_put(txn, revkey, revval, DB_NOOVERWRITE_FAST); if(rc < 0) goto cleanup; rc = db_txn_commit(txn); txn = NULL; cleanup: db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); return rc; }
// TODO static str_t *preview_metadata(preview_state const *const state, strarg_t const var) { int rc; strarg_t unsafe = NULL; str_t buf[URI_MAX]; if(0 == strcmp(var, "rawURI")) { str_t algo[SLN_ALGO_SIZE]; // SLN_INTERNAL_ALGO str_t hash[SLN_HASH_SIZE]; SLNParseURI(state->fileURI, algo, hash); snprintf(buf, sizeof(buf), "/sln/file/%s/%s", algo, hash); unsafe = buf; } if(0 == strcmp(var, "queryURI")) { str_t *escaped = QSEscape(state->fileURI, strlen(state->fileURI), true); snprintf(buf, sizeof(buf), "/?q=%s", escaped); FREE(&escaped); unsafe = buf; } if(0 == strcmp(var, "hashURI")) { unsafe = state->fileURI; } if(0 == strcmp(var, "fileSize")) { // TODO: Really, we should already have this info from when // we got the URI in the first place. SLNFileInfo info[1]; rc = SLNSessionGetFileInfo(state->session, state->fileURI, info); if(rc >= 0) { double const size = info->size; double base = 1.0; strarg_t const units[] = { "B", "KB", "MB", "GB", "TB" }; size_t i = 0; for(; base * 1024.0 <= size && i < numberof(units); i++) base *= 1024.0; if(0 == i) { snprintf(buf, sizeof(buf), "%.0f %s", size/base, units[i]); } else { snprintf(buf, sizeof(buf), "%.1f %s", size/base, units[i]); } unsafe = buf; // P.S. F**k scientific prefixes. } SLNFileInfoCleanup(info); } if(unsafe) return htmlenc(unsafe); str_t value[1024 * 4]; // TODO: Load all vars for the template in one transaction. DB_env *db = NULL; rc = SLNSessionDBOpen(state->session, SLN_RDONLY, &db); if(rc >= 0) { DB_txn *txn = NULL; rc = db_txn_begin(db, NULL, DB_RDONLY, &txn); if(rc >= 0) { rc = SLNSessionGetValueForField(state->session, txn, state->fileURI, var, value, sizeof(value)); if(rc >= 0 && '\0' != value[0]) unsafe = value; db_txn_abort(txn); txn = NULL; } SLNSessionDBClose(state->session, &db); } if(!unsafe) { if(0 == strcmp(var, "thumbnailURI")) unsafe = "/file.png"; if(0 == strcmp(var, "title")) unsafe = "(no title)"; if(0 == strcmp(var, "description")) unsafe = "(no description)"; } return htmlenc(unsafe); }