diff options
-rw-r--r-- | examples/upload.c | 9 | ||||
-rw-r--r-- | include/magi/file.h | 4 | ||||
-rw-r--r-- | include/magi/loadfiles.h | 29 | ||||
-rw-r--r-- | include/magi/response.h | 10 | ||||
-rw-r--r-- | man/magi.3 | 195 | ||||
-rw-r--r-- | src/file.c | 6 | ||||
-rw-r--r-- | src/loadfiles.c | 82 |
7 files changed, 274 insertions, 61 deletions
diff --git a/examples/upload.c b/examples/upload.c index c9752b6..6349118 100644 --- a/examples/upload.c +++ b/examples/upload.c @@ -36,12 +36,13 @@ void response(struct magi_request *r) void get(struct magi_request *r) { - struct magi_loadfiles rules = { 0, 0 }; + struct magi_loadfiles *cb; + magi_loadfiles_init(&cb); /* Setup callback to load file from "data" field into file "data": */ - magi_loadfiles_add(&rules, "data", "data", 0); - magi_loadfiles_set(r, &rules); /* Setup request to use the callback. */ + magi_loadfiles_add(&cb, "data", "data", 0); + magi_loadfiles_set(r, &cb); /* Setup request to use the callback. */ magi_parse(r); - magi_loadfiles_free(&rules); /* Free data of callback. */ + magi_loadfiles_free(&cb); /* Free data of callback. */ } int main() diff --git a/include/magi/file.h b/include/magi/file.h index a655855..0623644 100644 --- a/include/magi/file.h +++ b/include/magi/file.h @@ -30,6 +30,10 @@ const struct magi_file *magi_files_get(const struct magi_files *files, const char *name); +/* Get value of parameter with name from file->params. */ +char *magi_file_param(struct magi_file *file, const char *name); + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Callback to load files while analysing request. * addon_len is not null if something to add is in addon diff --git a/include/magi/loadfiles.h b/include/magi/loadfiles.h index 9d7978f..85aeefc 100644 --- a/include/magi/loadfiles.h +++ b/include/magi/loadfiles.h @@ -18,30 +18,31 @@ struct magi_loadfile { int max; /* Limit in bytes. Null means unlimited. */ }; -/* Table of rules for loading files. - * Set count and files as null to initialize. */ +/* Table of rules for loading files as linked list. */ struct magi_loadfiles { - int count; /* Size of files array.*/ - struct magi_loadfile *files; /* Dynamic array of rules to load files. */ + struct magi_loadfile *item; + struct magi_loadfiles *next; }; -/* Free memory used by table. Request using table will become invalid. */ -void magi_loadfiles_free(struct magi_loadfiles *table); +/* Initialise loadfiles. */ +void magi_loadfiles_init(struct magi_loadfiles **loadfiles); +/* Free memory used by loadfiles. Request using table will become invalid. */ +void magi_loadfiles_free(struct magi_loadfiles **loadfiles); -/* Add entity into table. +/* Add entity into loadfiles. * Specify form field to load file from with name, * wnated loaction to load file with path, * and file size limit in bytes with max (pass null to unlimit). */ -void magi_loadfiles_add(struct magi_loadfiles *table, - const char *name, - const char *path, - int max); +void magi_loadfiles_add(struct magi_loadfiles **loadfiles, + const char *name, + const char *path, + int max); -/* Setup request to use loadfiles callback with table. */ -void magi_loadfiles_set(struct magi_request *request, - struct magi_loadfiles *table); +/* Setup request to use loadfiles callback. */ +void magi_loadfiles_set(struct magi_request *request, + struct magi_loadfiles **loadfiles); #endif diff --git a/include/magi/response.h b/include/magi/response.h index 6a49a0c..10c5c1c 100644 --- a/include/magi/response.h +++ b/include/magi/response.h @@ -37,19 +37,19 @@ void magi_response_status(struct magi_response *r, /* Add cookie to response. */ void magi_response_cookie(struct magi_response *r, - const char *n, - const char *d); + const char *name, + const char *data); /* Add cookie with additional information to response. */ void magi_response_cookie_complex(struct magi_response *r, const struct magi_cookie *c); /* Add request to discard cookie to response. */ void magi_response_cookie_discard(struct magi_response *r, - const char *name); + const char *name); /* Just add some general custom header. */ void magi_response_header(struct magi_response *r, - const char *n, - const char *d); + const char *name, + const char *data); /* Change Content-Length header. */ void magi_response_content_length(struct magi_response *r, int length); @@ -218,7 +218,197 @@ structure. If no cookie is found, result is null. It has fields .I max_age (the last one is used only in response). .SS Files in request +While ordinary parameters are not very big and don't need special processing, +files can be huge and need specail processing. +Due to their size, files shouldn't be loaded into memory in general. +The +.B magi +library allows you to setup your own callback for file loading, +or use predefined one, +.BR magi_loadfiles . +.P +You can load file from field +.I to_load +into +.I ./uploaded +with limits on its size of 1MB as following: +.P +.RS +.nf +struct magi_loadfiles cb; +magi_loadfiles_init(&cb); +magi_loadfiles_add(&cb, "to_load", "./uploaded", 1024 * 1024); +magi_loadfiles_set(&request, &cb); +magi_parse_body(&request); +magi_loadfiles_free(&rules); +/* Use file ./uploaded */ +.fi +.RE +.P +There +.I request +is initialised +.B magi_request +with parsed head and not parsed body. +.P +To access information about file, use +.B magi_request_file . +It will get your +.B magi_file +structure for file loaded from field with passed name. +This structure have file name on user host in +.I filename +field. +Other parameters (as Content-Type) are in +.I params +field, accessible with +.BR magi_file_param . +.P +For example, we can get user feedback, returning Content-Type of loaded file: +.P +.RS +.nf +struct magi_file *loaded = magi_request_file(&request, "to_load"); +if (loaded) { + char *type = magi_file_param(loaded, "Content-Type"); + if (type_to_response) { + /* Report the user that file of 'type' was loaded. */ + } else { + /* Report the user that file was loaded without + specified Content-Type. */ + } +} else { + /* Report the user that file wasn't loaded. */ +} +.SS File processing callback +In some cases +.B magi_loadfiles +can be not enough. +Then you can specify your own +.BR magi_file_callback . +The +.I act +field contains the callback function itself. +The +.I userdata +field has type +.I "void *" +allowing you to exchange state across different calls of callback. +The +.I addon_max +field specify how much bytes can be passed to your callback with one call. +.P +Callback function has type +.BR magi_file_callback_act , +which is function returning void, with +.IR userdata , +.I newfile +flag, +.I file +.B magi_file +structure, +.I addon +and +.IR addon_len . +.P +Files are passed sequantially addon by addon. +At the file end callback will be called with +.I addon +and +.I addon_len +as nulls. +If current +.I addon +is first in current +.I file +.I newfile +flag will be setted up. +.P +You can load file from field 'file' into current directory with name, +as specified by +.I filename +field as: +.P +.RS +.nf +static void cb(void *userdata, + int newfile, + const struct magi_file *file, + const char *addon, + int addon_len) +{ + static FILE *f = 0; + if (!strcmp("file", file->field)) { + if (newfile) { + f = fopen(file->filename, "wb"); + } + fwrite(addon, 1, addon_len, f); + if (!addon) { + fclose(f); + } + } +} +.fi +.RE +.P +Set it as callback for processing files for initialised +.I request +with parsed head and not parsed body, and then parse the body: +.P +.RS +.nf +request.callback.act = cb; +magi_parse_body(&request); +/* Now file is loaded into filename */ +.fi +.RE .SH RESPONSE +Response headers are formed with +.B magi_response +structure. +It is initiated with +.BR magi_response_init , +sent with +.BR magi_response_send , +and freed with +.BR magi_response_free . +The only defaults are +.I text/html +as Content-Type and +.I 200 Ok +as status. +You can send them with +.BR magi_response_default . +.P +.BR magi_response_content_type , +.B magi_reponse_content_length +and +.B magi_response_status +are used to change corresponding headers. +Any other header can be only added, not changed with +.BR magi_response_header . +Don't use it in cases above. +.P +For cookies use +.B magi_response_cookie +to set cookies and +.B magi_response_cookie_discard +to discrad them. +In case of Cookie2 use +.BR magi_response_cookie_complex . +.P +Lets send headers for XHTML body, setting cookie 'monster' as 'cookie': +.P +.RS +.nf +struct magi_response response; +magi_response_init(&response); +magi_response_content_type(&response, "application/xhtml+xml"); +magi_response_cookie(&response, "monster", "cookie"); +magi_response_send(&response); +magi_response_free(&response); +.fi +.RE .SS URL encoding It is described in .IR "RFC 3986" . @@ -271,6 +461,11 @@ code is in field of .B magi_request structure. For other modules error codes seem to be overkill. +.P +You can access default error message with +.B magi_error_message +or send default error page with +.BR magi_error_response . .SH DEBUGGING To debug your CGI scripts with .I gdb @@ -38,3 +38,9 @@ void magi_files_free(struct magi_files *files) free(files->item.params); } } + + +char *magi_file_param(struct magi_file *file, const char *name) +{ + return file ? magi_params_get(file->params, name) : 0; +} diff --git a/src/loadfiles.c b/src/loadfiles.c index 49cf503..9d7043c 100644 --- a/src/loadfiles.c +++ b/src/loadfiles.c @@ -5,55 +5,25 @@ #include <string.h> -void magi_loadfiles_add(struct magi_loadfiles *table, - const char *name, - const char *path, - int max) -{ - static const int size = sizeof(*table->files); - if (!table) { - return; - } - if (table->count) { - table->files = realloc(table->files, size * table->count + size); - } else { - table->files = malloc(size); - } - table->files[table->count].name = name; - table->files[table->count].path = path; - table->files[table->count].max = max; - table->count++; -} - -void magi_loadfiles_free(struct magi_loadfiles *table) -{ - if (!table) { - return; - } - free(table->files); - table->count = 0; -} - static void loadfiles_callback(void *userdata, int newfile, const struct magi_file *file, const char *addon, int addon_len) { - int pos; - struct magi_loadfiles *table = userdata; + struct magi_loadfiles *loadfiles = *(struct magi_loadfiles **)userdata; if (!file->filename || !*file->filename) { return; } - for (pos = 0; pos != table->count; ++pos) { - if (!strcmp(table->files[pos].name, file->field)) { + for (; loadfiles; loadfiles = loadfiles->next) { + if (!strcmp(loadfiles->item->name, file->field)) { static FILE *f = 0; static int unlimited; static int left; if (newfile) { - const char *path = table->files[pos].path; + const char *path = loadfiles->item->path; f = fopen(path, "wb"); - left = table->files[pos].max; + left = loadfiles->item->max; unlimited = !left; } if (unlimited) { @@ -71,9 +41,45 @@ static void loadfiles_callback(void *userdata, } } -void magi_loadfiles_set(struct magi_request *request, - struct magi_loadfiles *table) + +void magi_loadfiles_init(struct magi_loadfiles **loadfiles) +{ + *loadfiles = 0; +} + +void magi_loadfiles_free(struct magi_loadfiles **loadfiles) +{ + if (!loadfiles || !*loadfiles) { + return; + } + free((*loadfiles)->item); + magi_loadfiles_free(&(*loadfiles)->next); + free(*loadfiles); +} + + +void magi_loadfiles_add(struct magi_loadfiles **loadfiles, + const char *name, + const char *path, + int max) +{ + struct magi_loadfiles *next; + if (!loadfiles) { + return; + } + next = *loadfiles ? (*loadfiles)->next : 0; + *loadfiles = malloc(sizeof(**loadfiles)); + (*loadfiles)->item = malloc(sizeof(struct magi_loadfile)); + (*loadfiles)->item->name = name; + (*loadfiles)->item->path = path; + (*loadfiles)->item->max = max; + (*loadfiles)->next = next; +} + + +void magi_loadfiles_set(struct magi_request *request, + struct magi_loadfiles **loadfiles) { request->callback.act = loadfiles_callback; - request->callback.userdata = table; + request->callback.userdata = loadfiles; } |