/****************************************************************** * $Id: nanohttp-common.c,v 1.27 2006/02/18 20:14:36 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 ******************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_STDIO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STDARG_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_CTYPE_H #include #endif #ifdef HAVE_PTHREAD_H #include #endif #ifdef MEM_DEBUG #include #endif #include #define MAX_OPTION_SIZE 50 #define MAX_OPTION_VALUE_SIZE 150 static char _hoption_table[MAX_OPTION_SIZE][MAX_OPTION_VALUE_SIZE]; static int strcmpigcase(const char *s1, const char *s2) { int l1, l2, i; if (s1 == NULL && s2 == NULL) return 1; if (s1 == NULL || s2 == NULL) return 0; l1 = strlen(s1); l2 = strlen(s2); if (l1 != l2) return 0; for (i = 0; i < l1; i++) if (toupper(s1[i]) != toupper(s2[i])) return 0; return 1; } /* option stuff */ void hoption_set(int opt, const char *value) { if (opt >= MAX_OPTION_SIZE) { log_warn3("Option to high (%d >= %d)", opt, MAX_OPTION_SIZE); return; } strncpy(_hoption_table[opt], value, MAX_OPTION_VALUE_SIZE); return; } char * hoption_get(int opt) { if (opt >= MAX_OPTION_SIZE) { log_warn3("Option to high (%d >= %d)", opt, MAX_OPTION_SIZE); return ""; } return _hoption_table[opt]; } void hoption_init_args(int argc, char *argv[]) { int i; hoption_set(HOPTION_TMP_DIR, "."); /* default value */ hoption_set(HOPTION_SSL_CERT, ""); hoption_set(HOPTION_SSL_PASS, ""); hoption_set(HOPTION_SSL_CA, ""); /* initialize from arguments */ for (i = 0; i < argc; i++) { if (!strcmp(argv[i], NHTTP_ARG_TMPDIR) && i < argc - 1) { hoption_set(HOPTION_TMP_DIR, argv[i + 1]); } else if (!strcmp(argv[i], NHTTP_ARG_LOGFILE) && i < argc - 1) { log_set_file(argv[i + 1]); } else if (!strcmp(argv[i], NHTTP_ARG_CERT) && i < argc - 1) { #ifndef HAVE_SSL fprintf(stderr, "WARNING: csoap compiled without '--with-ssl' flag. Parameter '%s' is disabled", NHTTP_ARG_CERT); #else hoption_set(HOPTION_SSL_CERT, argv[i + 1]); #endif } else if (!strcmp(argv[i], NHTTP_ARG_CERTPASS) && i < argc - 1) { #ifndef HAVE_SSL fprintf(stderr, "WARNING: csoap compiled without '--with-ssl' flag. Parameter '%s' is disabled", NHTTP_ARG_CERTPASS); #else hoption_set(HOPTION_SSL_PASS, argv[i + 1]); #endif } else if (!strcmp(argv[i], NHTTP_ARG_CA) && i < argc - 1) { #ifndef HAVE_SSL fprintf(stderr, "WARNING: csoap compiled without '--with-ssl' flag. Parameter '%s' is disabled", NHTTP_ARG_CA); #else hoption_set(HOPTION_SSL_CA, argv[i + 1]); #endif } else if (!strcmp(argv[i], NHTTP_ARG_HTTPS)) { #ifndef HAVE_SSL fprintf(stderr, "WARNING: csoap compiled without '--with-ssl' flag. Parameter '%s' is disabled", NHTTP_ARG_HTTPS); #else /* TODO (#1#) handle ssl arguments */ /*SSLCertLess = 1;*/ #endif } } } #ifdef WIN32 #ifndef __MINGW32__ /* not thread safe!*/ char * VisualC_funcname(const char *file, int line) { static char buffer[256]; int i = strlen(file) - 1; while (i > 0 && file[i] != '\\') i--; sprintf(buffer, "%s:%d", (file[i] != '\\') ? file : (file + i + 1), line); return buffer; } #endif #endif typedef struct _herror_impl_t { int errcode; char message[250]; char func[100]; } herror_impl_t; herror_t herror_new(const char *func, int errcode, const char *format, ...) { va_list ap; herror_impl_t *impl = (herror_impl_t *) malloc(sizeof(herror_impl_t)); impl->errcode = errcode; strcpy(impl->func, func); va_start(ap, format); vsprintf(impl->message, format, ap); va_end(ap); return (herror_t) impl; } int herror_code(herror_t err) { herror_impl_t *impl = (herror_impl_t *) err; if (!err) return H_OK; return impl->errcode; } char * herror_func(herror_t err) { herror_impl_t *impl = (herror_impl_t *) err; if (!err) return ""; return impl->func; } char * herror_message(herror_t err) { herror_impl_t *impl = (herror_impl_t *) err; if (!err) return ""; return impl->message; } void herror_release(herror_t err) { herror_impl_t *impl = (herror_impl_t *) err; if (!err) return; free(impl); } static log_level_t loglevel = HLOG_DEBUG; static char logfile[75] = { '\0' }; static int log_background = 0; log_level_t log_set_level(log_level_t level) { log_level_t old = loglevel; loglevel = level; return old; } log_level_t log_get_level() { return loglevel; } void log_set_file(const char *filename) { if (filename) strncpy(logfile, filename, 75); else logfile[0] = '\0'; } void log_set_background(int state) { log_background = state; } char * log_get_file() { if (logfile[0] == '\0') return NULL; return logfile; } static void log_write(log_level_t level, const char *prefix, const char *func, const char *format, va_list ap) { char buffer[1054]; char buffer2[1054]; FILE *f; if (level < loglevel) return; if (!log_background || log_get_file()) { #ifdef WIN32 sprintf(buffer, "*%s*: [%s] %s\n", prefix, func, format); #else sprintf(buffer, "*%s*:(%ld) [%s] %s\n", prefix, pthread_self(), func, format); #endif vsprintf(buffer2, buffer, ap); if (!log_background) { printf(buffer2); fflush(stdout); } if (log_get_file()) { f = fopen(log_get_file(), "a"); if (!f) f = fopen(log_get_file(), "w"); if (f) { fprintf(f, buffer2); fflush(f); fclose(f); } } } } void log_verbose(const char *FUNC, const char *format, ...) { va_list ap; va_start(ap, format); log_write(HLOG_VERBOSE, "VERBOSE", FUNC, format, ap); va_end(ap); } void log_debug(const char *FUNC, const char *format, ...) { va_list ap; va_start(ap, format); log_write(HLOG_DEBUG, "DEBUG", FUNC, format, ap); va_end(ap); } void log_info(const char *FUNC, const char *format, ...) { va_list ap; va_start(ap, format); log_write(HLOG_INFO, "INFO", FUNC, format, ap); va_end(ap); } void log_warn(const char *FUNC, const char *format, ...) { va_list ap; va_start(ap, format); log_write(HLOG_WARN, "WARN", FUNC, format, ap); va_end(ap); } void log_error(const char *FUNC, const char *format, ...) { va_list ap; va_start(ap, format); log_write(HLOG_ERROR, "ERROR", FUNC, format, ap); va_end(ap); } hpair_t * hpairnode_new(const char *key, const char *value, hpair_t * next) { hpair_t *pair; log_verbose3("new pair ('%s','%s')", SAVE_STR(key), SAVE_STR(value)); pair = (hpair_t *) malloc(sizeof(hpair_t)); if (key != NULL) { pair->key = (char *) malloc(strlen(key) + 1); strcpy(pair->key, key); } else { pair->key = NULL; } if (value != NULL) { pair->value = (char *) malloc(strlen(value) + 1); strcpy(pair->value, value); } else { pair->value = NULL; } pair->next = next; return pair; } hpair_t * hpairnode_parse(const char *str, const char *delim, hpair_t * next) { hpair_t *pair; char *key, *value; int c; pair = (hpair_t *) malloc(sizeof(hpair_t)); pair->key = ""; pair->value = ""; pair->next = next; key = strtok_r((char *) str, delim, &value); if (key != NULL) { pair->key = (char *) malloc(strlen(key) + 1); strcpy(pair->key, key); } if (value != NULL) { for (c = 0; value[c] == ' '; c++); /* skip white space */ pair->value = (char *) malloc(strlen(&value[c]) + 1); strcpy(pair->value, &value[c]); } return pair; } hpair_t * hpairnode_copy(const hpair_t * src) { hpair_t *pair; if (src == NULL) return NULL; pair = hpairnode_new(src->key, src->value, NULL); return pair; } hpair_t * hpairnode_copy_deep(const hpair_t * src) { hpair_t *pair, *result, *next; if (src == NULL) return NULL; result = hpairnode_copy(src); next = src->next; pair = result; while (next != NULL) { pair->next = hpairnode_copy(next); pair = pair->next; next = next->next; } return result; } void hpairnode_dump(hpair_t * pair) { if (pair == NULL) { log_verbose1("(NULL)[]"); return; } log_verbose5("(%p)['%s','%s','%p']", pair, SAVE_STR(pair->key), SAVE_STR(pair->value), pair->next); } void hpairnode_dump_deep(hpair_t * pair) { hpair_t *p; p = pair; log_verbose1("-- BEGIN dump hpairnode_t --"); while (p != NULL) { hpairnode_dump(p); p = p->next; } log_verbose1("-- END dump hpairnode_t --\n"); } void hpairnode_free(hpair_t * pair) { if (pair == NULL) return; free(pair->key); free(pair->value); free(pair); } void hpairnode_free_deep(hpair_t * pair) { hpair_t *tmp; while (pair != NULL) { tmp = pair->next; hpairnode_free(pair); pair = tmp; } } char * hpairnode_get_ignore_case(hpair_t * pair, const char *key) { if (key == NULL) { log_error1("key is NULL"); return NULL; } while (pair != NULL) { if (pair->key != NULL) { if (strcmpigcase(pair->key, key)) { return pair->value; } } pair = pair->next; } return NULL; } char * hpairnode_get(hpair_t * pair, const char *key) { if (key == NULL) { log_error1("key is NULL"); return NULL; } while (pair != NULL) { if (pair->key != NULL) { if (!strcmp(pair->key, key)) { return pair->value; } } pair = pair->next; } return NULL; } static void hurl_dump(const hurl_t * url) { if (url == NULL) { log_error1("url is NULL!"); return; } log_verbose2("PROTOCOL : %d", url->protocol); log_verbose2(" HOST : %s", url->host); log_verbose2(" PORT : %d", url->port); log_verbose2(" CONTEXT : %s", url->context); } herror_t hurl_parse(hurl_t * url, const char *urlstr) { int iprotocol; int ihost; int iport; int len; int size; char tmp[8]; char protocol[1024]; iprotocol = 0; len = strlen(urlstr); /* find protocol */ while (urlstr[iprotocol] != ':' && urlstr[iprotocol] != '\0') { iprotocol++; } if (iprotocol == 0) { log_error1("no protocol"); return herror_new("hurl_parse", URL_ERROR_NO_PROTOCOL, "No protocol"); } if (iprotocol + 3 >= len) { log_error1("no host"); return herror_new("hurl_parse", URL_ERROR_NO_HOST, "No host"); } if (urlstr[iprotocol] != ':' && urlstr[iprotocol + 1] != '/' && urlstr[iprotocol + 2] != '/') { log_error1("no protocol"); return herror_new("hurl_parse", URL_ERROR_NO_PROTOCOL, "No protocol"); } /* find host */ ihost = iprotocol + 3; while (urlstr[ihost] != ':' && urlstr[ihost] != '/' && urlstr[ihost] != '\0') { ihost++; } if (ihost == iprotocol + 1) { log_error1("no host"); return herror_new("hurl_parse", URL_ERROR_NO_HOST, "No host"); } /* find port */ iport = ihost; if (ihost + 1 < len) { if (urlstr[ihost] == ':') { while (urlstr[iport] != '/' && urlstr[iport] != '\0') { iport++; } } } /* find protocol */ strncpy(protocol, urlstr, iprotocol); protocol[iprotocol] = '\0'; if (strcmpigcase(protocol, "http")) url->protocol = PROTOCOL_HTTP; else if (strcmpigcase(protocol, "https")) url->protocol = PROTOCOL_HTTPS; else if (strcmpigcase(protocol, "ftp")) url->protocol = PROTOCOL_FTP; else return herror_new("hurl_parse", URL_ERROR_UNKNOWN_PROTOCOL, "Unknown protocol '%s'", protocol); /* TODO (#1#): add max of size and URL_MAX_HOST_SIZE */ size = ihost - iprotocol - 3; strncpy(url->host, &urlstr[iprotocol + 3], size); url->host[size] = '\0'; if (iport > ihost) { size = iport - ihost; strncpy(tmp, &urlstr[ihost + 1], size); url->port = atoi(tmp); } else { switch (url->protocol) { case PROTOCOL_HTTP: url->port = URL_DEFAULT_PORT_HTTP; break; case PROTOCOL_HTTPS: url->port = URL_DEFAULT_PORT_HTTPS; break; case PROTOCOL_FTP: url->port = URL_DEFAULT_PORT_FTP; break; } } len = strlen(urlstr); if (len > iport) { /* TODO (#1#): find max of size and URL_MAX_CONTEXT_SIZE */ size = len - iport; strncpy(url->context, &urlstr[iport], size); url->context[size] = '\0'; } else { url->context[0] = '\0'; } hurl_dump(url); return H_OK; } /* Content-type stuff */ content_type_t * content_type_new(const char *content_type_str) { hpair_t *pair = NULL, *last = NULL; content_type_t *ct; char ch, key[256], value[256]; int inQuote = 0, i = 0, c = 0, begin = 0, len; int mode = 0; /* 0: searching ';' 1: process key 2: process value */ /* Create object */ ct = (content_type_t *) malloc(sizeof(content_type_t)); ct->params = NULL; len = strlen(content_type_str); while (i <= len) { if (i != len) ch = content_type_str[i++]; else { ch = ' '; i++; } switch (mode) { case 0: if (ch == ';') { ct->type[c] = '\0'; c = 0; mode = 1; } else if (ch != ' ' && ch != '\t' && ch != '\r') ct->type[c++] = ch; break; case 1: if (ch == '=') { key[c] = '\0'; c = 0; mode = 2; } else if (ch != ' ' && ch != '\t' && ch != '\r') key[c++] = ch; break; case 2: if (ch != ' ') begin = 1; if ((ch == ' ' || ch == ';') && !inQuote && begin) { value[c] = '\0'; pair = hpairnode_new(key, value, NULL); if (ct->params == NULL) ct->params = pair; else last->next = pair; last = pair; c = 0; begin = 0; mode = 1; } else if (ch == '"') inQuote = !inQuote; else if (begin && ch != '\r') value[c++] = ch; break; } } return ct; } void content_type_free(content_type_t * ct) { if (!ct) return; hpairnode_free_deep(ct->params); free(ct); } part_t * part_new(const char *id, const char *filename, const char *content_type, const char *transfer_encoding, part_t * next) { part_t *part = (part_t *) malloc(sizeof(part_t)); part->header = NULL; part->next = next; part->deleteOnExit = 0; strcpy(part->id, id); strcpy(part->filename, filename); if (content_type) strcpy(part->content_type, content_type); else part->content_type[0] = '\0'; part->header = hpairnode_new(HEADER_CONTENT_ID, id, part->header); /* TODO (#1#): encoding is always binary. implement also others! */ /* part->header = hpairnode_new(HEADER_CONTENT_TRANSFER_ENCODING, "binary", part->header);*/ strcpy(part->transfer_encoding, transfer_encoding ? transfer_encoding : "binary"); if (content_type) { part->header = hpairnode_new(HEADER_CONTENT_TYPE, content_type, part->header); } else { /* TODO (#1#): get content-type from mime type list */ } return part; } void part_free(part_t * part) { if (part == NULL) return; if (part->deleteOnExit) { remove(part->filename); } hpairnode_free_deep(part->header); free(part); } attachments_t * attachments_new() /* should be used internally */ { attachments_t *attachments = (attachments_t *) malloc(sizeof(attachments_t)); attachments->parts = NULL; attachments->last = NULL; attachments->root_part = NULL; return attachments; } void attachments_add_part(attachments_t * attachments, part_t * part) { /* paranoya check */ if (!attachments) return; if (attachments->last) attachments->last->next = part; else attachments->parts = part; attachments->last = part; } /* Free a mime message */ void attachments_free(attachments_t * message) { part_t *tmp, *part; if (message == NULL) return; part = message->parts; while (part) { tmp = part->next; part_free(part); part = tmp; } if (message->root_part) part_free(message->root_part); /* TODO (#1#): HERE IS A BUG!!!! */ free(message); } #ifdef WIN32 /* strtok_r() */ char * strtok_r(char *s, const char *delim, char **save_ptr) { char *token; if (s == NULL) s = *save_ptr; /* Scan leading delimiters. */ s += strspn(s, delim); if (*s == '\0') return NULL; /* Find the end of the token. */ token = s; s = strpbrk(token, delim); if (s == NULL) /* This token finishes the string. */ *save_ptr = strchr(token, '\0'); else { /* Terminate the token and make *SAVE_PTR point past it. */ *s = '\0'; *save_ptr = s + 1; } return token; } /* localtime_r() */ struct tm * localtime_r(const time_t * const timep, struct tm *p_tm) { static struct tm *tmp; tmp = localtime(timep); if (tmp) { memcpy(p_tm, tmp, sizeof(struct tm)); tmp = p_tm; } return tmp; } #endif