diff --git a/src/server/distrend.c b/src/server/distrend.c --- a/src/server/distrend.c +++ b/src/server/distrend.c @@ -33,6 +33,7 @@ #include "common/protocol.h" #include "common/request.h" +#include #include #include #include @@ -61,6 +62,12 @@ struct general_info { /** general_info.xml */ char *geninfo; + /** + * where to store in-progress uploads or things that should + * otherwise be on the same filesystem as the rest of the datadir + * so that it may be rename()d. + */ + char *tmpdir; } files; @@ -88,9 +95,15 @@ int distrend_config_free(struct distrend int distrend_handle_request(struct distrend_listens *listens, struct distrend_client *client, struct distren_request *req, void *reqdata, struct general_info *geninfo); /** - client request handlers + * client request handlers */ int distrend_handle_version(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data); +int distrend_handle_file_post_start(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data); +int distrend_handle_file_post(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data); +int distrend_handle_file_post_finish(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data); + +/* functions of some generic sort ...ish */ +int distrend_handle_successful_upload(struct distrend_client *client, struct distrend_client_file_post *client_file_post); /* **************XML Functions**************** */ void update_general_info(struct general_info *geninfo); @@ -154,6 +167,10 @@ Ethan Zonca \n\ _distren_asprintf(&general_info.files.geninfo, "%s/general_info.xml", general_info.config->datadir); + _distren_asprintf(&general_info.files.tmpdir, "%s/tmp", + general_info.config->datadir); + distren_mkdir_recurse(general_info.files.tmpdir); + /** MySQL Connection */ fprintf(stderr,"Connecting to mysql...\n"); if(mysqlConnect(&general_info.conn, @@ -190,6 +207,9 @@ Ethan Zonca \n\ } distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_VERSION, &distrend_handle_version); + distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_FILE_POST_START, &distrend_handle_file_post_start); + distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_FILE_POST, &distrend_handle_file_post); + distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_FILE_POST_FINISH, &distrend_handle_file_post_finish); /* Main Loop */ general_info.config->die = 0; @@ -265,6 +285,259 @@ int distrend_handle_version(struct gener } /** + * Traversal helper for distrend_client_find_post(). + */ +int distrend_client_find_post_traverse(uint32_t *post_id, struct distrend_client_file_post *client_file_post) +{ + if(*post_id == client_file_post->post_id) + return FALSE; + + return TRUE; +} + +/** + * Find the record for an in-progress client's file posting. + */ +struct distrend_client_file_post *distrend_client_find_post(struct distrend_client *client, uint32_t post_id) +{ + if(list_traverse(client->file_post_list, &post_id, (list_traverse_func_t)&distrend_client_find_post_traverse, LIST_ALTR | LIST_FORW | LIST_FRNT) != LIST_EXTENT) + return list_curr(client->file_post_list); + return NULL; +} + +/** + * Finds a post_context based on the post_id and client. + * + * Compatible the distren_request_parse_file_post_find_context_func_t. + */ +static distren_request_file_post_context_t distrend_client_find_file_post_context(uint32_t post_id, void *client) +{ + struct distrend_client_file_post *client_file_post; + + client_file_post = distrend_client_find_post(client, post_id); + if(client_file_post) + return client_file_post->post_context; + return NULL; +} + +/** + * Clean up and free a client_file_post + * + * Whenever calling this functino, you almost _always_ have to call + * list_remove_element(client->file_post_list, client_file_post); + * first. + */ +void distrend_client_file_post_free(struct distrend_client_file_post *client_file_post) +{ + fclose(client_file_post->fd); + free(client_file_post->filename); + unlink(client_file_post->file_save_path); + free(client_file_post->file_save_path); + + free(client_file_post); +} + +int distrend_handle_file_post_start(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data) +{ + struct distrend_client_file_post *client_file_post; + + distren_request_file_post_context_t post_context; + uint32_t post_id; + char *filename; + + char *str_tmp; + + int ret; + + /** + * @todo access check! + */ + fprintf(stderr, __FILE__ ":%d:distrend_handle_file_post_start(): You need to check if a client is actually allowed to upload files somehow!\n", __LINE__); + + /* + * other servers should be excluded from this check, but we don't + * store the servertype in client yet. + */ + if(list_size(client->file_post_list) > 1) + { + distrend_send_disconnect(client, "You are trying to upload too many files at once!"); + return 1; + } + + ret = distren_request_parse_file_post_start(req, req_data, &post_context, &post_id, &filename); + if(ret) + { + distrend_send_disconnect(client, "You sent me an invalid DISTREN_REQUEST_FILE_POST_START packet"); + return 1; + } + + if(distrend_client_find_post(client, post_id)) + { + _distren_asprintf(&str_tmp, "Err accepting file: You are trying to upload using post_id=%d while you have already started another upload using the same post_id", post_id); + distrend_send_disconnect(client, str_tmp); + free(str_tmp); + + distren_request_file_post_context_free(post_context); + + return 1; + } + + client_file_post = malloc(sizeof(struct distrend_client_file_post)); + if(!client_file_post) + { + distrend_send_disconnect(client, "Error accepting file: out of memory"); + + distren_request_file_post_context_free(post_context); + + return 1; + } + + client_file_post->post_context = post_context; + client_file_post->post_id = post_id; + client_file_post->filename = filename; + _distren_asprintf(&client_file_post->file_save_path, "%s/conn-%d_file_post-%d", + geninfo->files.tmpdir, client->connection_id, post_id); + client_file_post->fd = fopen(client_file_post->file_save_path, "w"); + if(!client_file_post->fd) + { + perror("fopen"); + + fprintf(stderr, "error: Unable to open ``%s''. See above ``fopen'' error for more details.\n", client_file_post->file_save_path); + distrend_send_disconnect(client, "Error accepting file: unable to store the file n disk"); + + distren_request_file_post_context_free(post_context); + + list_remove_element(client->file_post_list, client_file_post); + distrend_client_file_post_free(client_file_post); + return 1; + } + + list_insert_after(client->file_post_list, client_file_post, 0); + + return 0; +} + +int distrend_handle_file_post(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data) +{ + struct distrend_client_file_post *client_file_post; + + void *file_data; + size_t file_data_len; + + uint32_t post_id; + char *tmp_str; + + size_t written_len; + + int ret; + + ret = distren_request_parse_file_post(req, req_data, &post_id, + &distrend_client_find_file_post_context, client, + &file_data, &file_data_len); + if(ret) + { + distrend_send_disconnect(client, "You sent me an invalid DISTREN_REQUEST_FILE_POST packet"); + return 1; + } + + client_file_post = distrend_client_find_post(client, post_id); + + if(!client_file_post) + { + _distren_asprintf(&tmp_str, "You are attempting to upload post_id=%d when you haven't given a DISTREN_REQUEST_FILE_POST_START packet", post_id); + distrend_send_disconnect(client, tmp_str); + free(tmp_str); + return 1; + } + + written_len = fwrite(file_data, 1, file_data_len, client_file_post->fd); + if(written_len < file_data_len) + { + distrend_send_disconnect(client, "Error saving upload: error while writing to the temporary upload file"); + + list_remove_element(client->file_post_list, client_file_post);; + /* closes the file being written, and free()s everything, unlinks the file */ + distrend_client_file_post_free(client_file_post); + + return 1; + } + + return 0; +} + +/** + * here be magic + */ +int distrend_handle_file_post_finish(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data) +{ + struct distrend_client_file_post *client_file_post; + + uint32_t post_id; + int ret; + + ret = distren_request_parse_file_post_finish(req, req_data, &post_id, + &distrend_client_find_file_post_context, client); + if(ret) + { + switch(ret) + { + case 2: + /* client asked to cancel a file upload */ + client_file_post = distrend_client_find_post(client, post_id); + if(client_file_post) + { + list_remove_element(client->file_post_list, client_file_post); + distrend_client_file_post_free(client_file_post); + } + return 0; + + case 3: + /* checksuming failed */ + distrend_send_disconnect(client, "You have uploaded a file that doesn't match its checksum somehow... which should be pretty much impossible"); + client_file_post = distrend_client_find_post(client, post_id); + if(client_file_post) + { + list_remove_element(client->file_post_list, client_file_post); + distrend_client_file_post_free(client_file_post); + } + return 1; + + default: + distrend_send_disconnect(client, "You sent me an invalid DISTREN_REQUEST_FILE_POST_FINISH packet"); + } + return 1; + } + + client_file_post = distrend_client_find_post(client, post_id); + if(!client_file_post) + return 1; + + /* + * Here it is... manage a file being submitted for rendering... or + * for whatever purpose it was uploaded for somehow... + */ + distrend_handle_successful_upload(client, client_file_post); + + list_remove_element(client->file_post_list, client_file_post); + distrend_client_file_post_free(client_file_post); + + return 0; +} + +/** + * Does stuff with file uploads after they've been successfully acquired. + * + * @todo this should probably be genericized to handle 1. file uploads and 2. server-initiated file _downloads_, i.e., using libcurl. In that case, struct distrend_client_file_post shouldn't be passed directly to ourself but some common struct might be passed here. + */ +int distrend_handle_successful_upload(struct distrend_client *client, struct distrend_client_file_post *client_file_post) +{ + fprintf(stderr, __FILE__ ":%d: STUB: I don't know what to do with %s[%s] :-/\n", __LINE__, + client_file_post->filename, client_file_post->file_save_path); + + return 0; +} + +/** Performs command stored in a client's request. @TODO: Fill stub */ int distrend_do() @@ -582,3 +855,4 @@ int interactiveTest(int test, multiio_co } return 0; } +