diff --git a/src/common/request.h b/src/common/request.h --- a/src/common/request.h +++ b/src/common/request.h @@ -22,6 +22,8 @@ #include "common/protocol.h" +#include + /** * @file functions to initialize various requests that the server and * client may both use. @@ -62,6 +64,152 @@ int distren_request_parse_version(struct * @param poing_data_len bytes in the poing_cookie * @return the length of the data allocated for this request */ -uint32_t distren_request_poing(struct distren_request **req, void **data, short is_ping, const void *poing_cookie, size_t poing_data_len); +int distren_request_poing(struct distren_request **req, void **data, short is_ping, const void *poing_cookie, size_t poing_data_len); + +struct distren_request_file_post_context; +typedef struct distren_request_file_post_context *distren_request_file_post_context_t; + +/** + * Frees a post_context for if a client is disconnected or an upload + * otherwise interrupted. + */ +int distren_request_file_post_context_free(distren_request_file_post_context_t post_context); + +/** + * Prepares for a file upload and initializes a + * distren_request_file_post_context_t. + * + * If you call this function and you never finish an upload, you + * _must_ call distren_request_file_post_finish() on post_context at + * least to free the post_context. + * + * @param req where to store the new distren_request pointer. + * @param data where to store the new request's data pointer. + * @param post_context where the newly allocated distren_request_file_post_context_t's addrss should be stored. + * @param post_id a number that uniquely identifies this upload for this connection. + * @param filename a user-friendly filename for the file posting. + */ +int distren_request_file_post_start(struct distren_request **req, + void **data, + distren_request_file_post_context_t *post_context, + uint32_t post_id, + const char *filename); + +/** + * Parses a DISTREN_REQUEST_FILE_POST_START request. + * + * @param req the recieved request. + * @param data the request's data. + * @param post_context a post_context to be initialized for a continued download. Must be free()d with a call to distren_request_parse_file_post_finish() or distren_request_file_post_context_free(), however. + * @param post_id will be set to the post_id contained in the packet. + * @param filename will be made to point at the filename the client requested. *filename must be free()d. + * @return 0 upon no problem, 1 if there's an error. If 1, then *filename should not be used (it will be set NULL, so no problem calling free() on it though). + */ +int distren_request_parse_file_post_start(struct distren_request *req, + void *data, + distren_request_file_post_context_t *post_context, + uint32_t *post_id, + char **filename); + +/** + * Initializes a DISTREN_REQUEST_FILE_POST packet's header. + * + * As file posting requires an acceptable chunk of memory, we don't + * malloc() the data portion of the packet here to encourage reading + * files in through static buffers. + * + * As a single file takes multiple DISTREN_REQUEST_FILE_POST packets + * before it's completely uploaded, certain state information has to + * persist between calls to distren_request_file_post(). This is what + * the post_context argument is for. This pointer must be preserved + * between calls to distren_request_file_post(). + * + * @param req place to store the new req's pointer + * @param data_header where the data's header should be stored + * @param post_context what this points to must be preserved between calls to distren_request_file_post(), for keeping state. +FILE_POST_DATA_LEN. + * @param data a hunk of the file being posted. + * @param len the length of the data argument. This must be less than or equal to DISTREN_REQUEST_ */ +int distren_request_file_post(struct distren_request **req, + struct distren_request_file_post *data_header, + distren_request_file_post_context_t post_context, + const void *data, + size_t len); +/** + * A callback to find a distren_request_file_post_context_t for a + * specified post_id. + * + * This exists because it is recognized that two servers may be + * transfering more than one file at a time between eachother. At the + * time that distren_request_parse_file_post() is called, the caller + * doesn't know post_id and thus can't provide the proper + * post_context. Yet distren_request_parse_file_post() needs the + * post_context to get its job done ;-). + * + * @param post_id the post_id for which to find a distren_request_file_post_context_t + * @param find_post_context_data the same find_post_context_data pointer passed to distren_request_parse_file_post() or distren_request_parse_file_post_finish(). + * @return NULL if the context could not be found or the context. + */ +typedef distren_request_file_post_context_t(*distren_request_parse_file_post_find_context_func_t)(uint32_t post_id, void *find_post_context_data); + +/** + * Parse a DISTREN_REQUEST_FILE_POST packet and update the + * post_context. + * + * @param req the request header. + * @param data the request data section. + * @param post_id will be filled with the request's post_id. + * @param find_context_func a function that can look up the necessary context_post given a post_id. + * @param find_post_context_data the pointer to pass as context data to find_context_func + * @param file_data will be set to the beginning of the file data portion of the packet. Do not free() this pointer. + * @param file_data_len the number of bytes of file data that may be accessed at *file_data. + * @return 0 on success and 1 on failure, in which case the client should be kicked and you should free the post_context with distren_request_file_post_context_free(). + */ +int distren_request_parse_file_post(struct distren_request *req, + void *data, + uint32_t *post_id, + distren_request_parse_file_post_find_context_func_t find_context_func, + void *find_post_context_data, + void **file_data, + size_t *file_data_len); + +/** + * Construct a packet signalling that a file posting is finished. + * + * Frees the memory used to store the post_context. Must be called + * once for every call to distren_request_file_post_start(), + * regardless of whether or not you actually call + * distren_request_send() on the result. + * + * @param req where to store the pointer of the newly allocated request + * @param data where to store the pointer t othe newly allocated request data + * @param post_context the post_context which will be used and then freed. + * @param cancel 0 if the file was uploaded successfully, 1 if the upload is cancelled or failed. + */ +int distren_request_file_post_finish(struct distren_request **req, + void **data, + distren_request_file_post_context_t post_context, + uint32_t cancel); + +/** + * Parses a DISTREN_REQUEST_FILE_POST_FINISH packet. + * + * You must still call distren_request_file_post_context_free() on the + * post_context which you must find yourself. If you are a server, you + * must keep a list of these contexts per-client so that when a client + * exits, you may reclaim these contexts. + * + * @param req the request header + * @param data the data section of the packet + * @param post_id a place where the post_id will be set. + * @param find_context_func a function pointer to let me find a post_context based on the post_id of the request. + * @param find_post_context_data the pointer to pass to the find_context_func callback. + * @return 0 for a good packet, 1 for a bad packet or if the post_context can't be found (post_id won't be valid), 2 if the request asks for the user to cancel the file upload (post_id will be valid), and 3 if the checksums don't match (post_id will be valid). + */ +int distren_request_parse_file_post_finish(struct distren_request *req, + void *data, + uint32_t *post_id, + distren_request_parse_file_post_find_context_func_t find_context_func, + void *find_post_context_data); #endif /* _DISTREN_REQUEST_H */