void db_bind_string_len(DB_val *const val, char const *const str, size_t const len, int const nulterm, DB_txn *const txn) { assert(val); assert(len == strnlen(str, len) && "Embedded nuls"); unsigned char *const out = val->data; if(0 == len) { out[val->size++] = '\0'; out[val->size++] = str ? 0x01 : 0x00; return; } if(len < DB_INLINE_MAX) { memcpy(out+val->size, str, len); val->size += len; out[val->size++] = '\0'; if(DB_INLINE_TRUNC != len+1) return; out[val->size++] = '\0'; return; } memcpy(out+val->size, str, DB_INLINE_TRUNC-1); val->size += DB_INLINE_TRUNC-1; out[val->size++] = '\0'; SHA256_CTX algo[1]; int rc; rc = SHA256_Init(algo); db_assert(rc >= 0); rc = SHA256_Update(algo, str, len); db_assert(rc >= 0); rc = SHA256_Final(out+val->size, algo); db_assert(rc >= 0); if(0x00 == out[val->size]) out[val->size] = 0x01; val->size += SHA256_DIGEST_LENGTH; if(!txn) return; unsigned flags = 0; rc = db_txn_get_flags(txn, &flags); db_assertf(rc >= 0, "Database error %s", db_strerror(rc)); if(flags & DB_RDONLY) return; DB_val key = { DB_INLINE_MAX, out+val->size-DB_INLINE_MAX }; char *str2 = nulterm ? (char *)str : strndup(str, len); DB_val full = { len+1, str2 }; assert('\0' == str2[full.size-1]); rc = db_put(txn, &key, &full, 0); if(!nulterm) free(str2); str2 = NULL; db_assertf(rc >= 0, "Database error %s", db_strerror(rc)); }
char const *db_read_string(DB_val *const val, DB_txn *const txn) { assert(txn); assert(val); db_assert(val->size >= 1); char const *const str = val->data; size_t const len = strnlen(str, MIN(val->size, DB_INLINE_MAX)); db_assert('\0' == str[len]); if(0 == len) { db_assert(val->size >= 2); val->data += 2; val->size -= 2; if(0x00 == str[1]) return NULL; if(0x01 == str[1]) return ""; db_assertf(0, "Invalid string type %u\n", str[1]); return NULL; } if(DB_INLINE_TRUNC != len+1) { val->data += len+1; val->size -= len+1; return str; } db_assert(val->size >= len+2); if(0x00 == str[len+1]) { val->data += len+2; val->size -= len+2; return str; } DB_val key = { DB_INLINE_MAX, (char *)str }; DB_val full[1]; int rc = db_get(txn, &key, full); db_assertf(rc >= 0, "Database error %s", db_strerror(rc)); char const *const fstr = full->data; db_assert('\0' == fstr[full->size-1]); return fstr; }
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; }