diff --git a/src/onion/http.c b/src/onion/http.c index 1726e226..21cffa74 100644 --- a/src/onion/http.c +++ b/src/onion/http.c @@ -44,6 +44,7 @@ int onion_http_read_ready(onion_request * req); struct onion_http_t { }; + /** * @short Creates an HTTP listen point * @memberof onion_http_t @@ -61,6 +62,7 @@ onion_listen_point *onion_http_new() { return ret; } + /** * @short Reads data from the http connection * @memberof onion_http_t @@ -76,7 +78,7 @@ static ssize_t onion_http_read(onion_request * con, char *data, size_t len) { * @ingroup http */ int onion_http_read_ready(onion_request * con) { - char buffer[1500]; + char buffer[9100]; ssize_t len = con->connection.listen_point->read(con, buffer, sizeof(buffer)); if (len <= 0) diff --git a/src/onion/listen_point.c b/src/onion/listen_point.c index ebc5bb3e..567c4778 100644 --- a/src/onion/listen_point.c +++ b/src/onion/listen_point.c @@ -63,9 +63,82 @@ static int onion_listen_point_read_ready(onion_request * req); */ onion_listen_point *onion_listen_point_new() { onion_listen_point *ret = onion_low_calloc(1, sizeof(onion_listen_point)); + ret->att_hndl.auth = NULL; + ret->att_hndl.open = NULL; + ret->att_hndl.pread = NULL; + ret->att_hndl.pwrite = NULL; + ret->att_hndl.close = NULL; + ret->att_hndl.unlink = NULL; + ret->cache_size = 4096; + + ret->new_hash_ctx = NULL; + ret->init_hash_ctx = NULL; + ret->update_hash_ctx = NULL; + ret->final_hash_ctx = NULL; + ret->free_hash_ctx = NULL; + ret->multi = false; + + ret->context = NULL; + return ret; } +void onion_listen_point_set_attachment_handlers(onion_listen_point* ret, + int (*f_auth)(onion_request*, char*), + int (*f_open)(const char*, int, ...), + ssize_t (*f_pread)(const char*, void*, size_t, off_t), + ssize_t (*f_pwrite)(const char*, const void*, size_t, off_t, onion_request*), + int (*f_close)(const char*), + int (*f_unlink)(const char*) ){ + ret->att_hndl.auth = f_auth; + ret->att_hndl.open = f_open; + ret->att_hndl.pread = f_pread; + ret->att_hndl.pwrite = f_pwrite; + ret->att_hndl.close = f_close; + ret->att_hndl.unlink = f_unlink; +} + +void* onion_listen_point_att_hndl_open(onion_listen_point* lp){ + return lp->att_hndl.open; +} + +void* onion_listen_point_att_hndl_pread(onion_listen_point* lp){ + return lp->att_hndl.pread; +} + +void* onion_listen_point_att_hndl_close(onion_listen_point* lp){ + return lp->att_hndl.close; +} + +void* onion_listen_point_att_hndl_unlink(onion_listen_point* lp){ + return lp->att_hndl.unlink; +} + +void onion_listen_point_set_hash_handlers(onion_listen_point* ret, + void* (*f_new)(), int (*f_init)(void*), + int (*f_update)(void* , const void *, size_t ), + int (*f_final)(unsigned char*, void*), + void (*f_free)(void* ctx), bool multi ){ + ret->new_hash_ctx = f_new; + ret->init_hash_ctx = f_init; + ret->update_hash_ctx = f_update; + ret->final_hash_ctx = f_final; + ret->free_hash_ctx = f_free; + ret->multi = multi; +} + +void onion_listen_point_set_cache_size(onion_listen_point* ret, size_t cache_size){ + ret->cache_size = cache_size; +} + +void onion_listen_point_set_context(onion_listen_point* ret, void* context){ + ret->context = context; +} + +void* onion_listen_point_get_context(onion_listen_point* lp){ + return lp->context; +} + /** * @short Free and closes the listen point * @memberof onion_listen_point_t diff --git a/src/onion/listen_point.h b/src/onion/listen_point.h index 67b16604..1a3190f2 100644 --- a/src/onion/listen_point.h +++ b/src/onion/listen_point.h @@ -37,6 +37,30 @@ extern "C" { int onion_listen_point_accept(onion_listen_point *); int onion_listen_point_request_init_from_socket(onion_request * op); void onion_listen_point_request_close_socket(onion_request * oc); + void onion_listen_point_set_attachment_handlers(onion_listen_point* lp, + int (*f_auth)(onion_request*, char*), + int (*f_open)(const char*, int, ...), + ssize_t (*f_pread)(const char*, void*, size_t, off_t), + ssize_t (*f_pwrite)(const char*, const void*, size_t, off_t, onion_request*), + int (*f_close)(const char*), + int (*f_unlink)(const char*)); + void onion_listen_point_set_hash_handlers(onion_listen_point* lp, + void* (*f_new)(), + int (*f_init)(void* ctx), + int (*f_update)(void* ctx, const void *data, size_t len), + int (*f_final)(unsigned char* data, void* ctx), + void (*f_free)(void* ctx), bool multi); + + void* onion_listen_point_att_hndl_open(onion_listen_point*); + void* onion_listen_point_att_hndl_pread(onion_listen_point*); + void* onion_listen_point_att_hndl_close(onion_listen_point*); + void* onion_listen_point_att_hndl_unlink(onion_listen_point*); + + void onion_listen_point_set_cache_size(onion_listen_point* lp, size_t); + + void onion_listen_point_set_context(onion_listen_point* lp, void* context); + void* onion_listen_point_get_context(onion_listen_point* lp); + #ifdef __cplusplus } #endif diff --git a/src/onion/onion.c b/src/onion/onion.c index 279fd188..ed1f7822 100644 --- a/src/onion/onion.c +++ b/src/onion/onion.c @@ -252,6 +252,68 @@ onion *onion_new(int flags) { return o; } +void onion_set_attachment_handlers(onion* onion, + int (*f_auth)(onion_request*, char*), + int (*f_open)(const char*, int, ...), + ssize_t (*f_read)(const char*, void*, size_t, off_t), + ssize_t (*f_write)(const char*, const void*, size_t, off_t, onion_request*), + int (*f_close)(const char*), + int (*f_unlink)(const char*) ){ + if (onion->listen_points) { + onion_listen_point **p = onion->listen_points; + while (*p != NULL) { + onion_listen_point_set_attachment_handlers(*p++, + f_auth, f_open, f_read, f_write, f_close, f_unlink); + } + } +} + +void onion_set_hash_handlers(onion* onion, void* (*f_new)(), int (*f_init)(void*), + int (*f_update)(void*, const void*, size_t), int (*f_final)(unsigned char*, void*), + void (*f_free)(void*), bool multi){ + if (onion->listen_points) { + onion_listen_point **p = onion->listen_points; + while (*p != NULL) { + onion_listen_point_set_hash_handlers(*p++, f_new, f_init, f_update, f_final, f_free, multi); + } + } +} + +/* +void onion_set_s3_handlers(onion* onion, + int (*f_auth)(onion_request*, char*), + int (*f_open)(const char*), + ssize_t (*f_write)(const char* , const void*, size_t, off_t), + int (*f_close)(const char*), + int (*f_unlink)(const char*)){ + if (onion->listen_points) { + onion_listen_point **p = onion->listen_points; + while (*p != NULL) { + onion_listen_point_set_s3_handlers(*p++, f_auth, f_open, f_write, f_close, f_unlink); + } + } +} +*/ + +void onion_set_cache_size(onion* onion, size_t cache_size){ + if (onion->listen_points) { + onion_listen_point **p = onion->listen_points; + while (*p != NULL) { + onion_listen_point_set_cache_size(*p++, cache_size); + } + } +} + + +void onion_set_context(onion* onion, void* context){ + if (onion->listen_points) { + onion_listen_point **p = onion->listen_points; + while (*p != NULL) { + onion_listen_point_set_context(*p++, context); + } + } +} + /** * @short Removes the allocated data * @ingroup onion diff --git a/src/onion/onion.h b/src/onion/onion.h index 2224939e..98f85824 100644 --- a/src/onion/onion.h +++ b/src/onion/onion.h @@ -86,6 +86,26 @@ extern "C" { /// Gets a single listen point, or NULL if not that many. onion_listen_point *onion_get_listen_point(onion * server, int nlisten_point); + void onion_set_attachment_handlers(onion* server, + int (*f_auth)(onion_request*, char*), + int (*f_open)(const char*, int, ...), + ssize_t (*f_read)(const char*, void*, size_t, off_t), + ssize_t (*f_write)(const char*, const void*, size_t, off_t, onion_request*), + int (*f_close)(const char*), + int (*f_unlink)(const char*)); + + void onion_set_hash_handlers(onion* server, + void* (*f_new)(), + int (*f_init)(void*), + int (*f_update)(void * , const void *, size_t ), + int (*f_final)(unsigned char* , void*), + void (*f_free)(void* ), + bool multi); + + void onion_set_cache_size(onion* server, size_t cache_size); + + void onion_set_context(onion* server, void* context); + /// Gets the current flags, for example to check SSL support. int onion_flags(onion * onion); diff --git a/src/onion/request.c b/src/onion/request.c index d606f9a5..819d30bf 100644 --- a/src/onion/request.c +++ b/src/onion/request.c @@ -76,6 +76,7 @@ onion_request *onion_request_new(onion_listen_point * op) { //req->connection=con; req->headers = onion_dict_new(); onion_dict_set_flags(req->headers, OD_ICASE); + req->hash_ctx = (op->new_hash_ctx) ? op->new_hash_ctx() : NULL; ONION_DEBUG0("Create request %p", req); if (op) { @@ -107,11 +108,14 @@ onion_request *onion_request_new_from_socket(onion_listen_point * con, int fd, * @short Helper to remove temporal files from req->files * @memberof onion_request_t * @ingroup request - */ -static void unlink_files(void *p, const char *key, const char *value, int flags) { + static void unlink_files(void *p, const char *key, const char *value, int flags) { ONION_DEBUG0("Unlinking temporal file %s", value); - unlink(value); + int (*f) (const char *value); + f = p; + f(value); + //unlink(value); } +*/ /** * @short Deletes a request and all its data @@ -119,6 +123,8 @@ static void unlink_files(void *p, const char *key, const char *value, int flags) * @ingroup request */ void onion_request_free(onion_request * req) { + + free(req->cache); ONION_DEBUG0("Free request %p", req); onion_dict_free(req->headers); @@ -132,7 +138,8 @@ void onion_request_free(onion_request * req) { if (req->POST) onion_dict_free(req->POST); if (req->FILES) { - onion_dict_preorder(req->FILES, unlink_files, NULL); + //onion_dict_preorder(req->FILES, unlink_files, NULL); + //onion_dict_preorder(req->FILES, unlink_files, req->connection.listen_point->unlink); onion_dict_free(req->FILES); } if (req->session) { @@ -162,6 +169,19 @@ void onion_request_free(onion_request * req) { onion_ptr_list_foreach(req->free_list, onion_low_free); onion_ptr_list_free(req->free_list); } + +#ifdef HAVE_PTHREADS + if (req->connection.listen_point->multi){ + pthread_mutex_lock( &req->mtx ); + pthread_mutex_unlock( &req->mtx ); + pthread_mutex_destroy( &req->mtx ); + free(req->hash_base); + } +#endif + if (req->hash_ctx){ + req->connection.listen_point->free_hash_ctx(req->hash_ctx); + req->hash_ctx = NULL; + } onion_low_free(req); } @@ -193,7 +213,8 @@ void onion_request_clean(onion_request * req) { req->POST = NULL; } if (req->FILES) { - onion_dict_preorder(req->FILES, unlink_files, NULL); + //onion_dict_preorder(req->FILES, unlink_files, NULL); + //onion_dict_preorder(req->FILES, unlink_files, req->connection.listen_point->unlink_att); onion_dict_free(req->FILES); req->FILES = NULL; } @@ -730,3 +751,25 @@ const char *onion_request_get_cookie(onion_request * req, bool onion_request_is_secure(onion_request * req) { return req->connection.listen_point->secure; } + + +void onion_request_get_hash(onion_request * req, unsigned char* value){ + if (req->hash_ctx && req->connection.listen_point->final_hash_ctx){ +#ifdef HAVE_PTHREADS + if (req->connection.listen_point->multi){ + pthread_mutex_lock( &req->mtx ); + } +#endif + req->connection.listen_point->final_hash_ctx(value, req->hash_ctx); +#ifdef HAVE_PTHREADS + if (req->connection.listen_point->multi){ + pthread_mutex_unlock( &req->mtx ); + } +#endif + } +} + + +onion_listen_point* onion_request_get_listen_point(onion_request* req){ + return req->connection.listen_point; +} diff --git a/src/onion/request.h b/src/onion/request.h index 1a7657b0..64d82b23 100644 --- a/src/onion/request.h +++ b/src/onion/request.h @@ -182,6 +182,11 @@ extern "C" { /// Determine if the request was sent over a secure listen point bool onion_request_is_secure(onion_request * req); + + void onion_request_get_hash(onion_request * req, unsigned char* value); + + onion_listen_point* onion_request_get_listen_point(onion_request* req); + #ifdef __cplusplus } #endif diff --git a/src/onion/request_parser.c b/src/onion/request_parser.c index 1c75c97f..9e55fe2b 100644 --- a/src/onion/request_parser.c +++ b/src/onion/request_parser.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "dict.h" #include "request.h" @@ -345,6 +346,46 @@ static onion_connection_status parse_CONTENT_LENGTH(onion_request * req, return OCS_NEED_MORE_DATA; } +static ssize_t onion_http_flush_att(onion_request* req, int* fd, const char* filename, const char* data, size_t len, off_t _offset){ + off_t offset = _offset; + char* pos = (char*)data; + char* end = (char*)data + len; + while (posconnection.listen_point->att_hndl.pwrite){ + if (*fd==0){ + res = req->connection.listen_point->att_hndl.pwrite(filename, pos, len, offset, req); + //printf("flush s3 %ld at %ld \n", res, offset); + }else{ + res = req->connection.listen_point->att_hndl.pwrite((const char*)fd, pos, len, offset, req); + //printf("flush s3 %ld at %ld \n", res, offset); + } + }else{ + res = len; + //printf("flush nop %ld at %ld \n", res, offset); + } + if (res==-1){ + return res; + } + pos += (size_t) res; + len -= (size_t) res; + offset += res; + } + return end - data; +} + + +#ifdef HAVE_PTHREADS +static void* update_hash_worker(void* args){ + onion_update_hash_params* p = (onion_update_hash_params*)args; + p->update(p->ctx, p->data, p->len); + pthread_mutex_unlock(p->mtx); + pthread_exit( (void*)NULL ); + return (void*) NULL; +} +#endif + + /** * @short Reads from the data to fulfill content-length data. * @@ -352,36 +393,82 @@ static onion_connection_status parse_CONTENT_LENGTH(onion_request * req, */ static onion_connection_status parse_PUT(onion_request * req, onion_buffer * data) { + + const char *filename = onion_block_data(req->data); onion_token *token = req->parser_data; - int length = data->size - data->pos; + int* fd = (int *)token->extra; int exit = 0; + while ( (size_t)data->pos < data->size){ + size_t available = (size_t)(req->end - req->pos); + size_t len = data->size - (size_t)data->pos; + len = MIN(len, available); + memcpy(req->pos, data->data + (size_t)data->pos, len); + + req->pos +=len; + token->pos +=len; + data->pos += len; + if (token->extra_size == (size_t)token->pos){ + exit = 1; + } - if (length >= token->extra_size - token->pos) { - exit = 1; - length = token->extra_size - token->pos; + if (req->pos==req->end || exit==1){ + size_t f_len = (size_t)(req->pos-req->cache); + ssize_t res = onion_http_flush_att(req, fd, filename, + req->cache, f_len, token->pos - f_len); + + if (res==-1){ + if (req->connection.listen_point->att_hndl.close){ + //todo: + if (*fd==0){ + req->connection.listen_point->att_hndl.close(filename); + }else{ + req->connection.listen_point->att_hndl.close((const char*)fd); + } + } + onion_low_free(token->extra); + token->extra = NULL; + ONION_ERROR("Could not write all data to temporal file."); + return OCS_INTERNAL_ERROR; + } +#ifdef HAVE_PTHREADS + if (req->connection.listen_point->multi){ + pthread_mutex_lock( &req->mtx ); + char* link = req->cache; + req->cache = req->hash_base; + req->hash_base = link; + req->end = req->cache + req->connection.listen_point->cache_size; + req->hash_params.data = req->hash_base; + req->hash_params.len = (size_t) res; + pthread_t thread; + pthread_create(&thread, NULL, update_hash_worker, (void*)&req->hash_params); + pthread_detach( thread ); + }else{ + if (req->connection.listen_point->update_hash_ctx) + req->connection.listen_point->update_hash_ctx(req->hash_ctx, req->cache, (size_t)res); + } +#else + if (req->connection.listen_point->update_hash_ctx) + req->connection.listen_point->update_hash_ctx(req->hash_ctx, req->cache, (size_t)res); +#endif + req->pos = req->cache; + } } - //ONION_DEBUG0("Writing %d. %d / %d bytes", length, token->pos+length, token->extra_size); - int *fd = (int *)token->extra; - ssize_t w = write(*fd, &data->data[data->pos], length); - if (w < 0) { - // cleanup - close(*fd); - onion_low_free(token->extra); - token->extra = NULL; - ONION_ERROR("Could not write all data to temporal file."); - return OCS_INTERNAL_ERROR; - } - data->pos += length; - token->pos += length; #if __DEBUG__ - const char *filename = onion_block_data(req->data); + //const char *filename = onion_block_data(req->data); ONION_DEBUG0("Done with PUT. Created %s (%d bytes)", filename, token->pos); #endif if (exit) { - close(*fd); + + if (req->connection.listen_point->att_hndl.close){ + if (*fd==0){ + req->connection.listen_point->att_hndl.close(filename); + }else{ + req->connection.listen_point->att_hndl.close((const char*)fd); + } + } onion_low_free(token->extra); token->extra = NULL; return OCS_REQUEST_READY; @@ -417,15 +504,19 @@ static onion_connection_status parse_POST_multipart_file(onion_request * req, multipart->startpos = multipart->pos = 0; data->pos++; // Not sure why this is needed. FIXME. - int w = write(multipart->fd, tmp, tmppos); + //int w = write(multipart->fd, tmp, tmppos); + //int w = req->connection.listen_point->write_att(multipart->fd, tmp, tmppos); +/* if (w != tmppos) { ONION_ERROR ("Error writing multipart data to file. Check permissions on temp directory, and availabe disk."); - close(multipart->fd); + //close(multipart->fd); + req->connection.listen_point->close_att(multipart->fd); return OCS_INTERNAL_ERROR; } - - close(multipart->fd); +*/ + //close(multipart->fd); + //req->connection.listen_point->close_att(multipart->fd); req->parser = parse_POST_multipart_next; return OCS_NEED_MORE_DATA; } @@ -437,24 +528,32 @@ static onion_connection_status parse_POST_multipart_file(onion_request * req, } if (multipart->pos != 0) { multipart->file_total_size += multipart->pos; - int r = multipart->pos - multipart->startpos; + //int r = multipart->pos - multipart->startpos; //ONION_DEBUG0("Write %d bytes",r); - int w = write(multipart->fd, tmp, tmppos); + //int w = write(multipart->fd, tmp, tmppos); + /* + int w = req->connection.listen_point->write_att(multipart->fd, tmp, tmppos); if (w != tmppos) { ONION_ERROR ("Error writing multipart data to file. Check permissions on temp directory, and availabe disk."); - close(multipart->fd); + //close(multipart->fd); + req->connection.listen_point->close_att(multipart->fd); return OCS_INTERNAL_ERROR; } tmppos = 0; - w = write(multipart->fd, multipart->boundary + multipart->startpos, r); + //w = write(multipart->fd, multipart->boundary + multipart->startpos, r); + */ + //w = req->connection.listen_point->write_att(multipart->fd, multipart->boundary + multipart->startpos, r); + /* if (w != r) { ONION_ERROR ("Error writing multipart data to file. Check permissions on temp directory, and availabe disk."); - close(multipart->fd); + //close(multipart->fd); + req->connection.listen_point->close_att(multipart->fd); return OCS_INTERNAL_ERROR; } + */ multipart->startpos = multipart->pos = 0; data->pos--; // Ignore read charater, try again. May be start of boundary. continue; @@ -462,26 +561,34 @@ static onion_connection_status parse_POST_multipart_file(onion_request * req, //ONION_DEBUG0("Write 1 byte"); tmp[tmppos++] = *p; if (tmppos == sizeof(tmp)) { - int w = write(multipart->fd, tmp, tmppos); + //int w = write(multipart->fd, tmp, tmppos); + /* + int w = req->connection.listen_point->write_att(multipart->fd, tmp, tmppos); if (w != tmppos) { ONION_ERROR ("Error writing multipart data to file. Check permissions on temp directory, and availabe disk."); - close(multipart->fd); + //close(multipart->fd); + req->connection.listen_point->close_att(multipart->fd); return OCS_INTERNAL_ERROR; } + */ tmppos = 0; } } multipart->file_total_size++; p++; } - int w = write(multipart->fd, tmp, tmppos); + //int w = write(multipart->fd, tmp, tmppos); + /* + int w = req->connection.listen_point->write_att(multipart->fd, tmp, tmppos); if (w != tmppos) { ONION_ERROR ("Error writing multipart data to file. Check permissions on temp directory, and availabe disk."); - close(multipart->fd); + //close(multipart->fd); + req->connection.listen_point->close_att(multipart->fd); return OCS_INTERNAL_ERROR; } + */ return OCS_NEED_MORE_DATA; } @@ -644,15 +751,19 @@ static onion_connection_status parse_POST_multipart_headers_key(onion_request * multipart->pos = 0; if (multipart->filename) { - char filename[] = "/tmp/onion-XXXXXX"; - multipart->fd = mkstemp(filename); - if (multipart->fd < 0) - ONION_ERROR("Could not create temporal file at %s.", filename); - if (!req->FILES) - req->FILES = onion_dict_new(); - onion_dict_add(req->POST, multipart->name, multipart->filename, 0); - onion_dict_add(req->FILES, multipart->name, filename, OD_DUP_VALUE); - ONION_DEBUG0("Created temporal file %s", filename); + //char filename[32*2+1]; // + //strcpy(filename, "/tmp/onion-XXXXXX"); + //if (req->connection.listen_point->auth){ + // req->connection.listen_point->auth(req, filename); //generate template for mks_att + //} + //multipart->fd = req->connection.listen_point->mks_att(filename); + //if (multipart->fd < 0) + // ONION_ERROR("Could not create temporal file at %s.", filename); + //if (!req->FILES) + // req->FILES = onion_dict_new(); + //onion_dict_add(req->POST, multipart->name, multipart->filename, 0); + //onion_dict_add(req->FILES, multipart->name, filename, OD_DUP_VALUE); + //ONION_DEBUG0("Created temporal file %s", filename); req->parser = parse_POST_multipart_file; return parse_POST_multipart_file(req, data); @@ -927,6 +1038,7 @@ onion_connection_status onion_request_write(onion_request * req, return r; } parse = req->parser; + // if parse == prepare_PUT } return OCS_NEED_MORE_DATA; } @@ -1114,6 +1226,12 @@ static onion_connection_status prepare_CONTENT_LENGTH(onion_request * req) { return OCS_NEED_MORE_DATA; } +static void to_hex(const unsigned char* data, size_t len, char* buf) { + for(size_t i = 0; i < len; i++){ + sprintf(buf+i*2, "%02x", data[i]); + } +} + /** * @short Prepares the PUT * @@ -1133,30 +1251,75 @@ static onion_connection_status prepare_PUT(onion_request * req) { return OCS_INTERNAL_ERROR; } - req->data = onion_block_new(); + if (cl == 0) { + return OCS_REQUEST_READY; + } + + const size_t hash_len = 32; // str repr of sha256 + const size_t str_hash_len = hash_len*2+1; + char filename[str_hash_len]; + int auth_len = 0; + + if ( req->connection.listen_point->att_hndl.auth){ + auth_len = req->connection.listen_point->att_hndl.auth(req, filename); + if (auth_len==-1){ + return OCS_FORBIDDEN; + }else if(auth_len==0){ + return OCS_REQUEST_READY; + } + } - char filename[] = "/tmp/onion-XXXXXX"; - int fd = mkstemp(filename); - if (fd < 0) - ONION_ERROR("Could not create temporary file at %s.", filename); + //todo: check for 0 auth - onion_block_add_str(req->data, filename); - ONION_DEBUG0("Creating PUT file %s (%d bytes long)", filename, - token->extra_size); + int fd = -1; + if (req->connection.listen_point->att_hndl.open){ + fd = req->connection.listen_point->att_hndl.open(filename, O_WRONLY|O_CREAT, req->connection.listen_point->cache_size); + if (fd < 0 ){ + ONION_ERROR("Could not create temporary file at %s.", filename); + return OCS_INTERNAL_ERROR; + } + } + + req->data = onion_block_new(); + if ((size_t)auth_len == str_hash_len){ + onion_block_add_str(req->data, filename); + }else{ + onion_block_add_data(req->data, filename, auth_len); + } + ONION_DEBUG0("Creating PUT file (%lu bytes long)", cl); if (!req->FILES) { req->FILES = onion_dict_new(); } - { + if ((size_t)auth_len == str_hash_len){ const char *filename = onion_block_data(req->data); onion_dict_add(req->FILES, "filename", filename, 0); + }else{ + //convert bin hash to str hash + char str_filename[str_hash_len]; + to_hex((const unsigned char*)filename, hash_len, str_filename); + onion_dict_add(req->FILES, "filename", str_filename, OD_DUP_VALUE); } - if (cl == 0) { - ONION_DEBUG0("Created 0 length file"); - close(fd); - return OCS_REQUEST_READY; + if (req->connection.listen_point->init_hash_ctx) + req->connection.listen_point->init_hash_ctx(req->hash_ctx); + + if (!req->cache){ + req->cache = (char*) malloc (req->connection.listen_point->cache_size); + } +#ifdef HAVE_PTHREADS + if (req->connection.listen_point->multi){ + if (!req->hash_base){ + req->hash_base = (char*) malloc (req->connection.listen_point->cache_size); + } + pthread_mutex_init(&req->mtx, NULL); + req->hash_params.update = req->connection.listen_point->update_hash_ctx; + req->hash_params.ctx = req->hash_ctx; + req->hash_params.mtx = &(req->mtx); } +#endif + req->pos = req->cache; + req->end = req->cache + req->connection.listen_point->cache_size; int *pfd = onion_low_scalar_malloc(sizeof(fd)); *pfd = fd; @@ -1165,7 +1328,6 @@ static onion_connection_status prepare_PUT(onion_request * req) { token->extra = (char *)pfd; token->extra_size = cl; token->pos = 0; - req->parser = parse_PUT; return OCS_NEED_MORE_DATA; } diff --git a/src/onion/response.c b/src/onion/response.c index eaaf3cae..1905045c 100644 --- a/src/onion/response.c +++ b/src/onion/response.c @@ -647,3 +647,8 @@ bool onion_response_add_cookie(onion_response * res, const char *cookiename, return true; } + +onion_listen_point* onion_response_get_listen_point(onion_response* resp){ + return resp->request->connection.listen_point; +} + diff --git a/src/onion/response.h b/src/onion/response.h index 93fa3e1c..35ff9f71 100644 --- a/src/onion/response.h +++ b/src/onion/response.h @@ -138,6 +138,8 @@ extern "C" { int onion_response_flush(onion_response * res); /// @} + onion_listen_point* onion_response_get_listen_point(onion_response* req); + #ifdef __cplusplus } #endif diff --git a/src/onion/types.h b/src/onion/types.h index 5ef24be9..c283269a 100644 --- a/src/onion/types.h +++ b/src/onion/types.h @@ -138,6 +138,10 @@ extern "C" { struct onion_poller_slot_t; typedef struct onion_poller_slot_t onion_poller_slot; + struct onion_attacment_handlers_t; + typedef struct onion_attachment_handlers_t onion_attachment_handlers; + + /** * @short Stored common data for each listen point: address, port, protocol status data... * @struct onion_listen_point_t diff --git a/src/onion/types_internal.h b/src/onion/types_internal.h index 994333eb..8faa34c9 100644 --- a/src/onion/types_internal.h +++ b/src/onion/types_internal.h @@ -57,6 +57,16 @@ extern "C" { int (*cmp) (const char *a, const char *b); }; +#ifdef HAVE_PTHREADS + typedef struct{ + int (*update)(void* ctx, const void* data, size_t len); + void* ctx; + const void* data; + size_t len; + pthread_mutex_t* mtx; + }onion_update_hash_params; +#endif + struct onion_t { int flags; int timeout; ///< Timeout in milliseconds @@ -110,6 +120,16 @@ extern "C" { void *parser_data; /// Data necesary while parsing, muy be deleted when state changed. At free is simply freed. onion_websocket *websocket; /// Websocket handler. onion_ptr_list *free_list; /// Memory that should be freed when the request finishes. IT allows to have simpler onion_dict, which dont copy/free data, but just splits a long string inplace. + void* hash_ctx; /// Hash context, created&user by user defined callbacks + char* cache; +#ifdef HAVE_PTHREADS + char* hash_base; + pthread_mutex_t mtx; + onion_update_hash_params hash_params; +#endif + char* pos; + char* end; + //char hash_s3[64]; }; struct onion_response_t { @@ -155,12 +175,23 @@ extern "C" { struct onion_url_data_t; typedef struct onion_url_data_t onion_url_data; + struct onion_attachment_handlers_t { + int (*auth)(onion_request* req, char* hash); + int (*open)(const char* hash, int flag, ...); // callback for creating + ssize_t (*pread)(const char* hash, void *data, size_t len, off_t offset); // callback for reading data + ssize_t (*pwrite)(const char* hash, const void *data, size_t len, off_t offset, onion_request* req); // callback for writing data + int (*close)(const char* hash); // callback for closing + int (*unlink)(const char* hash); // callback for unlinking + }; + struct onion_listen_point_t { onion *server; ///< Onion server char *hostname; ///< Stated hostname, as a string. If NULL tries to attach to any hostname, normally 0.0.0.0 (ipv4 and ipv6) char *port; ///< Stated port, if none then 8080 int listenfd; ///< For socket listening listen points, the listen fd. For others may be -1 as not used, or an fd to watch and when changed calls the request_init with a new request. bool secure; ///< Is this listen point secure? + size_t cache_size; + void* context; /// Internal data used by the listen point, for example in HTTPS is the certificate loaded data. void *user_data; @@ -202,6 +233,17 @@ extern "C" { ssize_t(*write) (onion_request * req, const char *data, size_t len); ///< Write data to the given request. ssize_t(*read) (onion_request * req, char *data, size_t len); ///< Read data from the given request and write it in data. void (*close) (onion_request * req); ///< Closes the connection and frees listen point user data. Request itself it left. It is called from onion_request_free ONLY. + + onion_attachment_handlers att_hndl; + + void* (*new_hash_ctx)(); // creates hash context + int (*init_hash_ctx)(void* ctx); // initializes hash context + int (*update_hash_ctx)(void* ctx, const void* data, size_t len); // updates hash context + int (*final_hash_ctx)(unsigned char* data, void* ctx); // finalize hash context + void (*free_hash_ctx)(void* ctx); // releases hash context + bool multi; + + /// @} };