void fcgi_send_file_revisions(void) { const sx_hashfs_volume_t *vol; const sx_hashfs_file_t *file; int comma = 0; rc_ty s; s = sx_hashfs_volume_by_name(hashfs, volume, &vol); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); s = sx_hashfs_revision_first(hashfs, vol, path, &file, 0); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); CGI_PUTS("Content-type: application/json\r\n\r\n{\"fileRevisions\":{"); do { CGI_PRINTF("%s\"%s\":{\"blockSize\":%d,\"fileSize\":", comma ? "," : "", file->revision, file->block_size); CGI_PUTLL(file->file_size); CGI_PRINTF(",\"createdAt\":%u}", file->created_at); comma = 1; s = sx_hashfs_revision_next(hashfs, 0); } while(s == OK); if(s == ITER_NO_MORE) CGI_PUTS("}}"); }
void auth_complete(void) { char content_hash[41]; unsigned char d[20]; unsigned int dlen = 20; if(authed == AUTH_OK) return; if(authed != AUTH_BODYCHECK && authed != AUTH_BODYCHECKING) goto auth_complete_fail; if(!sxi_sha1_final(body_ctx, d, NULL)) quit_errmsg(500, "Failed to initialize crypto engine"); bin2hex(d, sizeof(d), content_hash, sizeof(content_hash)); content_hash[sizeof(content_hash)-1] = '\0'; if(!sxi_hmac_sha1_update_str(hmac_ctx, content_hash)) quit_errmsg(500, "Failed to initialize crypto engine"); if(!sxi_hmac_sha1_final(hmac_ctx, d, &dlen)) quit_errmsg(500, "Failed to initialize crypto engine"); if(!hmac_compare(d, rhmac, sizeof(rhmac))) { authed = AUTH_OK; return; } auth_complete_fail: authed = AUTH_NOTAUTH; }
void fcgi_delete_file(void) { const char *rev = get_arg("rev"); const sx_hashfs_volume_t *vol; rc_ty s; s = sx_hashfs_volume_by_name(hashfs, volume, &vol); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); if(!sx_hashfs_is_or_was_my_volume(hashfs, vol, 0)) quit_errnum(404); if(has_priv(PRIV_CLUSTER)) { /* Request comes in from the cluster: apply locally */ s = sx_hashfs_file_delete(hashfs, vol, path, rev); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); CGI_PUTS("\r\n"); } else { /* Request comes in from the user: create job */ job_t job; s = sx_hashfs_filedelete_job(hashfs, uid, vol, path, rev, &job); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); send_job_info(job); } }
void fcgi_upgrade_2_1_4(void) { rc_ty s; const sx_hashfs_volume_t *vol = NULL; const char *startname, *startrev = NULL; struct rplfiles ctx; if(!has_arg("maxrev")) quit_errmsg(400, "Parameter maxrev is required"); startname = strchr(path, '/'); if(!startname) { s = sx_hashfs_volume_by_name(hashfs, path, &vol); } else { unsigned int vnamelen = startname - path; char *vname = malloc(vnamelen + 1); if(!vname) quit_errmsg(503, "Out of memory"); memcpy(vname, path, vnamelen); vname[vnamelen] = '\0'; s = sx_hashfs_volume_by_name(hashfs, vname, &vol); free(vname); startname++; if(strlen(startname)) startrev = get_arg("startrev"); else startname = NULL; } if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); if(!sx_hashfs_is_or_was_my_volume(hashfs, vol, 0)) quit_errnum(404); ctx.bytes_sent = 0; ctx.b = sx_blob_new(); if(!ctx.b) quit_errmsg(503, "Out of memory"); s = sx_hashfs_file_find(hashfs, vol, startname, startrev, get_arg("maxrev"), upgrade_2_1_4_cb, &ctx); if(s == FAIL_ETOOMANY || s == ITER_NO_MORE) { sx_blob_reset(ctx.b); if(!sx_blob_add_string(ctx.b, "$FILE$") && !sx_blob_add_string(ctx.b, ctx.lastfile.name) && !sx_blob_add_string(ctx.b, ctx.lastfile.revision)) send_rplfiles_header(&ctx); } if(s == ITER_NO_MORE) { sx_blob_reset(ctx.b); if(!sx_blob_add_string(ctx.b, "$THEEND$")) send_rplfiles_header(&ctx); sx_blob_free(ctx.b); return; } sx_blob_free(ctx.b); if(s != FAIL_ETOOMANY && !ctx.bytes_sent) quit_errmsg(rc2http(s), msg_get_reason()); }
void fcgi_create_tempfile(void) { const sx_hashfs_volume_t *vol; rc_ty s = sx_hashfs_putfile_begin(hashfs, uid, volume, path, &vol); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); create_or_extend_tempfile(vol, path, 0); }
void fcgi_flush_tempfile(void) { job_t job; rc_ty s; auth_complete(); quit_unless_authed(); s = sx_hashfs_putfile_commitjob(hashfs, user, uid, path, &job); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); send_job_info(job); return; }
void handle_request(worker_type_t wtype) { const char *param, *p_method, *p_uri; char *argp; unsigned int plen; int cluster_readonly = 0, s2sreq = 0; if(sx_hashfs_cluster_get_mode(hashfs, &cluster_readonly)) { CRIT("Failed to get cluster operating mode"); quit_errmsg(500, "Internal error: failed to check cluster operating mode"); } if(sx_hashfs_distcheck(hashfs) < 0) { CRIT("Failed to reload distribution"); quit_errmsg(503, "Internal error: failed to load distribution"); } if(sx_hashfs_is_orphan(hashfs)) quit_errmsg(410, "This node is no longer a cluster member"); msg_new_id(); verb = VERB_UNSUP; p_method = FCGX_GetParam("REQUEST_METHOD", envp); if(p_method) { plen = strlen(p_method); switch(plen) { case 3: if(!memcmp(p_method, "GET", 4)) verb = VERB_GET; else if(!memcmp(p_method, "PUT", 4)) verb = VERB_PUT; break; case 4: if(!memcmp(p_method, "HEAD", 5)) verb = VERB_HEAD; else if(!memcmp(p_method, "POST", 5)) verb = VERB_POST; break; case 6: if(!memcmp(p_method, "DELETE", 7)) verb = VERB_DELETE; break; case 7: if(!memcmp(p_method, "OPTIONS", 8)) { CGI_PUTS("Allow: GET,HEAD,OPTIONS,PUT,DELETE\r\nContent-Length: 0\r\n\r\n"); return; } break; } } if(verb == VERB_UNSUP) quit_errmsg(405, "Method Not Allowed"); if(content_len()<0 || (verb != VERB_PUT && content_len())) quit_errmsg(400, "Invalid Content-Length: must be positive and method must be PUT"); p_uri = param = FCGX_GetParam("REQUEST_URI", envp); if(!p_uri) quit_errmsg(400, "No URI provided"); plen = strlen(p_uri); if(*p_uri != '/') quit_errmsg(400, "URI must start with /"); if(plen > sizeof(reqbuf) - 1) quit_errmsg(414, "URL too long: request line must be <8k"); do { param++; plen--; } while(*param == '/'); if(!strncmp(param, ".s2s/", lenof(".s2s/"))) { param += lenof(".s2s/"); plen -= lenof(".s2s/"); while(*param == '/') { param++; plen--; } s2sreq = 1; } if(wtype == WORKER_S2S && !s2sreq) WARN("Misconfiguration detected. Please make sure your restricted-socket config option is properly set."); /* FIXME: we could detect the opposite kind of mismatch * at the cost of extra complications in the wtype definition * I prefer to privilege simplicity at this point */ memcpy(reqbuf, param, plen+1); argp = memchr(reqbuf, '?', plen); nargs = 0; if(argp) { unsigned int argslen = plen - (argp - reqbuf); plen = argp - reqbuf; do { *argp = '\0'; argp++; argslen--; } while(*argp == '?'); if(!argslen) argp = NULL; else { do { char *nextarg; if(nargs >= MAX_ARGS) quit_errmsg(414, "Too many parameters"); nextarg = memchr(argp, '&', argslen); if(nextarg) { do { *nextarg = '\0'; nextarg++; } while(*nextarg == '&'); } if(*argp) { if(!(args[nargs] = inplace_urldecode(argp, 0, 0, NULL, 1))) quit_errmsg(400, "Invalid URL encoding"); if(sxi_utf8_validate_len(args[nargs]) < 0) quit_errmsg(400, "Parameters with invalid utf-8 encoding"); nargs++; } argslen -= nextarg - argp; argp = nextarg; } while (argp); } } while(plen && reqbuf[plen-1] == '/') { plen--; reqbuf[plen] = '\0'; } path = memchr(reqbuf, '/', plen); if(path) { do { *path = '\0'; path ++; } while(*path == '/'); if(!*path) path = NULL; } volume = *reqbuf ? reqbuf : NULL; int forbidden = 0; if((volume && !inplace_urldecode(volume, '/', 0, &forbidden, 0)) || (path && !inplace_urldecode(path, '/', '/', &forbidden, 0))) { if (forbidden) quit_errmsg(400, "Volume or path with forbidden %2f or %00"); else quit_errmsg(400, "Invalid URL encoding"); } int vlen = volume ? sxi_utf8_validate_len(volume) : 0; int flen = path ? strlen(path) : 0; if (vlen < 0 || flen < 0) quit_errmsg(400, "URL with invalid utf-8 encoding"); if (is_reserved()) { /* No UTF8/url-encoding used on reserved volumes, allow higher limit. * Otherwise we hit the 1024 limit with batch requests already */ if (path && strlen(path) > SXLIMIT_MAX_FILENAME_LEN * 3) { msg_set_reason("Path too long: filename must be <%d bytes (%ld)", SXLIMIT_MAX_FILENAME_LEN*3+ 1, strlen(path)); quit_errmsg(414, msg_get_reason()); } } else { if (flen > SXLIMIT_MAX_FILENAME_LEN) { msg_set_reason("Path too long: filename must be <%d bytes (%d)", SXLIMIT_MAX_FILENAME_LEN + 1, flen); quit_errmsg(414, msg_get_reason()); } } if (volume && strlen(volume) > SXLIMIT_MAX_VOLNAME_LEN) { msg_set_reason("Volume name too long: must be <= %d bytes", SXLIMIT_MAX_VOLNAME_LEN); quit_errmsg(414, msg_get_reason()); } body_ctx = sxi_md_init(); if (!body_ctx || !sxi_sha1_init(body_ctx)) quit_errmsg(500, "Failed to initialize crypto engine"); hmac_ctx = sxi_hmac_sha1_init(); if (!hmac_ctx) quit_errmsg(503, "Cannot initialize crypto library"); authed = AUTH_NOTAUTH; role = PRIV_NONE; /* Begin auth check */ uint8_t buf[AUTHTOK_BIN_LEN], key[AUTH_KEY_LEN]; unsigned int blen = sizeof(buf); time_t reqdate, now; param = FCGX_GetParam("HTTP_AUTHORIZATION", envp); if(!param || strlen(param) != lenof("SKY ") + AUTHTOK_ASCII_LEN || strncmp(param, "SKY ", 4)) { if(volume) { send_authreq(); return; } quit_home(); } if(sxi_b64_dec_core(param+4, buf, &blen) || blen != sizeof(buf)) { send_authreq(); return; } memcpy(user, buf, sizeof(user)); memcpy(rhmac, buf+20, sizeof(rhmac)); if(sx_hashfs_get_user_info(hashfs, user, &uid, key, &role, NULL, &user_quota) != OK) /* no such user */ { DEBUG("No such user: %s", param+4); send_authreq(); return; } DEBUG("Request from uid %lld", (long long)uid); if(cluster_readonly && (verb == VERB_PUT || verb == VERB_DELETE) && !has_priv(PRIV_CLUSTER) && !has_priv(PRIV_ADMIN)) quit_errmsg(503, "Cluster is in read-only mode"); if(s2sreq && !has_priv(PRIV_CLUSTER)) { send_authreq(); return; } if(!sxi_hmac_sha1_init_ex(hmac_ctx, key, sizeof(key))) { WARN("hmac_init failed"); quit_errmsg(500, "Failed to initialize crypto engine"); } if(!sxi_hmac_sha1_update_str(hmac_ctx, p_method)) quit_errmsg(500, "Crypto error authenticating the request"); if(!sxi_hmac_sha1_update_str(hmac_ctx, p_uri+1)) quit_errmsg(500, "Crypto error authenticating the request"); param = FCGX_GetParam("HTTP_DATE", envp); if(!param) quit_errmsg(400, "Missing Date: header"); if(httpdate_to_time_t(param, &reqdate)) quit_errmsg(400, "Date header in wrong format"); now = time(NULL); if(reqdate < now - MAX_CLOCK_DRIFT * 60 || reqdate > now + MAX_CLOCK_DRIFT * 60) { CGI_PUTS("WWW-Authenticate: SKY realm=\"SXCLOCK\"\r\n"); quit_errmsg(401, "Client clock drifted more than "STRIFY(MAX_CLOCK_DRIFT)" minutes"); } if(!sxi_hmac_sha1_update_str(hmac_ctx, param)) quit_errmsg(500, "Crypto error authenticating the request"); if(!content_len()) { /* If no body is present, complete authentication now */ uint8_t chmac[20]; unsigned int chmac_len = 20; if(!sxi_hmac_sha1_update_str(hmac_ctx, "da39a3ee5e6b4b0d3255bfef95601890afd80709")) quit_errmsg(500, "Crypto error authenticating the request"); if(!sxi_hmac_sha1_final(hmac_ctx, chmac, &chmac_len)) quit_errmsg(500, "Crypto error authenticating the request"); if(!hmac_compare(chmac, rhmac, sizeof(rhmac))) { authed = AUTH_OK; } else { /* WARN("auth mismatch"); */ send_authreq(); return; } } else /* Otherwise set it as pending */ authed = AUTH_BODYCHECK; if(has_priv(PRIV_CLUSTER) && sx_hashfs_uses_secure_proto(hashfs) != is_https() && !sx_storage_is_bare(hashfs)) { /* programmed nodes: must obey cluster SSL mode * unprogrammed nodes: can use SSL instead of non-SSL, * it is the cluster's responsibility to initiate programming via SSL, * as the unprogrammed node would accept both * * */ WARN("hashfs use-ssl: %d, https: %d, is_bare: %d", sx_hashfs_uses_secure_proto(hashfs), is_https(), sx_storage_is_bare(hashfs)); quit_errmsg(403, sx_hashfs_uses_secure_proto(hashfs) ? "Cluster operations require SECURE mode" : "Cluster operations require INSECURE mode"); } if(!volume) cluster_ops(); else if(!path) volume_ops(); else file_ops(); if(authed == AUTH_BODYCHECKING) DEBUG("Bad request signature"); sxi_hmac_sha1_cleanup(&hmac_ctx); sxi_md_cleanup(&body_ctx); }
void send_authreq(void) { CGI_PUTS("WWW-Authenticate: SKY realm=\"SXAUTH\"\r\n"); quit_errmsg(401, "Invalid credentials"); }
void handle_request(void) { const char *param; char *argp; unsigned int plen; msg_new_id(); verb = VERB_UNSUP; param = FCGX_GetParam("REQUEST_METHOD", envp); if(param) { plen = strlen(param); switch(plen) { case 3: if(!memcmp(param, "GET", 4)) verb = VERB_GET; else if(!memcmp(param, "PUT", 4)) verb = VERB_PUT; break; case 4: if(!memcmp(param, "HEAD", 5)) verb = VERB_HEAD; else if(!memcmp(param, "POST", 5)) verb = VERB_POST; break; case 6: if(!memcmp(param, "DELETE", 7)) verb = VERB_DELETE; break; case 7: if(!memcmp(param, "OPTIONS", 8)) { CGI_PUTS("Allow: GET,HEAD,OPTIONS,PUT,DELETE\r\nContent-Length: 0\r\n\r\n"); return; } break; } } if(verb == VERB_UNSUP) quit_errmsg(405, "Method Not Allowed"); if(content_len()<0 || (verb != VERB_PUT && content_len())) quit_errmsg(400, "Invalid Content-Length: must be positive and method must be PUT"); param = FCGX_GetParam("REQUEST_URI", envp); if(!param) quit_errmsg(400, "No URI provided"); plen = strlen(param); if(*param != '/') quit_errmsg(400, "URI must start with /"); if(plen > sizeof(reqbuf) - 1) quit_errmsg(414, "URL too long: request line must be <8k"); do { param++; plen--; } while(*param == '/'); memcpy(reqbuf, param, plen+1); argp = memchr(reqbuf, '?', plen); nargs = 0; if(argp) { unsigned int argslen = plen - (argp - reqbuf); plen = argp - reqbuf; do { *argp = '\0'; argp++; argslen--; } while(*argp == '?'); if(!argslen) argp = NULL; else { do { char *nextarg; if(nargs >= MAX_ARGS) quit_errmsg(414, "Too many parameters"); nextarg = memchr(argp, '&', argslen); if(nextarg) { do { *nextarg = '\0'; nextarg++; } while(*nextarg == '&'); } if(*argp) { if(!(args[nargs] = inplace_urldecode(argp, 0, 0, NULL))) quit_errmsg(400, "Invalid URL encoding"); if(utf8_validate_len(args[nargs]) < 0) quit_errmsg(400, "Parameters with invalid utf-8 encoding"); nargs++; } argslen -= nextarg - argp; argp = nextarg; } while (argp); } } while(plen && reqbuf[plen-1] == '/') { plen--; reqbuf[plen] = '\0'; } path = memchr(reqbuf, '/', plen); if(path) { do { *path = '\0'; path ++; } while(*path == '/'); if(!*path) path = NULL; } volume = *reqbuf ? reqbuf : NULL; int forbidden = 0; if((volume && !inplace_urldecode(volume, '/', 0, &forbidden)) || (path && !inplace_urldecode(path, '/', '/', &forbidden))) { if (forbidden) quit_errmsg(400, "Volume or path with forbidden %2f or %00"); else quit_errmsg(400, "Invalid URL encoding"); } int vlen = volume ? utf8_validate_len(volume) : 0; int flen = path ? utf8_validate_len(path) : 0; if (vlen < 0 || flen < 0) quit_errmsg(400, "URL with invalid utf-8 encoding"); if (is_reserved()) { /* No UTF8 used on reserved volumes, allow higher limit. * Otherwise we hit the 512 limit with batch requests already */ if (path && strlen(path) > SXLIMIT_MAX_FILENAME_LEN * 12) { msg_set_reason("Path too long: filename must be <%d characters (%ld)", SXLIMIT_MAX_FILENAME_LEN*12+ 1, strlen(path)); quit_errmsg(414, msg_get_reason()); } } else { if (flen > SXLIMIT_MAX_FILENAME_LEN) { msg_set_reason("Path too long: filename must be <%d UTF8 characters (%d)", SXLIMIT_MAX_FILENAME_LEN + 1, flen); quit_errmsg(414, msg_get_reason()); } } if (volume && strlen(volume) > SXLIMIT_MAX_VOLNAME_LEN) { msg_set_reason("Volume name too long: must be <= %d bytes", SXLIMIT_MAX_VOLNAME_LEN); quit_errmsg(414, msg_get_reason()); } if(!EVP_DigestInit(&body_ctx, EVP_sha1())) quit_errmsg(500, "Failed to initialize crypto engine"); HMAC_CTX_init(&hmac_ctx); authed = AUTH_NOTAUTH; role = PRIV_NONE; auth_begin(); if(has_priv(PRIV_CLUSTER) && sx_hashfs_uses_secure_proto(hashfs) != is_https() && !sx_storage_is_bare(hashfs)) { /* programmed nodes: must obey cluster SSL mode * unprogrammed nodes: can use SSL instead of non-SSL, * it is the cluster's responsibility to initiate programming via SSL, * as the unprogrammed node would accept both * * */ WARN("hashfs use-ssl: %d, https: %d, is_bare: %d", sx_hashfs_uses_secure_proto(hashfs), is_https(), sx_storage_is_bare(hashfs)); quit_errmsg(403, sx_hashfs_uses_secure_proto(hashfs) ? "Cluster operations require SECURE mode" : "Cluster operations require INSECURE mode"); } int dc = sx_hashfs_distcheck(hashfs); if(dc < 0) { CRIT("Failed to reload distribution"); /* MODHDIST: should die here */ } if(!volume) cluster_ops(); else if(!path) volume_ops(); else file_ops(); if(authed == AUTH_BODYCHECKING) WARN("FIXME: Security fail"); HMAC_CTX_cleanup(&hmac_ctx); EVP_MD_CTX_cleanup(&body_ctx); }
static void auth_begin(void) { const char *param = FCGX_GetParam("HTTP_AUTHORIZATION", envp); uint8_t buf[AUTHTOK_BIN_LEN], key[AUTH_KEY_LEN]; unsigned int blen = sizeof(buf); time_t reqdate, now; if(!param || strlen(param) != lenof("SKY ") + AUTHTOK_ASCII_LEN || strncmp(param, "SKY ", 4)) return; if(sxi_b64_dec_core(param+4, buf, &blen) || blen != sizeof(buf)) return; memcpy(user, buf, sizeof(user)); memcpy(rhmac, buf+20, sizeof(rhmac)); if(sx_hashfs_get_user_info(hashfs, user, &uid, key, &role) != OK) /* no such user */ { WARN("No such user: %s", param+4); return; } DEBUG("Request from uid %lld", (long long)uid); if(!sxi_hmac_init_ex(&hmac_ctx, key, sizeof(key), EVP_sha1(), NULL)) { WARN("hmac_init failed"); quit_errmsg(500, "Failed to initialize crypto engine"); } param = FCGX_GetParam("REQUEST_METHOD", envp); if(!param) return; if(!hmac_update_str(&hmac_ctx, param)) quit_errmsg(500, "Failed to initialize crypto engine"); param = FCGX_GetParam("REQUEST_URI", envp); if(!param) return; if(!hmac_update_str(&hmac_ctx, param+1)) quit_errmsg(500, "Failed to initialize crypto engine"); param = FCGX_GetParam("HTTP_DATE", envp); if(!param) quit_errmsg(400, "Missing Date: header"); if(httpdate_to_time_t(param, &reqdate)) quit_errmsg(400, "Date header in wrong format"); now = time(NULL); if(reqdate < now - MAX_CLOCK_DRIFT * 60 || reqdate > now + MAX_CLOCK_DRIFT * 60) quit_errmsg(400, "Client clock drifted more than "STRIFY(MAX_CLOCK_DRIFT)" minutes"); if(!hmac_update_str(&hmac_ctx, param)) quit_errmsg(500, "Failed to initialize crypto engine"); if(!content_len()) { uint8_t chmac[20]; unsigned int chmac_len = 20; if(!hmac_update_str(&hmac_ctx, "da39a3ee5e6b4b0d3255bfef95601890afd80709")) quit_errmsg(500, "Failed to initialize crypto engine"); if(!sxi_hmac_final(&hmac_ctx, chmac, &chmac_len)) quit_errmsg(500, "Failed to initialize crypto engine"); if(!hmac_compare(chmac, rhmac, sizeof(rhmac))) { authed = AUTH_OK; } else { /* WARN("auth mismatch"); */ } return; } else authed = AUTH_BODYCHECK; }
void fcgi_extend_tempfile(void) { rc_ty s = sx_hashfs_putfile_extend_begin(hashfs, uid, user, path); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); create_or_extend_tempfile(NULL, NULL, 1); }
static void create_or_extend_tempfile(const sx_hashfs_volume_t *vol, const char *filename, int extending) { const struct jparse_actions acts = { JPACTS_INT64( JPACT(cb_newfile_size, JPKEY("fileSize")), JPACT(cb_newfile_seq, JPKEY("extendSeq")) ), JPACTS_STRING( JPACT(cb_newfile_block, JPKEY("fileData"), JPANYITM), JPACT(cb_newfile_addmeta, JPKEY("fileMeta"), JPANYKEY) ), JPACTS_NULL( JPACT(cb_newfile_delmeta, JPKEY("fileMeta"), JPANYKEY) ) }; struct cb_newfile_ctx yctx; hash_presence_ctx_t ctx; const char *token; jparse_t *J; int len; rc_ty s; yctx.filesize = -1; yctx.seq = -1; yctx.rc = EINVAL; J = sxi_jparse_create(&acts, &yctx, 0); if(!J) { sx_hashfs_putfile_end(hashfs); quit_errmsg(503, "Cannot create JSON parser"); } while((len = get_body_chunk(hashbuf, sizeof(hashbuf))) > 0) if(sxi_jparse_digest(J, hashbuf, len)) break; if(len || sxi_jparse_done(J)) { send_error(rc2http(yctx.rc), sxi_jparse_geterr(J)); sx_hashfs_putfile_end(hashfs); sxi_jparse_destroy(J); return; } sxi_jparse_destroy(J); auth_complete(); quit_unless_authed(); s = sx_hashfs_putfile_gettoken(hashfs, user, yctx.filesize, yctx.seq, &token, hash_presence_callback, &ctx); if (s != OK) { sx_hashfs_putfile_end(hashfs); if(!*msg_get_reason()) msg_set_reason("Cannot obtain upload token: %s", rc2str(s)); quit_errmsg((s == ENOSPC) ? 413 : rc2http(s), msg_get_reason()); } CGI_PRINTF("Content-type: application/json\r\n\r\n{\"uploadToken\":"); json_send_qstring(extending ? path : token); CGI_PUTS(",\"uploadData\":{"); ctx.h = hashfs; ctx.comma = 0; while((s = sx_hashfs_putfile_getblock(hashfs)) == OK) { /* Nothing to do here, API does a little bit of work at a time by design * We can stick keepalives in here if we ever need to */ } sx_hashfs_putfile_end(hashfs); CGI_PUTS("}"); if(s != ITER_NO_MORE) { quit_itererr("Failed to send file blocks", s); } CGI_PUTS("}"); }
void fcgi_create_file(void) { const struct jparse_actions acts = { JPACTS_INT64( JPACT(cb_newfile_size, JPKEY("fileSize")) ), JPACTS_STRING( JPACT(cb_newfile_block, JPKEY("fileData"), JPANYITM), JPACT(cb_newfile_addmeta, JPKEY("fileMeta"), JPANYKEY) ), JPACTS_NULL( JPACT(cb_newfile_delmeta, JPKEY("fileMeta"), JPANYKEY) ) }; struct cb_newfile_ctx yctx; jparse_t *J; int len; rc_ty s; sx_hash_t revid; quit_unless_has(PRIV_CLUSTER); /* Just in case */ if(!has_arg("rev") || !has_arg("revid") || strlen(get_arg("revid")) != SXI_SHA1_TEXT_LEN) quit_errmsg(500, "File revision missing"); if(hex2bin(get_arg("revid"), SXI_SHA1_TEXT_LEN, revid.b, sizeof(revid.b))) quit_errmsg(400, "Failed to parse revision ID"); s = sx_hashfs_createfile_begin(hashfs); switch (s) { case OK: break; case ENOENT: quit_errnum(404); case EINVAL: quit_errnum(400); default: WARN("sx_hashfs_createfile_begin failed: %d", s); quit_errmsg(rc2http(s), "Cannot initialize file upload"); } yctx.filesize = -1; yctx.rc = EINVAL; J = sxi_jparse_create(&acts, &yctx, 0); if(!J) { sx_hashfs_createfile_end(hashfs); quit_errmsg(503, "Cannot create JSON parser"); } while((len = get_body_chunk(hashbuf, sizeof(hashbuf))) > 0) if(sxi_jparse_digest(J, hashbuf, len)) break; if(len || sxi_jparse_done(J)) { send_error(rc2http(yctx.rc), sxi_jparse_geterr(J)); sx_hashfs_createfile_end(hashfs); sxi_jparse_destroy(J); return; } sxi_jparse_destroy(J); auth_complete(); quit_unless_authed(); s = sx_hashfs_createfile_commit(hashfs, volume, path, get_arg("rev"), &revid, yctx.filesize, 0); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); CGI_PUTS("\r\n"); }