diff options
Diffstat (limited to 'nanohttp')
-rwxr-xr-x | nanohttp/nanohttp-mime.c | 937 | ||||
-rwxr-xr-x | nanohttp/nanohttp-mime.h | 59 | ||||
-rwxr-xr-x | nanohttp/nanohttp-request.c | 284 | ||||
-rwxr-xr-x | nanohttp/nanohttp-request.h | 57 | ||||
-rwxr-xr-x | nanohttp/nanohttp-response.c | 213 | ||||
-rwxr-xr-x | nanohttp/nanohttp-response.h | 57 | ||||
-rwxr-xr-x | nanohttp/todo.txt | 19 |
7 files changed, 1626 insertions, 0 deletions
diff --git a/nanohttp/nanohttp-mime.c b/nanohttp/nanohttp-mime.c new file mode 100755 index 0000000..51f3219 --- /dev/null +++ b/nanohttp/nanohttp-mime.c @@ -0,0 +1,937 @@ +/****************************************************************** +* _ _ _ _ _ __ +* | \/ | | | | \/ | | _/ +* |_''_| |_| |_''_| |_'/ PARSER +* +* $Id: nanohttp-mime.c,v 1.1 2004/10/15 13:30:42 snowdrop Exp $ +* +* CSOAP Project: A http client/server library in C +* Copyright (C) 2003-2004 Ferhat Ayaz +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* Email: ferhatayaz@yahoo.com +******************************************************************/ + + + +#include <nanohttp/nanohttp-mime.h> +#include <stdio.h> + +/*---------------------------------------------------------------- +Buffered Reader. A helper object to read bytes from a source +----------------------------------------------------------------*/ + +#ifdef MEM_DEBUG +#include <utils/alloc.h> +#endif + + + +/* ------------------------------------------------------------------ + MIME Parser + ------------------------------------------------------------------*/ +typedef void (*MIME_part_begin) (void*); +typedef void (*MIME_part_end) (void*); +typedef void (*MIME_parse_begin) (void*); +typedef void (*MIME_parse_end) (void*); +typedef void (*MIME_ERROR_bytes) (void*, + const unsigned char*, size_t); + +typedef enum _MIME_parser_status +{ + MIME_PARSER_INCOMPLETE_MESSAGE, + MIME_PARSER_READ_ERROR, + MIME_PARSER_OK +}MIME_parser_status; + +typedef enum _MIME_read_status +{ + MIME_READ_OK, + MIME_READ_EOF, + MIME_READ_ERROR +}MIME_read_status; + +#define MIME_READER_MAX_BUFFER_SIZE 1054 +#define MIME_PARSER_BUFFER_SIZE 1054 + + +typedef MIME_read_status (*MIME_read_function) (void*, unsigned char*, int*); + + +/** + Reader structure. This will be use + by the parser +*/ +typedef struct _MIME_reader +{ + int size; + int marker; + int current; + MIME_read_function read_function; + char buffer[MIME_READER_MAX_BUFFER_SIZE]; + void *userdata; +}MIME_reader; + + +MIME_read_status MIME_filereader_function(void* userdata, + unsigned char* dest, int* size); + +typedef struct _MIME_callbacks +{ + MIME_part_begin part_begin_cb; + MIME_part_end part_end_cb; + MIME_parse_begin parse_begin_cb; + MIME_parse_end parse_end_cb; + MIME_ERROR_bytes received_bytes_cb; +}MIME_callbacks; + + +MIME_parser_status MIME_parse( + MIME_read_function reader_function, + void *reader_userdata, + const char* user_boundary, + const MIME_callbacks* callbacks, + void *callbacks_userdata +); + + +/** + Initialize a reader +*/ +void MIME_reader_init(MIME_reader *reader, + MIME_read_function reader_function,void *userdata) +{ + reader->size = 0; + reader->marker = -1; + reader->current = 0; + reader->userdata = userdata; + reader->read_function = reader_function; + +} + +/** + Read data from a reader source. +*/ +MIME_read_status MIME_reader_read(MIME_reader *reader, + unsigned char *buffer, size_t size) +{ + MIME_read_status status; + size_t readed_size; + unsigned char tempBuffer[MIME_READER_MAX_BUFFER_SIZE]; + int rest_size; + + /* Check if buffer is full */ + if (reader->size == reader->current) + { + /* Yes, so read some data */ + + /* First handle marker */ + if (reader->marker > -1) + { + if (reader->marker != 0) + { + memcpy(tempBuffer, reader->buffer + reader->marker, + reader->size - reader->marker); + memcpy(reader->buffer, tempBuffer, + reader->size - reader->marker); + reader->current = reader->size - reader->marker; + } + else if (reader->current == MIME_READER_MAX_BUFFER_SIZE-1) + { + fprintf(stderr, "Marker error"); + return MIME_READ_ERROR; + } + reader->marker = 0; + } + else + reader->current = 0; + + readed_size = MIME_READER_MAX_BUFFER_SIZE - reader->current -1; + status = reader->read_function(reader->userdata, + reader->buffer + reader->current, &readed_size); + + if (status == MIME_READ_OK) + { + reader->size = readed_size + reader->current; + } + else + return status; + } + + if (size <= reader->size - reader->current) + { + memcpy(buffer, reader->buffer + reader->current, size); + reader->current += size; + return MIME_READ_OK; + } + else + { + /* Fill rest data */ + rest_size = reader->size - reader->current; + memcpy(buffer, reader->buffer + reader->current, rest_size); + + reader->current = reader->size; + return MIME_reader_read(reader, buffer + rest_size, + size - rest_size); + } +} + + +void MIME_reader_set_marker(MIME_reader *reader) +{ + reader->marker = reader->current; +} + +void MIME_reader_unset_marker(MIME_reader *reader) +{ + reader->marker = -1; +} + +void MIME_reader_jump_marker(MIME_reader *reader) +{ + reader->current = reader->marker; +} + + + +typedef struct _MIME_buffer +{ + unsigned char data[MIME_PARSER_BUFFER_SIZE]; + int size; +}MIME_buffer; + + +void MIME_buffer_init(MIME_buffer *buffer) +{ + buffer->size = 0; +} + +void MIME_buffer_add(MIME_buffer *buffer, unsigned char ch) +{ + buffer->data[buffer->size++] = ch; +} + +void MIME_buffer_add_bytes(MIME_buffer *buffer, unsigned char *bytes, int size) +{ + memcpy(buffer->data, bytes, size); + buffer->size += size; +} + +int MIME_buffer_is_full(MIME_buffer *buffer) +{ + return buffer->size + 150 >= MIME_PARSER_BUFFER_SIZE; +} + +int MIME_buffer_is_empty(MIME_buffer *buffer) +{ + return buffer->size == 0; +} + +int MIME_buffer_clear(MIME_buffer *buffer) +{ + buffer->size = 0; +} + + +MIME_parser_status MIME_parse( + MIME_read_function reader_function, + void *reader_userdata, + const char* user_boundary, + const MIME_callbacks* callbacks, + void *callbacks_userdata +) +{ + char boundary[150]; + unsigned char ch[153]; + int boundary_length, n, ignore = 0; + MIME_reader reader; + MIME_buffer buffer; + MIME_read_status status; + + /* Init reader */ + MIME_reader_init(&reader, reader_function, reader_userdata); + + /* Init buffer */ + MIME_buffer_init(&buffer); + + /* Set boundary related stuff */ + sprintf(boundary, "\n--%s", user_boundary); + boundary_length = strlen(boundary); + + /* Call parse begin callback */ + callbacks->parse_begin_cb(callbacks_userdata); + + while (1) + { +set_marker: + + /* Set marker */ + MIME_reader_set_marker(&reader); + +read_byte: + + /* Read 1 byte */ + status = MIME_reader_read(&reader, ch, 1); + _log_str("buffer.log", ch,1); + if (status == MIME_READ_EOF) + return MIME_PARSER_INCOMPLETE_MESSAGE; + else if (status == MIME_READ_ERROR) + return MIME_PARSER_READ_ERROR; + + if (ch[0] == '\r' && !ignore) + { + n = 0; + while (n < boundary_length) + { + /* Read 1 byte */ + status = MIME_reader_read(&reader, ch, 1); + _log_str("buffer.log", ch,1); + if (status == MIME_READ_EOF) + return MIME_PARSER_INCOMPLETE_MESSAGE; + else if (status == MIME_READ_ERROR) + return MIME_PARSER_READ_ERROR; + + /* Check if byte is in boundary */ + if (ch[0] == boundary[n]) + { + n = n + 1; + continue; + } + else + { + MIME_reader_jump_marker(&reader); + ignore = 1; + goto read_byte; + } + + } /* while n < boundary_length*/ + + /* Read 1 byte */ + status = MIME_reader_read(&reader, ch, 1); + _log_str("buffer.log", ch,1); + + + if (status == MIME_READ_EOF) + return MIME_PARSER_INCOMPLETE_MESSAGE; + else if (status == MIME_READ_ERROR) + return MIME_PARSER_READ_ERROR; + + /* Show if byte is '\r' */ + if (ch[0] == '\r') + { + /* Read 1 byte */ + status = MIME_reader_read(&reader, ch, 1); + _log_str("buffer.log", ch,1); + + + if (status == MIME_READ_EOF) + return MIME_PARSER_INCOMPLETE_MESSAGE; + else if (status == MIME_READ_ERROR) + return MIME_PARSER_READ_ERROR; + + /* Check if byte is '\n' */ + if (ch[0] == '\n') + { + if (!MIME_buffer_is_empty(&buffer)) + { + /* Invoke callback */ + callbacks->received_bytes_cb( + callbacks_userdata, buffer.data, buffer.size); + + /* Empty the buffer */ + MIME_buffer_clear(&buffer); + + /* Invoke End Part */ + callbacks->part_end_cb(callbacks_userdata); + } + + /* Invoke start new Part */ + callbacks->part_begin_cb(callbacks_userdata); + goto set_marker; + + } /* if (ch[0] == '\n') */ + else + { + /* Jump to marker and read bytes */ + MIME_reader_jump_marker(&reader); + MIME_reader_read(&reader, ch, boundary_length+2); + _log_str("buffer.log", ch,1); + + MIME_buffer_add_bytes(&buffer, ch, boundary_length+2); + + if (MIME_buffer_is_full(&buffer)) + { + /* Invoke callback */ + callbacks->received_bytes_cb( + callbacks_userdata, buffer.data, buffer.size); + + + /* Empty the buffer */ + MIME_buffer_clear(&buffer); + } + } /* else of if (ch[0] == '\n') */ + + } /* if (ch[0] == '\r') */ + else + { + if (ch[0] == '-') + { + /* Read 1 byte */ + status = MIME_reader_read(&reader, ch, 1); + _log_str("buffer.log", ch,1); + + if (status == MIME_READ_EOF) + return MIME_PARSER_INCOMPLETE_MESSAGE; + else if (status == MIME_READ_ERROR) + return MIME_PARSER_READ_ERROR; + + if (ch[0] == '-') + { + if (!MIME_buffer_is_empty(&buffer)) + { + /* Invoke callback */ + callbacks->received_bytes_cb( + callbacks_userdata, buffer.data, buffer.size); + + /* Empty the buffer */ + MIME_buffer_clear(&buffer); + + /* Invoke End Part */ + callbacks->part_end_cb(callbacks_userdata); + } + + /* Invoke start new Part */ + callbacks->parse_end_cb(callbacks_userdata); + + /* Finish parsing*/ + /* TODO (#1#): We assume that after -- comes \r\n + This is not always correct */ + + return MIME_PARSER_OK; + + } /* if (ch[0] == '-') */ + else + { + MIME_reader_jump_marker(&reader); + ignore = 1; + goto read_byte; + } /* else of if (ch[0] == '-') */ + + } /* if (ch[0] == '-') */ + else + { + MIME_reader_jump_marker(&reader); + ignore = 1; + goto read_byte; + } /* else of if (ch[0] == '-') */ + + } /* else of if (ch[0] == '\r') */ + + } /* if ch[0] == '\r' && !ignore */ + else + { + ignore = 0; + MIME_buffer_add(&buffer, ch[0]); + + /* Chec if buffer is full*/ + if (MIME_buffer_is_full(&buffer)) + { + /* Invoke callback */ + callbacks->received_bytes_cb( + callbacks_userdata, buffer.data, buffer.size); + + /* Empty the buffer */ + MIME_buffer_clear(&buffer); + } + } /* else of if ch[0] == '\r' && !ignore */ + } /* while (1) */ +} + + + + +MIME_read_status MIME_filereader_function(void* userdata, + unsigned char* dest, int* size) +{ + FILE *f = (FILE*)userdata; + + if (feof(f)) + return MIME_READ_EOF; + + *size = fread(dest, 1, *size, f); + return MIME_READ_OK; +} + + +/* ------------------------------------------------------------------ + "multipart/related" MIME Message Builder + ------------------------------------------------------------------*/ + +/* + Callback data to use internally +*/ +typedef struct _mime_callback_data +{ + int part_id; + attachments_t *message; + part_t *current_part; + int buffer_capacity; + char header[4064]; + char root_id[256]; + int header_index; + int header_search; + FILE *current_fd; + char root_dir[512]; +}mime_callback_data_t; + + + +MIME_read_status mime_streamreader_function(void* userdata, + unsigned char* dest, int* size) +{ + int readed=0; + http_input_stream_t *in = (http_input_stream_t*)userdata; + + if (!http_input_stream_is_ready(in)) + return MIME_READ_EOF; + + readed = http_input_stream_read(in, dest, *size); + *size = readed; + if (*size!=-1) { + _log_str("reader.log", dest, *size); + return MIME_READ_OK; + } + + return MIME_READ_ERROR; +} + + +/* + Start Callback functions +*/ +static +void _mime_parse_begin(void *data) +{ +/* Nothing to do + mime_callback_data_t *cbdata = (mime_callback_data_t)data; + */ + log_verbose2("Begin parse (%p)", data); +} + + +static +void _mime_parse_end(void *data) +{ +/* Nothing to do + mime_callback_data_t *cbdata = (mime_callback_data_t)data; + */ + log_verbose2("End parse (%p)", data); +} + + +static +void _mime_part_begin(void *data) +{ + char buffer[1054]; + mime_callback_data_t *cbdata = (mime_callback_data_t*)data; + part_t *part; + + log_verbose2("Begin Part (%p)", data); + part = (part_t *)malloc(sizeof(part_t)); + part->next = NULL; + + + if (cbdata->current_part) + cbdata->current_part->next = part; + + cbdata->current_part = part; + + if (!cbdata->message->parts) + cbdata->message->parts = part; + + cbdata->header[0] = '\0'; + cbdata->header_index = 0; + cbdata->header_search = 0; + +#ifdef WIN32 + sprintf(buffer, "%s\\mime_%p_%d.part", cbdata->root_dir, + cbdata, cbdata->part_id++); +#else + sprintf(buffer, "%s/mime_%p_%d.part", cbdata->root_dir, + cbdata, cbdata->part_id++); +#endif + + cbdata->current_fd = fopen(buffer, "wb"); + if (cbdata->current_fd) + strcpy(cbdata->current_part->filename, buffer); + else + log_error2("Can not open file for write '%s'", buffer); +} + + +static +void _mime_part_end(void *data) +{ + mime_callback_data_t *cbdata = (mime_callback_data_t*)data; + log_verbose2("End Part (%p)", data); + if (cbdata->current_fd) + { + fclose(cbdata->current_fd); + cbdata->current_fd = NULL; + } +} + + +static +hpair_t *_mime_process_header(char *buffer) +{ + int i=0, c=0, proc_key, begin=0; + hpair_t *first = NULL, *last = NULL; + char key[1054], value[1054]; + + key[0] = '\0'; + value[0] = '\0'; + proc_key = 1; + + while (buffer[i] != '\0') + { + if (buffer[i] == '\r' && buffer[i+1] == '\n') + { + value[c] = '\0'; + if (last) + { + last->next = hpairnode_new(key, value, NULL); + last = last->next; + } + else + { + first = last = hpairnode_new(key, value, NULL); + } + proc_key = 1; + c = 0; + i++; + } + else if (buffer[i] == ':') + { + key[c] = '\0'; + c = 0; + begin = 0; + proc_key = 0; + } + else + { + if (proc_key) + key[c++] = buffer[i]; + else + { + if (buffer[i] != ' ') + begin = 1; + if (begin) + value[c++] = buffer[i]; + } + } + i++; + } + return first; +} + + +static +void _mime_received_bytes(void *data, const unsigned char* bytes, size_t size) +{ + int i=0; + char *id; + mime_callback_data_t *cbdata = (mime_callback_data_t*)data; + + if (!cbdata ) { + log_error1("MIME transport error Called <received bytes> without initializing\n"); + return; + } + if (!cbdata->current_part) { + log_error1("MIME transport error Called <received bytes> without initializing\n"); + return; + } +/* log_verbose4("Received %d bytes (%p), header_search = %d", + size, data, cbdata->header_search); +*/ + if (cbdata->header_search < 4) + { + /* Find \r\n\r\n in bytes */ + for (i=0;i<size;i++) + { + if (cbdata->header_search == 0) + { + if (bytes[i] == '\r') + cbdata->header_search++; + else + { + cbdata->header[cbdata->header_index++] = bytes[i]; + cbdata->header_search = 0; + } + } + + else if (cbdata->header_search == 1) + { + if (bytes[i] == '\n') + cbdata->header_search++; + else + { + cbdata->header[cbdata->header_index++] = '\r'; + cbdata->header[cbdata->header_index++] = bytes[i]; + cbdata->header_search = 0; + } + } + + else if (cbdata->header_search == 2) + { + if (bytes[i] == '\r') + cbdata->header_search++; + else + { + cbdata->header[cbdata->header_index++] = '\r'; + cbdata->header[cbdata->header_index++] = '\n'; + cbdata->header[cbdata->header_index++] = bytes[i]; + cbdata->header_search = 0; + } + } + + else if (cbdata->header_search == 3) + { + if (bytes[i] == '\n') + { + cbdata->header[cbdata->header_index++] = '\r'; + cbdata->header[cbdata->header_index++] = '\n'; + cbdata->header[cbdata->header_index++] = '\0'; + cbdata->header_search = 4; + cbdata->current_part->header = _mime_process_header(cbdata->header); + hpairnode_dump_deep(cbdata->current_part->header); + /* set id */ + id = hpairnode_get(cbdata->current_part->header, HEADER_CONTENT_ID); + if (id != NULL) + { + strcpy(cbdata->current_part->id, id); + if (!strcmp(id, cbdata->root_id)) + cbdata->message->root_part = cbdata->current_part; + } + i++; + break; + } + else + { + cbdata->header[cbdata->header_index++] = '\r'; + cbdata->header[cbdata->header_index++] = '\n'; + cbdata->header[cbdata->header_index++] = '\r'; + cbdata->header[cbdata->header_index++] = bytes[i]; + cbdata->header_search = 0; + } + } + /* TODO (#1#): Check for cbdata->header overflow */ + + } /* for (i=0;i<size;i++) */ + } /* if (cbdata->header_search < 4) */ + + if (i >= size-1) + return; + + /* Write remaining bytes into the file or buffer (if root) + (buffer is disabled in this version) */ + if (cbdata->current_fd) + fwrite(&(bytes[i]), 1, size - i, cbdata->current_fd); +} + + +/* + The mime message parser +*/ + +attachments_t * +mime_message_parse(http_input_stream_t *in, const char* root_id, + const char* boundary, const char* dest_dir) +{ + MIME_parser_status status; + MIME_callbacks callbacks; + attachments_t* message; + + mime_callback_data_t *cbdata = (mime_callback_data_t*) + malloc(sizeof(mime_callback_data_t)); + + cbdata->part_id = 100; + cbdata->buffer_capacity = 0; + cbdata-> current_fd = NULL; + cbdata->current_part = NULL; + cbdata->header_index = 0; + cbdata->header_search = 0; + strcpy(cbdata->root_id, root_id); + strcpy(cbdata->root_dir, dest_dir); + message = (attachments_t*)malloc(sizeof(attachments_t)); + cbdata->message = message; + cbdata->message->parts = NULL; + cbdata->message->root_part = NULL; + + callbacks.parse_begin_cb = _mime_parse_begin; + callbacks.parse_end_cb = _mime_parse_end; + callbacks.part_begin_cb = _mime_part_begin; + callbacks.part_end_cb = _mime_part_end; + callbacks.received_bytes_cb = _mime_received_bytes; + + status = MIME_parse(mime_streamreader_function, + in, boundary, &callbacks, cbdata); + + if (status == MIME_PARSER_OK) + { + free(cbdata); + return message; + } + else + { + log_error2("MIME parser error '%s'!", + status == MIME_PARSER_READ_ERROR ? "read error" : "Incomplete message"); + return NULL; + } +} + +attachments_t * +mime_message_parse_from_file(FILE *in, const char* root_id, + const char* boundary, const char* dest_dir) +{ + MIME_parser_status status; + MIME_callbacks callbacks; + attachments_t* message; + + mime_callback_data_t *cbdata = (mime_callback_data_t*) + malloc(sizeof(mime_callback_data_t)); + + cbdata->part_id = 100; + cbdata->buffer_capacity = 0; + cbdata-> current_fd = NULL; + cbdata->current_part = NULL; + cbdata->header_index = 0; + cbdata->header_search = 0; + strcpy(cbdata->root_id, root_id); + strcpy(cbdata->root_dir, dest_dir); + message = (attachments_t*)malloc(sizeof(attachments_t)); + cbdata->message = message; + cbdata->message->parts = NULL; + cbdata->message->root_part = NULL; + + callbacks.parse_begin_cb = _mime_parse_begin; + callbacks.parse_end_cb = _mime_parse_end; + callbacks.part_begin_cb = _mime_part_begin; + callbacks.part_end_cb = _mime_part_end; + callbacks.received_bytes_cb = _mime_received_bytes; + + status = MIME_parse(MIME_filereader_function, + in, boundary, &callbacks, cbdata); + + if (status == MIME_PARSER_OK) + { + free(cbdata); + return message; + } + else + { + /* TODO (#1#): Free objects */ + + log_error2("MIME parser error '%s'!", + status == MIME_PARSER_READ_ERROR ? "general error" : "Incomplete message"); + return NULL; + } +} + +/* + Free a mime part +*/ +void +mime_part_free(part_t *part) +{ + if (part == NULL) + return; + + hpairnode_free_deep(part->header); + + free(part); +} + + + + +hstatus_t mime_get_attachments(content_type_t *ctype, http_input_stream_t *in, attachments_t **dest) +{ + /* MIME variables */ + attachments_t *mimeMessage; + part_t *part, *tmp_part=NULL; + char *boundary, *root_id; + + /* Check for MIME message */ + if (!(ctype && + !strcmp(ctype->type, "multipart/related"))) + return MIME_ERROR_NOT_MIME_MESSAGE; + + boundary = hpairnode_get(ctype->params, "boundary"); + root_id = hpairnode_get(ctype->params, "start"); + if (boundary == NULL) + { + /* TODO (#1#): Handle Error in http form */ + log_error1("'boundary' not set for multipart/related"); + return MIME_ERROR_NO_BOUNDARY_PARAM; + } + + if (root_id == NULL) + { + /* TODO (#1#): Handle Error in http form */ + log_error1("'start' not set for multipart/related"); + return MIME_ERROR_NO_START_PARAM; + } + + /* TODO (#1#): Set this not to working directory + This must be configured */ + + mimeMessage = mime_message_parse(in, root_id, boundary, "."); + if (mimeMessage == NULL) + { + /* TODO (#1#): Handle Error in http form */ + log_error1("MIME Parse Error"); + return MIME_ERROR_PARSE_ERROR; + } + + /* Find root */ + if (!mimeMessage->root_part) + { + attachments_free(mimeMessage); + return MIME_ERROR_NO_ROOT_PART; + } + + /* delete root_part from list */ + part = mimeMessage->parts; + while (part) { + if (part == mimeMessage->root_part) + { + if (tmp_part) + tmp_part->next = part->next; + else + mimeMessage->parts = part->next; + + break; + } + tmp_part = part; + part = part->next; + } + *dest = mimeMessage; + return H_OK; +} + + diff --git a/nanohttp/nanohttp-mime.h b/nanohttp/nanohttp-mime.h new file mode 100755 index 0000000..e863488 --- /dev/null +++ b/nanohttp/nanohttp-mime.h @@ -0,0 +1,59 @@ +/****************************************************************** +* _ _ _ _ _ __ +* | \/ | | | | \/ | | _/ +* |_''_| |_| |_''_| |_'/ PARSER +* +* $Id: nanohttp-mime.h,v 1.1 2004/10/15 13:30:42 snowdrop Exp $ +* +* CSOAP Project: A http client/server library in C +* Copyright (C) 2003-2004 Ferhat Ayaz +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* Email: ferhatayaz@yahoo.com +******************************************************************/ + +#ifndef NANO_HTTP_MIME_PARSER_H +#define NANO_HTTP_MIME_PARSER_H + +#include <stdlib.h> + +#include <nanohttp/nanohttp-stream.h> +#include <nanohttp/nanohttp-common.h> + +/* ------------------------------------------------------------------ + "multipart/related" MIME Message Builder + ------------------------------------------------------------------*/ + + + +hstatus_t mime_get_attachments(content_type_t *ctype, + http_input_stream_t *in, attachments_t **dest); + + +#endif + + + + + + + + + + + + diff --git a/nanohttp/nanohttp-request.c b/nanohttp/nanohttp-request.c new file mode 100755 index 0000000..bed59f2 --- /dev/null +++ b/nanohttp/nanohttp-request.c @@ -0,0 +1,284 @@ +/****************************************************************** +* $Id: nanohttp-request.c,v 1.1 2004/10/15 13:30:42 snowdrop Exp $ +* +* CSOAP Project: A http client/server library in C +* Copyright (C) 2003 Ferhat Ayaz +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* Email: ayaz@jprogrammer.net +******************************************************************/ + +#include <nanohttp/nanohttp-common.h> +#include <nanohttp/nanohttp-request.h> + +#ifdef MEM_DEBUG +#include <utils/alloc.h> +#endif + +/* request stuff */ + + +static +hrequest_t *hrequest_new() +{ + hrequest_t * req = (hrequest_t *) malloc(sizeof(hrequest_t)); + + req->method = HTTP_REQUEST_GET; + req->version = HTTP_1_1; + req->query = NULL; + req->header = NULL; + req->in = NULL; + req->attachments = NULL; + req->content_type = NULL; + +} + +static +hrequest_t * +_hrequest_parse_header(char *data) +{ + hrequest_t *req; + hpair_t *hpair = NULL, *qpair = NULL, *tmppair = NULL; + + char *tmp; + char *tmp2; + char *saveptr; + char *saveptr2; + char *saveptr3; + char *result; + char *key; + char *value; + char *opt_key; + char *opt_value; + int firstline = 1; + + req = hrequest_new(); + tmp = data; + + for (;;) { + result = (char *) strtok_r(tmp, "\r\n", &saveptr); + tmp = saveptr; + + if (result == NULL) + break; + + if (firstline) { + firstline = 0; + tmp2 = result; + + /* parse [GET|POST] [PATH] [SPEC] */ + key = (char *) strtok_r(tmp2, " ", &saveptr2); + + /* save method (get or post) */ + tmp2 = saveptr2; + if (key != NULL) { + if (!strcmp(key, "POST")) + req->method = HTTP_REQUEST_POST; + else + req->method = HTTP_REQUEST_GET; + } + /* below is key the path and tmp2 the spec */ + key = (char *) strtok_r(tmp2, " ", &saveptr2); + + /* save version */ + tmp2 = saveptr2; + if (tmp2 != NULL) { + /*req->spec = (char *) malloc(strlen(tmp2) + 1); + strcpy(req->spec, tmp2); + */ + if (!strcmp(tmp2, "HTTP/1.0")) + req-> version = HTTP_1_0; + else + req-> version = HTTP_1_1; + } + /* + * parse and save path+query parse: + * /path/of/target?key1=value1&key2=value2... + */ + + if (key != NULL) { + tmp2 = key; + key = (char *) strtok_r(tmp2, "?", &saveptr2); + tmp2 = saveptr2; + + /* save path */ + /*req->path = (char *) malloc(strlen(key) + 1);*/ + strncpy(req->path, key, REQUEST_MAX_PATH_SIZE); + + /* parse options */ + for (;;) { + key = (char *) strtok_r(tmp2, "&", &saveptr2); + tmp2 = saveptr2; + + if (key == NULL) + break; + + opt_key = (char *) strtok_r(key, "=", &saveptr3); + opt_value = saveptr3; + + if (opt_value == NULL) + opt_value = ""; + + /* create option pair */ + if (opt_key != NULL) { + tmppair = (hpair_t *) malloc(sizeof(hpair_t)); + + if (req->query == NULL) { + req->query = qpair = tmppair; + } else { + qpair->next = tmppair; + qpair = tmppair; + } + + /* fill hpairnode_t struct */ + qpair->next = NULL; + qpair->key = (char *) malloc(strlen(opt_key) + 1); + qpair->value = (char *) malloc(strlen(opt_value) + 1); + + strcpy(qpair->key, opt_key); + strcpy(qpair->value, opt_value); + + } + } + } + } else { + + /* parse "key: value" */ + /* tmp2 = result; + key = (char *) strtok_r(tmp2, ": ", &saveptr2); + value = saveptr2;*/ + + /* create pair */ +/* tmppair = (hpair_t *) malloc(sizeof(hpair_t));*/ + tmppair = hpairnode_parse(result, ":", NULL); + + if (req->header == NULL) { + req->header = hpair = tmppair; + } else { + hpair->next = tmppair; + hpair = tmppair; + } + + /* fill pairnode_t struct */ + /* + hpair->next = NULL; + hpair->key = (char *) malloc(strlen(key) + 1); + hpair->value = (char *) malloc(strlen(value) + 1); + + strcpy(hpair->key, key); + strcpy(hpair->value, value); + */ + } + } + + /* Check Content-type */ + tmp = hpairnode_get(req->header, HEADER_CONTENT_TYPE); + if (tmp != NULL) + req->content_type = content_type_new(tmp); + + return req; +} + + +void +hrequest_free(hrequest_t * req) +{ + if (req == NULL) + return; + + + hpairnode_free_deep(req->header); + hpairnode_free_deep(req->query); + + if (req->in) + http_input_stream_free(req->in); + + if (req->content_type) + content_type_free(req->content_type); + + if (req->attachments) + attachments_free(req->attachments); + + free(req); +} + + +hrequest_t * +hrequest_new_from_socket(hsocket_t sock) +{ + int i=0, status; + hrequest_t *req; + char buffer[MAX_HEADER_SIZE+1]; + attachments_t *mimeMessage; + + /* Read header */ + while (i<MAX_HEADER_SIZE) + { + status = hsocket_read(sock, &(buffer[i]), 1, 1); + if (status == -1) + { + log_error1("Socket read error"); + return NULL; + } + + buffer[i+1] = '\0'; /* for strmp */ + + if (i > 3) + { + if (!strcmp(&(buffer[i-1]), "\n\n") || + !strcmp(&(buffer[i-2]), "\n\r\n")) + break; + } + i++; + } + + /* Create response */ + req = _hrequest_parse_header(buffer); + if (req == NULL) + { + log_error1("Header parse error"); + return NULL; + } + + /* Create input stream */ + req->in = http_input_stream_new(sock, req->header); + + /* Check for MIME message */ + if ((req->content_type && + !strcmp(req->content_type->type, "multipart/related"))) + { + status = mime_get_attachments(req->content_type, req->in, &mimeMessage); + if (status != H_OK) + { + /* TODO (#1#): Handle error */ + hrequest_free(req); + return NULL; + } + else + { + req->attachments = mimeMessage; + req->in = http_input_stream_new_from_file(mimeMessage->root_part->filename); + } + } + + + return req; +} + + + + diff --git a/nanohttp/nanohttp-request.h b/nanohttp/nanohttp-request.h new file mode 100755 index 0000000..c445596 --- /dev/null +++ b/nanohttp/nanohttp-request.h @@ -0,0 +1,57 @@ +/****************************************************************** + * $Id: nanohttp-request.h,v 1.1 2004/10/15 13:30:42 snowdrop Exp $ + * + * CSOAP Project: A http client/server library in C + * Copyright (C) 2003-2004 Ferhat Ayaz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Email: ferhatayaz@yahoo.com + ******************************************************************/ +#ifndef NANO_HTTP_REQUEST_H +#define NANO_HTTP_REQUEST_H + +#include <nanohttp/nanohttp-stream.h> +#include <nanohttp/nanohttp-mime.h> + +/* + request object + */ +typedef struct hrequest +{ + hreq_method_t method; + http_version_t version; + char path[REQUEST_MAX_PATH_SIZE]; + + hpair_t *query; + hpair_t *header; + + http_input_stream_t *in; + content_type_t *content_type; + attachments_t *attachments; + char root_part_id[150]; +}hrequest_t; + +hrequest_t *hrequest_new_from_socket(hsocket_t sock); +void hrequest_free(hrequest_t *req); + +#endif + + + + + + diff --git a/nanohttp/nanohttp-response.c b/nanohttp/nanohttp-response.c new file mode 100755 index 0000000..8f55856 --- /dev/null +++ b/nanohttp/nanohttp-response.c @@ -0,0 +1,213 @@ +/****************************************************************** +* $Id: nanohttp-response.c,v 1.1 2004/10/15 13:30:42 snowdrop Exp $ +* +* CSOAP Project: A http client/server library in C +* Copyright (C) 2003-2004 Ferhat Ayaz +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* Email: ferhatayaz@yahoo.com +******************************************************************/ + +#include <nanohttp/nanohttp-common.h> +#include <nanohttp/nanohttp-response.h> + + +#ifdef MEM_DEBUG +#include <utils/alloc.h> +#endif + +static +hresponse_t* +hresponse_new() +{ + hresponse_t *res; + + /* create response object */ + res = (hresponse_t *) malloc(sizeof(hresponse_t)); + res->version = HTTP_1_1; + res->errcode = -1; + res->desc[0] = '\0'; + res->header = NULL; + res->in = NULL; + res->content_type = NULL; + res->attachments = NULL; + return res; +} + +static +hresponse_t* +_hresponse_parse_header(const char *buffer) +{ + hresponse_t *res; + char *s1, *s2, *str; + + /* create response object */ + res = hresponse_new(); + + /* *** parse spec *** */ + /* [HTTP/1.1 | 1.2] [CODE] [DESC] */ + + /* stage 1: HTTP spec */ + str = (char *) strtok_r((char *) buffer, " ", &s2); + s1 = s2; + if (str == NULL) { + log_error1("Parse error reading HTTP spec"); + return NULL; + } + + if (!strcmp(str, "HTTP/1.0")) + res->version = HTTP_1_0; + else + res->version = HTTP_1_1; + + /* stage 2: http code */ + str = (char *) strtok_r(s1, " ", &s2); + s1 = s2; + if (str == NULL) { + log_error1("Parse error reading HTTP code"); + return NULL; + } + res->errcode = atoi(str); + + /* stage 3: description text */ + str = (char *) strtok_r(s1, "\r\n", &s2); + s1 = s2; + if (str == NULL) { + log_error1("Parse error reading HTTP description"); + return NULL; + } +/* res->desc = (char *) malloc(strlen(str) + 1);*/ + strncpy(res->desc, str, RESPONSE_MAX_DESC_SIZE); + res->desc[strlen(str)] = '\0'; + + /* *** parse header *** */ + /* [key]: [value] */ + for (;;) { + str = strtok_r(s1, "\n", &s2); + s1 = s2; + + /* check if header ends without body */ + if (str == NULL) { + return res; + } + /* check also for end of header */ + if (!strcmp(str, "\r")) { + break; + } + str[strlen(str) - 1] = '\0'; + res->header = hpairnode_parse(str, ":", res->header); + } + + /* Check Content-type */ + str = hpairnode_get(res->header, HEADER_CONTENT_TYPE); + if (str != NULL) + res->content_type = content_type_new(str); + + /* return response object */ + return res; +} + +hresponse_t * +hresponse_new_from_socket(hsocket_t sock) +{ + int i=0, status; + hresponse_t *res; + attachments_t *mimeMessage; + char buffer[MAX_HEADER_SIZE+1]; + +read_header: /* for errorcode: 100 (continue) */ + /* Read header */ + while (i<MAX_HEADER_SIZE) + { + status = hsocket_read(sock, &(buffer[i]), 1, 1); + if (status == -1) + { + log_error1("Socket read error"); + return NULL; + } + + buffer[i+1] = '\0'; /* for strmp */ + + if (i > 3) + { + if (!strcmp(&(buffer[i-1]), "\n\n") || + !strcmp(&(buffer[i-2]), "\n\r\n")) + break; + } + i++; + } + + /* Create response */ + res = _hresponse_parse_header(buffer); + if (res == NULL) + { + log_error1("Header parse error"); + return NULL; + } + + /* Chec for Errorcode: 100 (continue) */ + if (res->errcode == 100) { + hresponse_free(res); + i=0; + goto read_header; + } + + /* Create input stream */ + res->in = http_input_stream_new(sock, res->header); + + + /* Check for MIME message */ + if ((res->content_type && + !strcmp(res->content_type->type, "multipart/related"))) + { + status = mime_get_attachments(res->content_type, res->in, &mimeMessage); + if (status != H_OK) + { + /* TODO (#1#): Handle error */ + hresponse_free(res); + return NULL; + } + else + { + res->attachments = mimeMessage; + res->in = http_input_stream_new_from_file(mimeMessage->root_part->filename); + } + } + + return res; +} + + + +void +hresponse_free(hresponse_t * res) +{ + if (res == NULL) + return; + + if (res->header) + hpairnode_free_deep(res->header); + + if (res->in) + http_input_stream_free(res->in); + + if (res->content_type) + content_type_free(res->content_type); + + free(res); +} + diff --git a/nanohttp/nanohttp-response.h b/nanohttp/nanohttp-response.h new file mode 100755 index 0000000..7607eb7 --- /dev/null +++ b/nanohttp/nanohttp-response.h @@ -0,0 +1,57 @@ +/****************************************************************** + * $Id: nanohttp-response.h,v 1.1 2004/10/15 13:30:42 snowdrop Exp $ + * + * CSOAP Project: A http client/server library in C + * Copyright (C) 2003-2004 Ferhat Ayaz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Email: ferhatayaz@yahoo.com + ******************************************************************/ +#ifndef NANO_HTTP_RESPONSE_H +#define NANO_HTTP_RESPONSE_H + +#include <nanohttp/nanohttp-stream.h> +#include <nanohttp/nanohttp-common.h> +#include <nanohttp/nanohttp-mime.h> + +/* response object */ +typedef struct hresponse +{ + http_version_t version; + int errcode; + char desc[RESPONSE_MAX_DESC_SIZE]; + + hpair_t *header; + + http_input_stream_t *in; + content_type_t *content_type; + attachments_t *attachments; + char root_part_id[150]; +}hresponse_t; + +hresponse_t *hresponse_new_from_socket(hsocket_t sock); +void hresponse_free(hresponse_t *res); + + + +#endif + + + + + + diff --git a/nanohttp/todo.txt b/nanohttp/todo.txt new file mode 100755 index 0000000..3cf9981 --- /dev/null +++ b/nanohttp/todo.txt @@ -0,0 +1,19 @@ +
+------------------------------------------------------------
+ERROR HANDLING !!!!
+------------------------------------------------------------
+
+Merge
+
+
+mime_message_t *
+mime_message_parse(http_input_stream_t *in,
+ const char* root_id, const char* boundary,
+ const char* dest_dir);
+
+and try to optimize mime API
+
+------------------------------------------------------------
+check socket API again
+
+
|