diff --git a/src/mod/applications/mod_http/Makefile b/src/mod/applications/mod_http/Makefile new file mode 100644 index 0000000000..97df6e3004 --- /dev/null +++ b/src/mod/applications/mod_http/Makefile @@ -0,0 +1,5 @@ +BASE=../../../.. +LOCAL_SOURCES=arraylist.c debug.c json_object.c json_tokener.c json_util.c linkhash.c printbuf.c url_encoding.c http_req.c +LOCAL_OBJS=arraylist.o debug.o json_object.o json_tokener.o json_util.o linkhash.o printbuf.o url_encoding.o http_req.o +include $(BASE)/build/modmake.rules + diff --git a/src/mod/applications/mod_http/arraylist.c b/src/mod/applications/mod_http/arraylist.c new file mode 100644 index 0000000000..dbd075d80d --- /dev/null +++ b/src/mod/applications/mod_http/arraylist.c @@ -0,0 +1,93 @@ +/* + * $Id: arraylist.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#if STDC_HEADERS +# include +# include +#endif /* STDC_HEADERS */ + +#if HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ + +#include "bits.h" +#include "arraylist.h" + +struct array_list* +array_list_new(array_list_free_fn *free_fn) +{ + struct array_list *this; + + if(!(this = calloc(1, sizeof(struct array_list)))) return NULL; + this->size = ARRAY_LIST_DEFAULT_SIZE; + this->length = 0; + this->free_fn = free_fn; + if(!(this->array = calloc(sizeof(void*), this->size))) { + free(this); + return NULL; + } + return this; +} + +extern void +array_list_free(struct array_list *this) +{ + int i; + for(i = 0; i < this->length; i++) + if(this->array[i]) this->free_fn(this->array[i]); + free(this->array); + free(this); +} + +void* +array_list_get_idx(struct array_list *this, int i) +{ + if(i >= this->length) return NULL; + return this->array[i]; +} + +static int array_list_expand_internal(struct array_list *this, int max) +{ + void *t; + int new_size; + + if(max < this->size) return 0; + new_size = max(this->size << 1, max); + if(!(t = realloc(this->array, new_size*sizeof(void*)))) return -1; + this->array = t; + (void)memset(this->array + this->size, 0, (new_size-this->size)*sizeof(void*)); + this->size = new_size; + return 0; +} + +int +array_list_put_idx(struct array_list *this, int idx, void *data) +{ + if(array_list_expand_internal(this, idx)) return -1; + if(this->array[idx]) this->free_fn(this->array[idx]); + this->array[idx] = data; + if(this->length <= idx) this->length = idx + 1; + return 0; +} + +int +array_list_add(struct array_list *this, void *data) +{ + return array_list_put_idx(this, this->length, data); +} + +int +array_list_length(struct array_list *this) +{ + return this->length; +} diff --git a/src/mod/applications/mod_http/arraylist.h b/src/mod/applications/mod_http/arraylist.h new file mode 100644 index 0000000000..2948e042a9 --- /dev/null +++ b/src/mod/applications/mod_http/arraylist.h @@ -0,0 +1,45 @@ +/* + * $Id: arraylist.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _arraylist_h_ +#define _arraylist_h_ + +#define ARRAY_LIST_DEFAULT_SIZE 32 + +typedef void (array_list_free_fn) (void *data); + +struct array_list +{ + void **array; + int length; + int size; + array_list_free_fn *free_fn; +}; + +extern struct array_list* +array_list_new(array_list_free_fn *free_fn); + +extern void +array_list_free(struct array_list *al); + +extern void* +array_list_get_idx(struct array_list *al, int i); + +extern int +array_list_put_idx(struct array_list *al, int i, void *data); + +extern int +array_list_add(struct array_list *al, void *data); + +extern int +array_list_length(struct array_list *al); + +#endif diff --git a/src/mod/applications/mod_http/bits.h b/src/mod/applications/mod_http/bits.h new file mode 100644 index 0000000000..2c107cce11 --- /dev/null +++ b/src/mod/applications/mod_http/bits.h @@ -0,0 +1,27 @@ +/* + * $Id: bits.h,v 1.10 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _bits_h_ +#define _bits_h_ + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) +#define error_ptr(error) ((void*)error) +#define is_error(ptr) ((unsigned long)ptr > (unsigned long)-4000L) + +#endif diff --git a/src/mod/applications/mod_http/config.h b/src/mod/applications/mod_http/config.h new file mode 100644 index 0000000000..d96a74b648 --- /dev/null +++ b/src/mod/applications/mod_http/config.h @@ -0,0 +1,118 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `open' function. */ +#define HAVE_OPEN 1 + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#define HAVE_REALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strncasecmp' function. */ +#define HAVE_STRNCASECMP 1 + +/* Define to 1 if you have the `strndup' function. */ +#define HAVE_STRNDUP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vasprintf' function. */ +#define HAVE_VASPRINTF 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `vsyslog' function. */ +#define HAVE_VSYSLOG 1 + +/* Name of package */ +#define PACKAGE "" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "michael@metaparadigm.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "JSON C Library" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "JSON C Library 0.3" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "json-c" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.3" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.3" + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to rpl_realloc if the replacement function should be used. */ +/* #undef realloc */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/src/mod/applications/mod_http/debug.c b/src/mod/applications/mod_http/debug.c new file mode 100644 index 0000000000..eaa6fca33c --- /dev/null +++ b/src/mod/applications/mod_http/debug.c @@ -0,0 +1,94 @@ +/* + * $Id: debug.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#if HAVE_SYSLOG_H +# include +#endif /* HAVE_SYSLOG_H */ + +#if HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#if HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#include "debug.h" + +static int _syslog = 0; +static int _debug = 0; + +void mc_set_debug(int debug) { _debug = debug; } +int mc_get_debug() { return _debug; } + +extern void mc_set_syslog(int syslog) +{ + _syslog = syslog; +} + +void mc_abort(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_ERR, msg, ap); + } else +#endif + vprintf(msg, ap); + exit(1); +} + + +void mc_debug(const char *msg, ...) +{ + va_list ap; + if(_debug) { + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_DEBUG, msg, ap); + } else +#endif + vprintf(msg, ap); + } +} + +void mc_error(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_ERR, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); +} + +void mc_info(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_INFO, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); +} diff --git a/src/mod/applications/mod_http/debug.h b/src/mod/applications/mod_http/debug.h new file mode 100644 index 0000000000..1db69ef016 --- /dev/null +++ b/src/mod/applications/mod_http/debug.h @@ -0,0 +1,24 @@ +/* + * $Id: debug.h,v 1.5 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +extern void mc_set_debug(int debug); +extern int mc_get_debug(); + +extern void mc_set_syslog(int syslog); +extern void mc_abort(const char *msg, ...); +extern void mc_debug(const char *msg, ...); +extern void mc_error(const char *msg, ...); +extern void mc_info(const char *msg, ...); + +#endif diff --git a/src/mod/applications/mod_http/http_req.c b/src/mod/applications/mod_http/http_req.c new file mode 100644 index 0000000000..15deff7da4 --- /dev/null +++ b/src/mod/applications/mod_http/http_req.c @@ -0,0 +1,680 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2008, Eric des Courtis + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Eric des Courtis + * Copyright (C) Benbria. All Rights Reserved. + * + * Contributor(s): + * + * Eric des Courtis + * + * + * http_req.c -- HTTP client implementation + * + */ + +#include "http_req.h" + +/* +extern int dprintf(int, const char *, ...); +extern int isblank(int); +*/ + +/* NOTE: v = version, header = h, status = s, newline = n, phrase = p */ +static accept_state_t state_start(char c, state_machine_t *sm); +static accept_state_t state_shp (char c, state_machine_t *sm); +static accept_state_t state_n (char c, state_machine_t *sm); +static accept_state_t state_vhp_A(char c, state_machine_t *sm); +static accept_state_t state_vhp_B(char c, state_machine_t *sm); +static accept_state_t state_vhp_C(char c, state_machine_t *sm); +static accept_state_t state_vhp_D(char c, state_machine_t *sm); +static accept_state_t state_vhp_E(char c, state_machine_t *sm); +static accept_state_t state_vhp_F(char c, state_machine_t *sm); +static accept_state_t state_vhp_G(char c, state_machine_t *sm); +static accept_state_t state_vhp_H(char c, state_machine_t *sm); +static accept_state_t state_hp_A (char c, state_machine_t *sm); +static accept_state_t state_hp_B (char c, state_machine_t *sm); +static accept_state_t state_p (char c, state_machine_t *sm); +static accept_state_t state_error(char c, state_machine_t *sm); + +static int read_all(int s, char *buf, size_t len); + +#ifdef DEBUG +int main(int argc, char *argv[]) +{ + http_response_t response; + http_request_t request; + int ret; + int i; + + if(argc != 2) return EXIT_FAILURE; + + request.method = GET; + request.version = DEFAULT_HTTP_VERSION; + request.url = argv[1]; + request.header_len = 0; + request.body_len = 0; + + ret = http_req(&request, &response); + if(ret == ERROR) return EXIT_FAILURE; + + printf("Version : %s\n", response.version); + printf("Status Code : %d\n", response.status_code); + printf("Phrase : %s\n", response.phrase); + + for(i = 0; i < response.header_len; i++){ + printf( + "Header : key = [%s] value = [%s]\n", + response.headers[i].field_name, response.headers[i].value + ); + } + + fflush(stdout); + write(STDOUT_FILENO, response.body, response.body_len); + printf("\n"); + + free_http_response(&response); + + return EXIT_SUCCESS; +} +#endif + +int http_req(http_request_t *req, http_response_t *res) +{ + uint32_t addr; + int s; + int ret; + uint16_t port = 0; + size_t len; + struct sockaddr_in sck; + struct hostent *hst; + char *hostname; + char *method; + char *buf; + int buf_len; + ssize_t i; + int j; + int l; + int m; + int p; + int q = 0; + char f = HTTP_FALSE; + char port_s[MAX_PORT_LEN]; + sighandler_t sig; + struct timeval tv; + + tv.tv_sec = 1; + tv.tv_usec = 0; + + + (void)memset(&sck, 0, sizeof(sck)); + + sig = signal(SIGPIPE, SIG_IGN); + if(sig == SIG_ERR){ + fprintf(stderr, "Cannot ignore SIGPIPE signal\n"); + return ERROR; + } + + s = socket(PF_INET, SOCK_STREAM, 0); + if(s == ERROR){ + perror("Could not create socket"); + return ERROR; + } + + (void)setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + (void)setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + + len = strlen(req->url); + hostname = (char *)malloc(len * sizeof(char) + 1); + if(hostname == NULL){ + perror("Could not allocate memory for hostname"); + close(s); + return ERROR; + } + + EMPTY_STRING(hostname); + + l = strlen(req->url) + 1; + m = strlen("http://"); + for(p = 0, j = m; j < l; j++){ + if(req->url[j] == ':'){ + strncpy(hostname, req->url + m, j - m); + hostname[j - m] = '\0'; + f = HTTP_TRUE; + p = j; + continue; + } + + if((req->url[j] == '/' || req->url[j] == '\0') && f == HTTP_TRUE){ + if((j - p) < MAX_PORT_LEN){ + strncpy(port_s, req->url + p + 1, j - p); + port_s[j - p] = '\0'; + port = (uint16_t)atoi(port_s); + }else port = 0; + q = j; + break; + } + + if((req->url[j] == '/' || req->url[j] == '\0') && f == HTTP_FALSE){ + strncpy(hostname, req->url + m, j - m); + hostname[j - m] = '\0'; + port = DEFAULT_HTTP_PORT; + q = j; + break; + } + } + + if(port == 0 || hostname[0] == '\0'){ + fprintf(stderr, "Invalid URL\n"); + close(s); + free(hostname); + return ERROR; + } + + l = strlen(hostname); + for(j = 0; j < l; j++){ + if(hostname[j] == '/'){ + hostname[j] = '\0'; + break; + } + } + + hst = gethostbyname(hostname); + if(hst == NULL){ + herror("Could not find host"); + close(s); + free(hostname); + return ERROR; + } + + addr = *((uint32_t *)hst->h_addr_list[0]); + + sck.sin_family = AF_INET; + sck.sin_port = htons(port); + sck.sin_addr.s_addr = addr; + + ret = connect(s, (struct sockaddr *)&sck, sizeof(sck)); + if(ret == ERROR){ + perror("Could not connect to host"); + close(s); + free(hostname); + return ERROR; + } + + switch(req->method){ + case GET: + method = HTTP_GET_METHOD; + break; + case HEAD: + method = HTTP_HEAD_METHOD; + break; + case POST: + method = HTTP_POST_METHOD; + break; + case DELETE: + method = HTTP_DELETE_METHOD; + break; + case PUT: + method = HTTP_PUT_METHOD; + break; + default: + method = HTTP_GET_METHOD; + } + + if(req->url[q] == '/'){ + dprintf(s, "%s %s HTTP/%s\r\n", method, req->url + q, req->version); + }else{ + dprintf(s, "%s /%s HTTP/%s\r\n", method, req->url + q, req->version); + } + + if(port != DEFAULT_HTTP_PORT) + dprintf(s, "Host: %s:%d\r\n", hostname, port); + else dprintf(s, "Host: %s\r\n", hostname); + dprintf(s, "Connection: close\r\n"); + dprintf(s, "Content-Length: %d\r\n", req->body_len); + + for(i = 0; i < req->header_len; i++){ + dprintf( + s, + "%s: %s\r\n", + req->headers[i].field_name, + req->headers[i].value + ); + } + + dprintf(s, "\r\n"); + + + if(req->body != NULL && req->body_len != 0){ + ret = write(s, req->body, req->body_len); + if(ret == ERROR){ + perror("Could not write body to socket"); + free(hostname); + return ERROR; + } + } + + buf = (char *)malloc(RECV_BUFF_SIZE * sizeof(char)); + if(buf == NULL){ + perror("Could not allocate memory for buffer"); + close(s); + free(hostname); + return ERROR; + } + + buf_len = read_all(s, buf, RECV_BUFF_SIZE -1); + if(buf_len == ERROR){ + perror("Could not read into buffer"); + free(hostname); + return ERROR; + } + buf[buf_len] = '\0'; + + close(s); + + (void)signal(SIGPIPE, sig); + free(hostname); + return http_parse_response(buf, buf_len, res); +} + +int http_parse_response(char *buf, ssize_t buf_len, http_response_t *response) +{ + token_t token; + state_machine_t sm; + int pos; + ssize_t size; + char buff[STATUS_CODE_LEN]; + int old_pos; + int nt; + int i; + int j; + int f; + + INIT_STATE_MACHINE(&sm); + + sm.buf = buf; + sm.buf_len = buf_len; + + pos = sm.pos; + token = get_next_token(&sm); + if(token != VERSION){ + fprintf(stderr, "ERROR %d-%d\n", VERSION, token); + return ERROR; + } + + size = sm.pos - pos; + response->version = (char *)malloc((size + 1) * sizeof(char)); + if(response->version == NULL){ + perror("Cannot allocate memory for version number"); + return ERROR; + } + strncpy(response->version, sm.buf + pos, size); + response->version[size] = '\0'; + + pos = sm.pos; + token = get_next_token(&sm); + if(token != STATUS_CODE){ + fprintf(stderr, "ERROR %d-%d\n", STATUS_CODE, token); + return ERROR; + } + + size = sm.pos - pos; + + buff[STATUS_CODE_LEN - 1] = '\0'; + strncpy(buff, sm.buf + pos, STATUS_CODE_LEN - 1); + + response->status_code = atoi(buff); + + pos = sm.pos; + token = get_next_token(&sm); + if(token != PHRASE){ + fprintf(stderr, "ERROR %d-%d\n", PHRASE, token); + return ERROR; + } + + size = sm.pos - pos - 2; + response->phrase = (char *)malloc((size + 1) * sizeof(char)); + if(response->phrase == NULL){ + perror("Cannot allocate memory for phrase"); + return ERROR; + } + strncpy(response->phrase, sm.buf + pos, size); + response->phrase[size] = '\0'; + + old_pos = sm.pos; + nt = 0; + f = HTTP_FALSE; + do{ + token = get_next_token(&sm); + switch(token){ + case PHRASE: + case STATUS_CODE: + f = HTTP_FALSE; + case VERSION: + case NEWLINE: + break; + case HEADER: + if(f == HTTP_FALSE){ + nt++; + f = HTTP_TRUE; + } + else f = HTTP_FALSE; + break; + case SYNTAX_ERROR: + return ERROR; + } + + if(token != HEADER && token != PHRASE && token != STATUS_CODE) break; + }while(token != SYNTAX_ERROR); + + if(nt != 0){ + response->headers = (http_header_t *)malloc(sizeof(http_header_t)*nt); + if(response->headers == NULL){ + perror("Could not allocate memory for headers"); + return ERROR; + } + }else response->headers = NULL; + + response->header_len = nt; + + sm.pos = old_pos; + for(i = 0; i < nt; i++){ + pos = sm.pos; + sm.state = state_start; + sm.stop = HTTP_FALSE; + token = get_next_token(&sm); + size = sm.pos - pos; + size -= 2; + + response->headers[i].field_name = + (char *)malloc((size + 1) * sizeof(char)); + + if(response->headers[i].field_name == NULL){ + perror("Could not allocate memory for header"); + return ERROR; + } + + strncpy(response->headers[i].field_name, sm.buf + pos, size); + response->headers[i].field_name[size] = '\0'; + + pos = sm.pos; + token = get_next_token(&sm); + if(token == HEADER || token == STATUS_CODE){ + for(j = 0; + ((sm.buf + pos)[j] == '\r' + && (sm.buf + pos)[j + 1] == '\n') == 0; + j++ + ); + size = j; + sm.pos = j + 2; + }else size = sm.pos - pos - 2; + + response->headers[i].value = + (char *)malloc((size + 1) * sizeof(char)); + + if(response->headers[i].value == NULL){ + perror("Could not allocate memory for header"); + return ERROR; + } + strncpy(response->headers[i].value, sm.buf + pos, size); + response->headers[i].value[size] = '\0'; + + } + + pos = sm.pos; + token = get_next_token(&sm); + if(token != NEWLINE){ + fprintf(stderr, "ERROR %d-%d\n", NEWLINE, token); + return ERROR; + } + + + response->body = (char *)malloc((buf_len - sm.pos + 1) * sizeof(char)); + if(response->body == NULL){ + perror("Could not allocate memory for body"); + return ERROR; + } + + response->body_len = buf_len - sm.pos; + response->body[response->body_len] = '\0'; + + (void)memcpy(response->body, buf + sm.pos, response->body_len); + + return SUCCESS; +} + +void free_http_response(http_response_t *response) +{ + free(response->version); + free(response->phrase); + if(response->headers != NULL) free(response->headers); + if(response->body != NULL) free(response->body); +} + +token_t get_next_token(state_machine_t *sm) +{ + char c; + accept_state_t accept; + + while(sm->stop != HTTP_TRUE){ + c = sm->buf[sm->pos]; + accept = sm->state(c, sm); + switch(accept){ + case NOAS: + case ASNR: + sm->pos++; + case ASWR: + break; + } + + switch(accept){ + case ASNR: + case ASWR: + sm->state = state_start; + return sm->token; + case NOAS: + break; + } + + if(sm->pos >= sm->buf_len){ + sm->stop = HTTP_TRUE; + break; + } + } + + return SYNTAX_ERROR; +} + +static accept_state_t state_start(char c, state_machine_t *sm) +{ + if(toupper(c) == 'H') sm->state = state_vhp_A; + else if(isdigit(c)) sm->state = state_shp; + else if(c == '\r' || c == '\n') sm->state = state_n; + else if(isprint(c)) sm->state = state_hp_A; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_shp(char c, state_machine_t *sm) +{ + if(isdigit(c)) sm->state = state_shp; + else if(isblank(c)){ + sm->token = STATUS_CODE; + return ASNR; + }else if(c == ':') sm->state = state_hp_B; + else if(c == '\r' || c == '\n') sm->state = state_p; + else if(isprint(c)) sm->state = state_hp_A; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_n(char c, state_machine_t *sm) +{ + if(c == '\r' || c == '\n'){ + sm->token = NEWLINE; + return ASNR; + }else if(c == ':') sm->state = state_hp_B; + else if(isprint(c)) sm->state = state_hp_A; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_vhp_A(char c, state_machine_t *sm) +{ + if(toupper(c) == 'T') sm->state = state_vhp_B; + else if(isprint(c)) sm->state = state_hp_A; + else if(c == '\n' || c == '\r') sm->state = state_p; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_vhp_B(char c, state_machine_t *sm) +{ + if(toupper(c) == 'T') sm->state = state_vhp_C; + else if(isprint(c)) sm->state = state_hp_A; + else if(c == '\n' || c == '\r') sm->state = state_p; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_vhp_C(char c, state_machine_t *sm) +{ + if(toupper(c) == 'P') sm->state = state_vhp_D; + else if(isprint(c)) sm->state = state_hp_A; + else if(c == '\n' || c == '\r') sm->state = state_p; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_vhp_D(char c, state_machine_t *sm) +{ + if(toupper(c) == '/') sm->state = state_vhp_E; + else if(isprint(c)) sm->state = state_hp_A; + else if(c == '\n' || c == '\r') sm->state = state_p; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_vhp_E(char c, state_machine_t *sm) +{ + if(isdigit(c)) sm->state = state_vhp_F; + else if(isprint(c)) sm->state = state_hp_A; + else if(c == '\n' || c == '\r') sm->state = state_p; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_vhp_F(char c, state_machine_t *sm) +{ + if(isdigit(c)) sm->state = state_vhp_F; + else if(c == '.') sm->state = state_vhp_G; + else if(isprint(c)) sm->state = state_hp_A; + else if(c == '\n' || c == '\r') sm->state = state_p; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_vhp_G(char c, state_machine_t *sm) +{ + if(isdigit(c)) sm->state = state_vhp_H; + else if(isprint(c)) sm->state = state_hp_A; + else if(c == '\n' || c == '\r') sm->state = state_p; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_vhp_H(char c, state_machine_t *sm) +{ + if(isdigit(c)) sm->state = state_vhp_H; + else if(isblank(c)){ + sm->token = VERSION; + return ASNR; + + }else if(isprint(c)) sm->state = state_hp_A; + + return NOAS; +} + +static accept_state_t state_hp_A(char c, state_machine_t *sm) +{ + if(c == ':') sm->state = state_hp_B; + else if(c == '\r' || c == '\n') sm->state = state_p; + else if(isprint(c)) sm->state = state_hp_A; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_hp_B(char c, state_machine_t *sm) +{ + if(isblank(c)){ + sm->token = HEADER; + return ASNR; + } else if(c == '\r' || c == '\n') sm->state = state_p; + else if(c == ':') sm->state = state_hp_B; + else if(isprint(c)) sm->state = state_hp_A; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_p(char c, state_machine_t *sm) +{ + if(c == '\r' || c == '\n'){ + sm->token = PHRASE; + return ASNR; + }else if(c == ':') sm->state = state_hp_B; + else if(isprint(c)) sm->state = state_hp_A; + else sm->state = state_error; + + return NOAS; +} + +static accept_state_t state_error(char c, state_machine_t *sm) +{ + c = 0; + sm->token = SYNTAX_ERROR; + + return ASNR; +} + +static int read_all(int s, char *buf, size_t len) +{ + size_t t; + size_t r = ERROR; + + for(t = 0; t < len && r != 0;){ + r = read(s, buf + t, len - t); + if((int)r == ERROR){ + return ERROR; + }else{ + t += r; + } + } + + return (int)t; +} + diff --git a/src/mod/applications/mod_http/http_req.h b/src/mod/applications/mod_http/http_req.h new file mode 100644 index 0000000000..e91f591eac --- /dev/null +++ b/src/mod/applications/mod_http/http_req.h @@ -0,0 +1,138 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2008, Eric des Courtis + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Eric des Courtis + * Copyright (C) Benbria. All Rights Reserved. + * + * Contributor(s): + * + * Eric des Courtis + * + * + * http_req.h -- HTTP client implementation + * + */ + +#ifndef __HTTP_REQ_H__ +#define __HTTP_REQ_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ERROR -1 +#define SUCCESS 0 +#define EMPTY_STRING(s) s[0] = '\0' + +#define DEFAULT_HTTP_VERSION "1.1" +#define DEFAULT_HTTP_PORT 80 + +#define HTTP_GET_METHOD "GET" +#define HTTP_HEAD_METHOD "HEAD" +#define HTTP_POST_METHOD "POST" +#define HTTP_DELETE_METHOD "DELETE" +#define HTTP_PUT_METHOD "PUT" + +#define MAX_PORT_LEN 6 + +#define RECV_BUFF_SIZE 65536 + +#define HTTP_TRUE 1 +#define HTTP_FALSE 0 + +#define INIT_STATE_MACHINE(m) \ +do {\ + (m)->pos = 0;\ + (m)->buf = NULL;\ + (m)->token = SYNTAX_ERROR;\ + (m)->stop = HTTP_FALSE;\ + (m)->state = state_start;\ +} while(0) + +#define STATUS_CODE_LEN 4 + +typedef enum tokens { + VERSION, + STATUS_CODE, + PHRASE, + HEADER, + NEWLINE, + SYNTAX_ERROR +} token_t; + +typedef enum accept_states { + ASWR, + ASNR, + NOAS +} accept_state_t; + +typedef struct state_machines { + accept_state_t (*state)(char, struct state_machines *); + int pos; + char *buf; + ssize_t buf_len; + int stop; + token_t token; +} state_machine_t; + +typedef enum http_methods { + GET, + HEAD, + POST, + DELETE, + PUT +} http_method_t; + +typedef struct http_headers { + char *field_name; + char *value; +} http_header_t; + +typedef struct http_requests { + http_method_t method; + char *version; + char *url; + http_header_t *headers; + ssize_t header_len; + char *body; + ssize_t body_len; +} http_request_t; + +typedef struct http_responses { + char *version; + int status_code; + char *phrase; + http_header_t *headers; + ssize_t header_len; + char *body; + int body_len; +} http_response_t; + +token_t get_next_token(state_machine_t *sm); +int http_parse_response(char *buf, ssize_t buf_len, http_response_t *response); +int http_req(http_request_t *req, http_response_t *res); +void free_http_response(http_response_t *response); + +#endif diff --git a/src/mod/applications/mod_http/json.h b/src/mod/applications/mod_http/json.h new file mode 100644 index 0000000000..a5a3432b2c --- /dev/null +++ b/src/mod/applications/mod_http/json.h @@ -0,0 +1,31 @@ +/* + * $Id: json.h,v 1.6 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_h_ +#define _json_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bits.h" +#include "debug.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_util.h" +#include "json_object.h" +#include "json_tokener.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/mod/applications/mod_http/json_object.c b/src/mod/applications/mod_http/json_object.c new file mode 100644 index 0000000000..55782b9f5e --- /dev/null +++ b/src/mod/applications/mod_http/json_object.c @@ -0,0 +1,510 @@ +/* + * $Id: json_object.c,v 1.17 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include + +#include "debug.h" +#include "printbuf.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_object.h" +#include "json_object_private.h" +#include "json_tokener.h" + +#if !HAVE_STRNDUP + + char* strndup(const char* str, size_t n); + +#endif /* !HAVE_STRNDUP */ + +/* #define REFCOUNT_DEBUG 1 */ + +char *json_number_chars = "0123456789.+-e"; +char *json_hex_chars = "0123456789abcdef"; + +#ifdef REFCOUNT_DEBUG +static char* json_type_name[] = { + "null", + "boolean", + "double", + "int", + "object", + "array", + "string", +}; +#endif /* REFCOUNT_DEBUG */ + +static void json_object_generic_delete(struct json_object* this); +static struct json_object* json_object_new(enum json_type o_type); + + +/* ref count debugging */ + +#ifdef REFCOUNT_DEBUG + +static struct lh_table *json_object_table; + +static void json_object_init() __attribute__ ((constructor)); +static void json_object_init() { + mc_debug("json_object_init: creating object table\n"); + json_object_table = lh_kptr_table_new(128, "json_object_table", NULL); +} + +static void json_object_fini() __attribute__ ((destructor)); +static void json_object_fini() { + struct lh_entry *ent; + if(mc_get_debug() && json_object_table->count) { + mc_debug("json_object_fini: %d referenced objects at exit\n", + json_object_table->count); + lh_foreach(json_object_table, ent) { + struct json_object* obj = (struct json_object*)ent->v; + mc_debug("\t%s:%p\n", json_type_name[obj->o_type], obj); + } + } + mc_debug("json_object_fini: freeing object table\n"); + lh_table_free(json_object_table); +} +#endif /* REFCOUNT_DEBUG */ + + +/* string escaping */ + +static int json_escape_str(struct printbuf *pb, char *str) +{ + int pos = 0, start_offset = 0; + unsigned char c; + do { + c = str[pos]; + switch(c) { + case '\0': + break; + case '\b': + case '\n': + case '\r': + case '\t': + case '"': + case '\\': + case '/': + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + if(c == '\b') printbuf_memappend(pb, "\\b", 2); + else if(c == '\n') printbuf_memappend(pb, "\\n", 2); + else if(c == '\r') printbuf_memappend(pb, "\\r", 2); + else if(c == '\t') printbuf_memappend(pb, "\\t", 2); + else if(c == '"') printbuf_memappend(pb, "\\\"", 2); + else if(c == '\\') printbuf_memappend(pb, "\\\\", 2); + else if(c == '/') printbuf_memappend(pb, "\\/", 2); + start_offset = ++pos; + break; + default: + if(c < ' ') { + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + sprintbuf(pb, "\\u00%c%c", + json_hex_chars[c >> 4], + json_hex_chars[c & 0xf]); + start_offset = ++pos; + } else pos++; + } + } while(c); + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + return 0; +} + + +/* reference counting */ + +extern struct json_object* json_object_get(struct json_object *this) +{ + if(this) { + this->_ref_count++; + } + return this; +} + +extern void json_object_put(struct json_object *this) +{ + if(this) { + this->_ref_count--; + if(!this->_ref_count) this->_delete(this); + } +} + + +/* generic object construction and destruction parts */ + +static void json_object_generic_delete(struct json_object* this) +{ +#ifdef REFCOUNT_DEBUG + mc_debug("json_object_delete_%s: %p\n", + json_type_name[this->o_type], this); + lh_table_delete(json_object_table, this); +#endif /* REFCOUNT_DEBUG */ + printbuf_free(this->_pb); + free(this); +} + +static struct json_object* json_object_new(enum json_type o_type) +{ + struct json_object *this = calloc(sizeof(struct json_object), 1); + if(!this) return NULL; + this->o_type = o_type; + this->_ref_count = 1; + this->_delete = &json_object_generic_delete; +#ifdef REFCOUNT_DEBUG + lh_table_insert(json_object_table, this, this); + mc_debug("json_object_new_%s: %p\n", json_type_name[this->o_type], this); +#endif /* REFCOUNT_DEBUG */ + return this; +} + + +/* type checking functions */ + +int json_object_is_type(struct json_object *this, enum json_type type) +{ + return (this->o_type == type); +} + +enum json_type json_object_get_type(struct json_object *this) +{ + return this->o_type; +} + + +/* json_object_to_json_string */ + +char* json_object_to_json_string(struct json_object *this) +{ + if(!this) return "null"; + if(!this->_pb) { + if(!(this->_pb = printbuf_new())) return NULL; + } else { + printbuf_reset(this->_pb); + } + if(this->_to_json_string(this, this->_pb) < 0) return NULL; + return this->_pb->buf; +} + + +/* json_object_object */ + +static int json_object_object_to_json_string(struct json_object* this, + struct printbuf *pb) +{ + int i=0; + struct json_object_iter iter; + sprintbuf(pb, "{"); + + /* CAW: scope operator to make ANSI correctness */ + /* CAW: switched to json_object_object_foreachC which uses an iterator struct */ + json_object_object_foreachC(this, iter) { + if(i) sprintbuf(pb, ","); + sprintbuf(pb, " \""); + json_escape_str(pb, iter.key); + sprintbuf(pb, "\": "); + if(iter.val == NULL) sprintbuf(pb, "null"); + else iter.val->_to_json_string(iter.val, pb); + i++; + } + + return sprintbuf(pb, " }"); +} + +static void json_object_lh_entry_free(struct lh_entry *ent) +{ + free(ent->k); + json_object_put((struct json_object*)ent->v); +} + +static void json_object_object_delete(struct json_object* this) +{ + lh_table_free(this->o.c_object); + json_object_generic_delete(this); +} + +struct json_object* json_object_new_object() +{ + struct json_object *this = json_object_new(json_type_object); + if(!this) return NULL; + this->_delete = &json_object_object_delete; + this->_to_json_string = &json_object_object_to_json_string; + this->o.c_object = lh_kchar_table_new(JSON_OBJECT_DEF_HASH_ENTIRES, + NULL, &json_object_lh_entry_free); + return this; +} + +struct lh_table* json_object_get_object(struct json_object *this) +{ + if(!this) return NULL; + switch(this->o_type) { + case json_type_object: + return this->o.c_object; + default: + return NULL; + } +} + +void json_object_object_add(struct json_object* this, char *key, + struct json_object *val) +{ + lh_table_delete(this->o.c_object, key); + lh_table_insert(this->o.c_object, strdup(key), val); +} + +struct json_object* json_object_object_get(struct json_object* this, char *key) +{ + return (struct json_object*) lh_table_lookup(this->o.c_object, key); +} + +void json_object_object_del(struct json_object* this, char *key) +{ + lh_table_delete(this->o.c_object, key); +} + + +/* json_object_boolean */ + +static int json_object_boolean_to_json_string(struct json_object* this, + struct printbuf *pb) +{ + if(this->o.c_boolean) return sprintbuf(pb, "true"); + else return sprintbuf(pb, "false"); +} + +struct json_object* json_object_new_boolean(boolean b) +{ + struct json_object *this = json_object_new(json_type_boolean); + if(!this) return NULL; + this->_to_json_string = &json_object_boolean_to_json_string; + this->o.c_boolean = b; + return this; +} + +boolean json_object_get_boolean(struct json_object *this) +{ + if(!this) return FALSE; + switch(this->o_type) { + case json_type_boolean: + return this->o.c_boolean; + case json_type_int: + return (this->o.c_int != 0); + case json_type_double: + return (this->o.c_double != 0); + case json_type_string: + if(strlen(this->o.c_string)) return TRUE; + default: + return TRUE; + } +} + + +/* json_object_int */ + +static int json_object_int_to_json_string(struct json_object* this, + struct printbuf *pb) +{ + return sprintbuf(pb, "%d", this->o.c_int); +} + +struct json_object* json_object_new_int(int i) +{ + struct json_object *this = json_object_new(json_type_int); + if(!this) return NULL; + this->_to_json_string = &json_object_int_to_json_string; + this->o.c_int = i; + return this; +} + +int json_object_get_int(struct json_object *this) +{ + int cint; + + if(!this) return 0; + switch(this->o_type) { + case json_type_int: + return this->o.c_int; + case json_type_double: + return (int)this->o.c_double; + case json_type_boolean: + return this->o.c_boolean; + case json_type_string: + if(sscanf(this->o.c_string, "%d", &cint) == 1) return cint; + default: + return 0; + } +} + + +/* json_object_double */ + +static int json_object_double_to_json_string(struct json_object* this, + struct printbuf *pb) +{ + return sprintbuf(pb, "%lf", this->o.c_double); +} + +struct json_object* json_object_new_double(double d) +{ + struct json_object *this = json_object_new(json_type_double); + if(!this) return NULL; + this->_to_json_string = &json_object_double_to_json_string; + this->o.c_double = d; + return this; +} + +double json_object_get_double(struct json_object *this) +{ + double cdouble; + + if(!this) return 0.0; + switch(this->o_type) { + case json_type_double: + return this->o.c_double; + case json_type_int: + return this->o.c_int; + case json_type_boolean: + return this->o.c_boolean; + case json_type_string: + if(sscanf(this->o.c_string, "%lf", &cdouble) == 1) return cdouble; + default: + return 0.0; + } +} + + +/* json_object_string */ + +static int json_object_string_to_json_string(struct json_object* this, + struct printbuf *pb) +{ + sprintbuf(pb, "\""); + json_escape_str(pb, this->o.c_string); + sprintbuf(pb, "\""); + return 0; +} + +static void json_object_string_delete(struct json_object* this) +{ + free(this->o.c_string); + json_object_generic_delete(this); +} + +struct json_object* json_object_new_string(char *s) +{ + struct json_object *this = json_object_new(json_type_string); + if(!this) return NULL; + this->_delete = &json_object_string_delete; + this->_to_json_string = &json_object_string_to_json_string; + this->o.c_string = strdup(s); + return this; +} + +struct json_object* json_object_new_string_len(char *s, int len) +{ + struct json_object *this = json_object_new(json_type_string); + if(!this) return NULL; + this->_delete = &json_object_string_delete; + this->_to_json_string = &json_object_string_to_json_string; + this->o.c_string = strndup(s, len); + return this; +} + +char* json_object_get_string(struct json_object *this) +{ + if(!this) return NULL; + switch(this->o_type) { + case json_type_string: + return this->o.c_string; + default: + return json_object_to_json_string(this); + } +} + + +/* json_object_array */ + +static int json_object_array_to_json_string(struct json_object* this, + struct printbuf *pb) +{ + int i; + sprintbuf(pb, "["); + for(i=0; i < json_object_array_length(this); i++) { + struct json_object *val; + if(i) { sprintbuf(pb, ", "); } + else { sprintbuf(pb, " "); } + + val = json_object_array_get_idx(this, i); + if(val == NULL) { sprintbuf(pb, "null"); } + else { val->_to_json_string(val, pb); } + } + return sprintbuf(pb, " ]"); +} + +static void json_object_array_entry_free(void *data) +{ + json_object_put((struct json_object*)data); +} + +static void json_object_array_delete(struct json_object* this) +{ + array_list_free(this->o.c_array); + json_object_generic_delete(this); +} + +struct json_object* json_object_new_array() +{ + struct json_object *this = json_object_new(json_type_array); + if(!this) return NULL; + this->_delete = &json_object_array_delete; + this->_to_json_string = &json_object_array_to_json_string; + this->o.c_array = array_list_new(&json_object_array_entry_free); + return this; +} + +struct array_list* json_object_get_array(struct json_object *this) +{ + if(!this) return NULL; + switch(this->o_type) { + case json_type_array: + return this->o.c_array; + default: + return NULL; + } +} + +int json_object_array_length(struct json_object *this) +{ + return array_list_length(this->o.c_array); +} + +int json_object_array_add(struct json_object *this,struct json_object *val) +{ + return array_list_add(this->o.c_array, val); +} + +int json_object_array_put_idx(struct json_object *this, int idx, + struct json_object *val) +{ + return array_list_put_idx(this->o.c_array, idx, val); +} + +struct json_object* json_object_array_get_idx(struct json_object *this, + int idx) +{ + return (struct json_object*)array_list_get_idx(this->o.c_array, idx); +} + diff --git a/src/mod/applications/mod_http/json_object.h b/src/mod/applications/mod_http/json_object.h new file mode 100644 index 0000000000..71f956b297 --- /dev/null +++ b/src/mod/applications/mod_http/json_object.h @@ -0,0 +1,308 @@ +/* + * $Id: json_object.h,v 1.12 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_object_h_ +#define _json_object_h_ + +#define JSON_OBJECT_DEF_HASH_ENTIRES 16 + +#undef FALSE +#define FALSE ((boolean)0) + +#undef TRUE +#define TRUE ((boolean)1) + +extern char *json_number_chars; +extern char *json_hex_chars; + +/* forward structure definitions */ + +typedef int boolean; +struct printbuf; +struct lh_table; +struct array_list; +struct json_object; +struct json_object_iter; + +/* supported object types */ + +enum json_type { + json_type_null, + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string +}; + +/* reference counting functions */ + +/** + * Increment the reference count of json_object + * @param obj the json_object instance + */ +extern struct json_object* json_object_get(struct json_object *obj); + +/** + * Decrement the reference count of json_object and free if it reaches zero + * @param obj the json_object instance + */ +extern void json_object_put(struct json_object *obj); + + +/** + * Check if the json_object is of a given type + * @param obj the json_object instance + * @param type one of: + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, + */ +extern int json_object_is_type(struct json_object *obj, enum json_type type); + +/** + * Get the type of the json_object + * @param obj the json_object instance + * @returns type being one of: + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, + */ +extern enum json_type json_object_get_type(struct json_object *obj); + + +/** Stringify object to json format + * @param obj the json_object instance + * @returns a string in JSON format + */ +extern char* json_object_to_json_string(struct json_object *obj); + + +/* object type methods */ + +/** Create a new empty object + * @returns a json_object of type json_type_object + */ +extern struct json_object* json_object_new_object(); + +/** Get the hashtable of a json_object of type json_type_object + * @param obj the json_object instance + * @returns a linkhash + */ +extern struct lh_table* json_object_get_object(struct json_object *obj); + +/** Add an object field to a json_object of type json_type_object + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * @param obj the json_object instance + * @param key the object field name (a private copy will be duplicated) + * @param val a json_object or NULL member to associate with the given field + */ +extern void json_object_object_add(struct json_object* obj, char *key, + struct json_object *val); + +/** Get the json_object associate with a given object field + * @param obj the json_object instance + * @param key the object field name + * @returns the json_object associated with the given field name + */ +extern struct json_object* json_object_object_get(struct json_object* obj, + char *key); + +/** Delete the given json_object field + * + * The reference count will be decremented for the deleted object + * + * @param obj the json_object instance + * @param key the object field name + */ +extern void json_object_object_del(struct json_object* obj, char *key); + +/** Iterate through all keys and values of an object + * @param obj the json_object instance + * @param key the local name for the char* key variable defined in the body + * @param val the local name for the json_object* object variable defined in the body + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) + +# define json_object_object_foreach(obj,key,val) \ + for(entry = json_object_get_object(obj)->head; ({ if(entry) { key = (char*)entry->k; val = (struct json_object*)entry->v; } ; entry; }); entry = entry->next ) + +#else /* ANSI C or MSC */ + +# define json_object_object_foreach(obj,key,val) \ + for(entry = json_object_get_object(obj)->head; (entry ? (key = (char*)entry->k, val = (struct json_object*)entry->v, entry) : 0); entry = entry->next) + +#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) */ + +/** Iterate through all keys and values of an object (ANSI C Safe) + * @param obj the json_object instance + * @param iter the object iterator + */ +#define json_object_object_foreachC(obj,iter) \ + for(iter.entry = json_object_get_object(obj)->head; (iter.entry ? (iter.key = (char*)iter.entry->k, iter.val = (struct json_object*)iter.entry->v, iter.entry) : 0); iter.entry = iter.entry->next) + +/* Array type methods */ + +/** Create a new empty json_object of type json_type_array + * @returns a json_object of type json_type_array + */ +extern struct json_object* json_object_new_array(); + +/** Get the arraylist of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an arraylist + */ +extern struct array_list* json_object_get_array(struct json_object *obj); + +/** Get the length of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an int + */ +extern int json_object_array_length(struct json_object *obj); + +/** Add an element to the end of a json_object of type json_type_array + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * @param obj the json_object instance + * @param val the json_object to be added + */ +extern int json_object_array_add(struct json_object *obj, + struct json_object *val); + +/** Insert or replace an element at a specified index in an array (a json_object of type json_type_array) + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * The reference count of a replaced object will be decremented. + * + * The array size will be automatically be expanded to the size of the + * index if the index is larger than the current size. + * + * @param obj the json_object instance + * @param idx the index to insert the element at + * @param val the json_object to be added + */ +extern int json_object_array_put_idx(struct json_object *obj, int idx, + struct json_object *val); + +/** Get the element at specificed index of the array (a json_object of type json_type_array) + * @param obj the json_object instance + * @param idx the index to get the element at + * @returns the json_object at the specified index (or NULL) + */ +extern struct json_object* json_object_array_get_idx(struct json_object *obj, + int idx); + +/* boolean type methods */ + +/** Create a new empty json_object of type json_type_boolean + * @param b a boolean TRUE or FALSE (0 or 1) + * @returns a json_object of type json_type_boolean + */ +extern struct json_object* json_object_new_boolean(boolean b); + +/** Get the boolean value of a json_object + * + * The type is coerced to a boolean if the passed object is not a boolean. + * integer and double objects will return FALSE if there value is zero + * or TRUE otherwise. If the passed object is a string it will return + * TRUE if it has a non zero length. If any other object type is passed + * TRUE will be returned if the object is not NULL. + * + * @param obj the json_object instance + * @returns a boolean + */ +extern boolean json_object_get_boolean(struct json_object *obj); + + +/* int type methods */ + +/** Create a new empty json_object of type json_type_int + * @param i the integer + * @returns a json_object of type json_type_int + */ +extern struct json_object* json_object_new_int(int i); + +/** Get the int value of a json_object + * + * The type is coerced to a int if the passed object is not a int. + * double objects will return their integer conversion. Strings will be + * parsed as an integer. If no conversion exists then 0 is returned. + * + * @param obj the json_object instance + * @returns an int + */ +extern int json_object_get_int(struct json_object *obj); + + +/* double type methods */ + +/** Create a new empty json_object of type json_type_double + * @param d the double + * @returns a json_object of type json_type_double + */ +extern struct json_object* json_object_new_double(double d); + +/** Get the double value of a json_object + * + * The type is coerced to a double if the passed object is not a double. + * integer objects will return their dboule conversion. Strings will be + * parsed as a double. If no conversion exists then 0.0 is returned. + * + * @param obj the json_object instance + * @returns an double + */ +extern double json_object_get_double(struct json_object *obj); + + +/* string type methods */ + +/** Create a new empty json_object of type json_type_string + * + * A copy of the string is made and the memory is managed by the json_object + * + * @param s the string + * @returns a json_object of type json_type_string + */ +extern struct json_object* json_object_new_string(char *s); + +extern struct json_object* json_object_new_string_len(char *s, int len); + +/** Get the string value of a json_object + * + * If the passed object is not of type json_type_string then the JSON + * representation of the object is returned. + * + * The returned string memory is managed by the json_object and will + * be freed when the reference count of the json_object drops to zero. + * + * @param obj the json_object instance + * @returns a string + */ +extern char* json_object_get_string(struct json_object *obj); + +#endif diff --git a/src/mod/applications/mod_http/json_object_private.h b/src/mod/applications/mod_http/json_object_private.h new file mode 100644 index 0000000000..35a44f3a09 --- /dev/null +++ b/src/mod/applications/mod_http/json_object_private.h @@ -0,0 +1,44 @@ +/* + * $Id: json_object_private.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_object_private_h_ +#define _json_object_private_h_ + +typedef void (json_object_delete_fn)(struct json_object *o); +typedef int (json_object_to_json_string_fn)(struct json_object *o, + struct printbuf *pb); + +struct json_object +{ + enum json_type o_type; + json_object_delete_fn *_delete; + json_object_to_json_string_fn *_to_json_string; + int _ref_count; + struct printbuf *_pb; + union data { + boolean c_boolean; + double c_double; + int c_int; + struct lh_table *c_object; + struct array_list *c_array; + char *c_string; + } o; +}; + +/* CAW: added for ANSI C iteration correctness */ +struct json_object_iter +{ + char *key; + struct json_object *val; + struct lh_entry *entry; +}; + +#endif diff --git a/src/mod/applications/mod_http/json_tokener.c b/src/mod/applications/mod_http/json_tokener.c new file mode 100644 index 0000000000..8f743289bf --- /dev/null +++ b/src/mod/applications/mod_http/json_tokener.c @@ -0,0 +1,520 @@ +/* + * $Id: json_tokener.c,v 1.20 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "arraylist.h" +#include "json_object.h" +#include "json_tokener.h" + + +#if !HAVE_STRNCASECMP && defined(_MSC_VER) + /* MSC has the version as _strnicmp */ +# define strncasecmp _strnicmp +#elif !HAVE_STRNCASECMP +# error You do not have strncasecmp on your system. +#endif /* HAVE_STRNCASECMP */ + + +static const char* json_null_str = "null"; +static const char* json_true_str = "true"; +static const char* json_false_str = "false"; + +const char* json_tokener_errors[] = { + "success", + "continue", + "nesting to deep", + "unexpected end of data", + "unexpected character", + "null expected", + "boolean expected", + "number expected", + "array value separator ',' expected", + "quoted object property name expected", + "object property name separator ':' expected", + "object value separator ',' expected", + "invalid string sequence", + "expected comment", +}; + + +struct json_tokener* json_tokener_new() +{ + struct json_tokener *tok = calloc(1, sizeof(struct json_tokener)); + tok->pb = printbuf_new(); + json_tokener_reset(tok); + return tok; +} + +void json_tokener_free(struct json_tokener *tok) +{ + json_tokener_reset(tok); + if(tok) printbuf_free(tok->pb); + free(tok); +} + +static void json_tokener_reset_level(struct json_tokener *tok, int depth) +{ + tok->stack[depth].state = json_tokener_state_eatws; + tok->stack[depth].saved_state = json_tokener_state_start; + json_object_put(tok->stack[depth].current); + tok->stack[depth].current = NULL; + free(tok->stack[depth].obj_field_name); + tok->stack[depth].obj_field_name = NULL; +} + +void json_tokener_reset(struct json_tokener *tok) +{ + int i; + for(i = tok->depth; i >= 0; i--) + json_tokener_reset_level(tok, i); + tok->depth = 0; + tok->err = json_tokener_success; +} + +struct json_object* json_tokener_parse(char *str) +{ + struct json_tokener* tok; + struct json_object* obj; + + tok = json_tokener_new(); + obj = json_tokener_parse_ex(tok, str, -1); + if(tok->err != json_tokener_success) + obj = error_ptr(-tok->err); + json_tokener_free(tok); + return obj; +} + + +#if !HAVE_STRNDUP +/* CAW: compliant version of strndup() */ +char* strndup(const char* str, size_t n) +{ + if(str) { + size_t len = strlen(str); + size_t nn = min(len,n); + char* s = (char*)malloc(sizeof(char) * (nn + 1)); + + if(s) { + memcpy(s, str, nn); + s[nn] = '\0'; + } + + return s; + } + + return NULL; +} +#endif + + +#define state tok->stack[tok->depth].state +#define saved_state tok->stack[tok->depth].saved_state +#define current tok->stack[tok->depth].current +#define obj_field_name tok->stack[tok->depth].obj_field_name + +struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + char *str, int len) +{ + struct json_object *obj = NULL; + char c; + + tok->char_offset = 0; + tok->err = json_tokener_success; + + do { + if(tok->char_offset == len) { + if(tok->depth == 0 && state == json_tokener_state_eatws && + saved_state == json_tokener_state_finish) + tok->err = json_tokener_success; + else + tok->err = json_tokener_continue; + goto out; + } + + c = *str; + redo_char: + switch(state) { + + case json_tokener_state_eatws: + if(isspace(c)) { + /* okay */ + } else if(c == '/') { + printbuf_reset(tok->pb); + printbuf_memappend(tok->pb, &c, 1); + state = json_tokener_state_comment_start; + } else { + state = saved_state; + goto redo_char; + } + break; + + case json_tokener_state_start: + switch(c) { + case '{': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_object_field_start; + current = json_object_new_object(); + break; + case '[': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_array; + current = json_object_new_array(); + break; + case 'N': + case 'n': + state = json_tokener_state_null; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case '"': + case '\'': + state = json_tokener_state_string; + printbuf_reset(tok->pb); + tok->quote_char = c; + break; + case 'T': + case 't': + case 'F': + case 'f': + state = json_tokener_state_boolean; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; +/* +#if defined(__GNUC__) + case '0' ... '9': +#else +*/ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': +/* +#endif +*/ + case '-': + state = json_tokener_state_number; + printbuf_reset(tok->pb); + tok->is_double = 0; + goto redo_char; + default: + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + break; + + case json_tokener_state_finish: + if(tok->depth == 0) goto out; + obj = json_object_get(current); + json_tokener_reset_level(tok, tok->depth); + tok->depth--; + goto redo_char; + + case json_tokener_state_null: + printbuf_memappend(tok->pb, &c, 1); + if(strncasecmp(json_null_str, tok->pb->buf, + min(tok->st_pos+1, strlen(json_null_str))) == 0) { + if(tok->st_pos == strlen(json_null_str)) { + current = NULL; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_null; + goto out; + } + tok->st_pos++; + break; + + case json_tokener_state_comment_start: + if(c == '*') { + state = json_tokener_state_comment; + } else if(c == '/') { + state = json_tokener_state_comment_eol; + } else { + tok->err = json_tokener_error_parse_comment; + goto out; + } + printbuf_memappend(tok->pb, &c, 1); + break; + + case json_tokener_state_comment: + if(c == '*') state = json_tokener_state_comment_end; + printbuf_memappend(tok->pb, &c, 1); + break; + + case json_tokener_state_comment_eol: + if(c == '\n') { + mc_debug("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } else { + printbuf_memappend(tok->pb, &c, 1); + } + break; + + case json_tokener_state_comment_end: + printbuf_memappend(tok->pb, &c, 1); + if(c == '/') { + mc_debug("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } else { + state = json_tokener_state_comment; + } + break; + + case json_tokener_state_string: + if(c == tok->quote_char) { + current = json_object_new_string(tok->pb->buf); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == '\\') { + saved_state = json_tokener_state_string; + state = json_tokener_state_string_escape; + } else { + printbuf_memappend(tok->pb, &c, 1); + } + break; + + case json_tokener_state_string_escape: + switch(c) { + case '"': + case '\\': + case '/': + printbuf_memappend(tok->pb, &c, 1); + state = saved_state; + break; + case 'b': + case 'n': + case 'r': + case 't': + if(c == 'b') printbuf_memappend(tok->pb, "\b", 1); + else if(c == 'n') printbuf_memappend(tok->pb, "\n", 1); + else if(c == 'r') printbuf_memappend(tok->pb, "\r", 1); + else if(c == 't') printbuf_memappend(tok->pb, "\t", 1); + state = saved_state; + break; + case 'u': + tok->ucs_char = 0; + tok->st_pos = 0; + state = json_tokener_state_escape_unicode; + break; + default: + tok->err = json_tokener_error_parse_string; + goto out; + } + break; + + case json_tokener_state_escape_unicode: + if(strchr(json_hex_chars, c)) { + tok->ucs_char += ((unsigned int)hexdigit(c) << ((3-tok->st_pos++)*4)); + if(tok->st_pos == 4) { + unsigned char utf_out[3]; + if (tok->ucs_char < 0x80) { + utf_out[0] = tok->ucs_char; + printbuf_memappend(tok->pb, (char*)utf_out, 1); + } else if (tok->ucs_char < 0x800) { + utf_out[0] = 0xc0 | (tok->ucs_char >> 6); + utf_out[1] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend(tok->pb, (char*)utf_out, 2); + } else { + utf_out[0] = 0xe0 | (tok->ucs_char >> 12); + utf_out[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + utf_out[2] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend(tok->pb, (char*)utf_out, 3); + } + state = saved_state; + } + } else { + tok->err = json_tokener_error_parse_string; + goto out; + } + break; + + case json_tokener_state_boolean: + printbuf_memappend(tok->pb, &c, 1); + if(strncasecmp(json_true_str, tok->pb->buf, + min(tok->st_pos+1, strlen(json_true_str))) == 0) { + if(tok->st_pos == strlen(json_true_str)) { + current = json_object_new_boolean(1); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else if(strncasecmp(json_false_str, tok->pb->buf, + min(tok->st_pos+1, strlen(json_false_str))) == 0) { + if(tok->st_pos == strlen(json_false_str)) { + current = json_object_new_boolean(0); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_boolean; + goto out; + } + tok->st_pos++; + break; + + case json_tokener_state_number: + if(c && strchr(json_number_chars, c)) { + printbuf_memappend(tok->pb, &c, 1); + if(c == '.' || c == 'e') tok->is_double = 1; + } else { + int numi; + double numd; + if(!tok->is_double && sscanf(tok->pb->buf, "%d", &numi) == 1) { + current = json_object_new_int(numi); + } else if(tok->is_double && sscanf(tok->pb->buf, "%lf", &numd) == 1) { + current = json_object_new_double(numd); + } else { + tok->err = json_tokener_error_parse_number; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + break; + + case json_tokener_state_array: + if(c == ']') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else { + if(tok->depth >= JSON_TOKENER_MAX_DEPTH-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_array_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + } + break; + + case json_tokener_state_array_add: + json_object_array_add(current, obj); + saved_state = json_tokener_state_array_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_array_sep: + if(c == ']') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_array; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_array; + goto out; + } + break; + + case json_tokener_state_object_field_start: + if(c == '}') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if (c == '"' || c == '\'') { + tok->quote_char = c; + printbuf_reset(tok->pb); + state = json_tokener_state_object_field; + } else { + tok->err = json_tokener_error_parse_object_key_name; + goto out; + } + break; + + case json_tokener_state_object_field: + if(c == tok->quote_char) { + obj_field_name = strdup(tok->pb->buf); + saved_state = json_tokener_state_object_field_end; + state = json_tokener_state_eatws; + } else if(c == '\\') { + saved_state = json_tokener_state_object_field; + state = json_tokener_state_string_escape; + } else { + printbuf_memappend(tok->pb, &c, 1); + } + break; + + case json_tokener_state_object_field_end: + if(c == ':') { + saved_state = json_tokener_state_object_value; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_key_sep; + goto out; + } + break; + + case json_tokener_state_object_value: + if(tok->depth >= JSON_TOKENER_MAX_DEPTH-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_object_value_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + + case json_tokener_state_object_value_add: + json_object_object_add(current, obj_field_name, obj); + free(obj_field_name); + obj_field_name = NULL; + saved_state = json_tokener_state_object_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_object_sep: + if(c == '}') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_object_field_start; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_value_sep; + goto out; + } + break; + + } + str++; + tok->char_offset++; + } while(c); + + if(state != json_tokener_state_finish && + saved_state != json_tokener_state_finish) + tok->err = json_tokener_error_parse_eof; + + out: + if(tok->err == json_tokener_success) return json_object_get(current); + mc_debug("json_tokener_parse_ex: error %s at offset %d\n", + json_tokener_errors[tok->err], tok->char_offset); + return NULL; +} diff --git a/src/mod/applications/mod_http/json_tokener.h b/src/mod/applications/mod_http/json_tokener.h new file mode 100644 index 0000000000..d2c21270f4 --- /dev/null +++ b/src/mod/applications/mod_http/json_tokener.h @@ -0,0 +1,89 @@ +/* + * $Id: json_tokener.h,v 1.10 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_tokener_h_ +#define _json_tokener_h_ + +#include "json_object.h" + +enum json_tokener_error { + json_tokener_success, + json_tokener_continue, + json_tokener_error_depth, + json_tokener_error_parse_eof, + json_tokener_error_parse_unexpected, + json_tokener_error_parse_null, + json_tokener_error_parse_boolean, + json_tokener_error_parse_number, + json_tokener_error_parse_array, + json_tokener_error_parse_object_key_name, + json_tokener_error_parse_object_key_sep, + json_tokener_error_parse_object_value_sep, + json_tokener_error_parse_string, + json_tokener_error_parse_comment +}; + +enum json_tokener_state { + json_tokener_state_eatws, + json_tokener_state_start, + json_tokener_state_finish, + json_tokener_state_null, + json_tokener_state_comment_start, + json_tokener_state_comment, + json_tokener_state_comment_eol, + json_tokener_state_comment_end, + json_tokener_state_string, + json_tokener_state_string_escape, + json_tokener_state_escape_unicode, + json_tokener_state_boolean, + json_tokener_state_number, + json_tokener_state_array, + json_tokener_state_array_add, + json_tokener_state_array_sep, + json_tokener_state_object_field_start, + json_tokener_state_object_field, + json_tokener_state_object_field_end, + json_tokener_state_object_value, + json_tokener_state_object_value_add, + json_tokener_state_object_sep +}; + +struct json_tokener_srec +{ + enum json_tokener_state state, saved_state; + struct json_object *obj; + struct json_object *current; + char *obj_field_name; +}; + +#define JSON_TOKENER_MAX_DEPTH 32 + +struct json_tokener +{ + char *str; + struct printbuf *pb; + int depth, is_double, st_pos, char_offset; + enum json_tokener_error err; + unsigned int ucs_char; + char quote_char; + struct json_tokener_srec stack[JSON_TOKENER_MAX_DEPTH]; +}; + +extern const char* json_tokener_errors[]; + +extern struct json_tokener* json_tokener_new(); +extern void json_tokener_free(struct json_tokener *tok); +extern void json_tokener_reset(struct json_tokener *tok); +extern struct json_object* json_tokener_parse(char *str); +extern struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + char *str, int len); + +#endif diff --git a/src/mod/applications/mod_http/json_util.c b/src/mod/applications/mod_http/json_util.c new file mode 100644 index 0000000000..e20be24038 --- /dev/null +++ b/src/mod/applications/mod_http/json_util.c @@ -0,0 +1,121 @@ +/* + * $Id: json_util.c,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#if HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ + +#if HAVE_SYS_STAT_H +#include +#endif /* HAVE_SYS_STAT_H */ + +#if HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#if HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#ifdef WIN32 +# define WIN32_LEAN_AND_MEAN +# include +# include +#endif /* defined(WIN32) */ + +#if !HAVE_OPEN && defined(WIN32) +# define open _open +#endif + + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +struct json_object* json_object_from_file(char *filename) +{ + struct printbuf *pb; + struct json_object *obj; + char buf[JSON_FILE_BUF_SIZE]; + int fd, ret; + + if((fd = open(filename, O_RDONLY)) < 0) { + mc_error("json_object_from_file: error reading file %s: %s\n", + filename, strerror(errno)); + return error_ptr(-1); + } + if(!(pb = printbuf_new())) { + mc_error("json_object_from_file: printbuf_new failed\n"); + return error_ptr(-1); + } + while((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { + printbuf_memappend(pb, buf, ret); + } + close(fd); + if(ret < 0) { + mc_abort("json_object_from_file: error reading file %s: %s\n", + filename, strerror(errno)); + printbuf_free(pb); + return error_ptr(-1); + } + obj = json_tokener_parse(pb->buf); + printbuf_free(pb); + return obj; +} + +int json_object_to_file(char *filename, struct json_object *obj) +{ + char *json_str; + int fd, ret; + unsigned int wpos, wsize; + + if(!obj) { + mc_error("json_object_to_file: object is null\n"); + return -1; + } + + if((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) { + mc_error("json_object_to_file: error opening file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + if(!(json_str = json_object_to_json_string(obj))) { return -1; } + + + wsize = (unsigned int)(strlen(json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ + wpos = 0; + while(wpos < wsize) { + if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) { + close(fd); + mc_error("json_object_to_file: error writing file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + /* because of the above check for ret < 0, we can safely cast and add */ + wpos += (unsigned int)ret; + } + + close(fd); + return 0; +} diff --git a/src/mod/applications/mod_http/json_util.h b/src/mod/applications/mod_http/json_util.h new file mode 100644 index 0000000000..30fe2ab2c4 --- /dev/null +++ b/src/mod/applications/mod_http/json_util.h @@ -0,0 +1,23 @@ +/* + * $Id: json_util.h,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_util_h_ +#define _json_util_h_ + +#include "json_object.h" + +#define JSON_FILE_BUF_SIZE 4096 + +/* utlitiy functions */ +extern struct json_object* json_object_from_file(char *filename); +extern int json_object_to_file(char *filename, struct json_object *obj); + +#endif diff --git a/src/mod/applications/mod_http/linkhash.c b/src/mod/applications/mod_http/linkhash.c new file mode 100644 index 0000000000..6cfc9a0ea6 --- /dev/null +++ b/src/mod/applications/mod_http/linkhash.c @@ -0,0 +1,217 @@ +/* + * $Id: linkhash.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "linkhash.h" + +void lh_abort(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + exit(1); +} + +unsigned long lh_ptr_hash(void *k) +{ + /* CAW: refactored to be 64bit nice */ + return (unsigned long)((((ptrdiff_t)k * LH_PRIME) >> 4) & ULONG_MAX); +} + +int lh_ptr_equal(void *k1, void *k2) +{ + return (k1 == k2); +} + +unsigned long lh_char_hash(void *k) +{ + unsigned int h = 0; + const char* data = k; + + while( *data!=0 ) h = h*129 + (unsigned int)(*data++) + LH_PRIME; + + return h; +} + +int lh_char_equal(void *k1, void *k2) +{ + return (strcmp((char*)k1, (char*)k2) == 0); +} + +struct lh_table* lh_table_new(int size, char *name, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn) +{ + int i; + struct lh_table *t; + + t = calloc(1, sizeof(struct lh_table)); + if(!t) lh_abort("lh_table_new: calloc failed\n"); + t->count = 0; + t->size = size; + t->name = name; + t->table = calloc(size, sizeof(struct lh_entry)); + if(!t->table) lh_abort("lh_table_new: calloc failed\n"); + t->free_fn = free_fn; + t->hash_fn = hash_fn; + t->equal_fn = equal_fn; + for(i = 0; i < size; i++) t->table[i].k = LH_EMPTY; + return t; +} + +struct lh_table* lh_kchar_table_new(int size, char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_char_hash, lh_char_equal); +} + +struct lh_table* lh_kptr_table_new(int size, char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_ptr_hash, lh_ptr_equal); +} + +void lh_table_resize(struct lh_table *t, int new_size) +{ + struct lh_table *new_t; + struct lh_entry *ent; + + new_t = lh_table_new(new_size, t->name, NULL, t->hash_fn, t->equal_fn); + ent = t->head; + while(ent) { + lh_table_insert(new_t, ent->k, ent->v); + ent = ent->next; + } + free(t->table); + t->table = new_t->table; + t->size = new_size; + t->head = new_t->head; + t->tail = new_t->tail; + t->resizes++; + free(new_t); +} + +void lh_table_free(struct lh_table *t) +{ + struct lh_entry *c; + for(c = t->head; c != NULL; c = c->next) { + if(t->free_fn) { + t->free_fn(c); + } + } + free(t->table); + free(t); +} + + +int lh_table_insert(struct lh_table *t, void *k, void *v) +{ + unsigned long h, n; + + t->inserts++; + if(t->count > t->size * 0.66) lh_table_resize(t, t->size * 2); + + h = t->hash_fn(k); + n = h % t->size; + + while( 1 ) { + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) break; + t->collisions++; + if(++n == t->size) n = 0; + } + + t->table[n].k = k; + t->table[n].v = v; + t->count++; + + if(t->head == NULL) { + t->head = t->tail = &t->table[n]; + t->table[n].next = t->table[n].prev = NULL; + } else { + t->tail->next = &t->table[n]; + t->table[n].prev = t->tail; + t->table[n].next = NULL; + t->tail = &t->table[n]; + } + + return 0; +} + + +struct lh_entry* lh_table_lookup_entry(struct lh_table *t, void *k) +{ + unsigned long h = t->hash_fn(k); + unsigned long n = h % t->size; + + t->lookups++; + while( 1 ) { + if(t->table[n].k == LH_EMPTY) return NULL; + if(t->table[n].k != LH_FREED && + t->equal_fn(t->table[n].k, k)) return &t->table[n]; + if(++n == t->size) n = 0; + } + return NULL; +} + + +void* lh_table_lookup(struct lh_table *t, void *k) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if(e) return e->v; + return NULL; +} + + +int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e) +{ + ptrdiff_t n = (ptrdiff_t)(e - t->table); /* CAW: fixed to be 64bit nice, still need the crazy negative case... */ + + /* CAW: this is bad, really bad, maybe stack goes other direction on this machine... */ + if(n < 0) { return -2; } + + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) return -1; + t->count--; + if(t->free_fn) t->free_fn(e); + t->table[n].v = NULL; + t->table[n].k = LH_FREED; + if(t->tail == &t->table[n] && t->head == &t->table[n]) { + t->head = t->tail = NULL; + } else if (t->head == &t->table[n]) { + t->head->next->prev = NULL; + t->head = t->head->next; + } else if (t->tail == &t->table[n]) { + t->tail->prev->next = NULL; + t->tail = t->tail->prev; + } else { + t->table[n].prev->next = t->table[n].next; + t->table[n].next->prev = t->table[n].prev; + } + t->table[n].next = t->table[n].prev = NULL; + return 0; +} + + +int lh_table_delete(struct lh_table *t, void *k) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if(!e) return -1; + return lh_table_delete_entry(t, e); +} + diff --git a/src/mod/applications/mod_http/linkhash.h b/src/mod/applications/mod_http/linkhash.h new file mode 100644 index 0000000000..5c9fa852d8 --- /dev/null +++ b/src/mod/applications/mod_http/linkhash.h @@ -0,0 +1,261 @@ +/* + * $Id: linkhash.h,v 1.6 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _linkhash_h_ +#define _linkhash_h_ + +/** + * golden prime used in hash functions + */ +#define LH_PRIME 0x9e370001UL + +/** + * sentinel pointer value for empty slots + */ +#define LH_EMPTY (void*)-1 + +/** + * sentinel pointer value for freed slots + */ +#define LH_FREED (void*)-2 + +struct lh_entry; + +/** + * callback function prototypes + */ +typedef void (lh_entry_free_fn) (struct lh_entry *e); +/** + * callback function prototypes + */ +typedef unsigned long (lh_hash_fn) (void *k); +/** + * callback function prototypes + */ +typedef int (lh_equal_fn) (void *k1, void *k2); + +/** + * An entry in the hash table + */ +struct lh_entry { + /** + * The key. + */ + void *k; + /** + * The value. + */ + void *v; + /** + * The next entry + */ + struct lh_entry *next; + /** + * The previous entry. + */ + struct lh_entry *prev; +}; + + +/** + * The hash table structure. + */ +struct lh_table { + /** + * Size of our hash. + */ + int size; + /** + * Numbers of entries. + */ + int count; + + /** + * Number of collisions. + */ + int collisions; + + /** + * Number of resizes. + */ + int resizes; + + /** + * Number of lookups. + */ + int lookups; + + /** + * Number of inserts. + */ + int inserts; + + /** + * Number of deletes. + */ + int deletes; + + /** + * Name of the hash table. + */ + char *name; + + /** + * The first entry. + */ + struct lh_entry *head; + + /** + * The last entry. + */ + struct lh_entry *tail; + + struct lh_entry *table; + + /** + * A pointer onto the function responsible for freeing an entry. + */ + lh_entry_free_fn *free_fn; + lh_hash_fn *hash_fn; + lh_equal_fn *equal_fn; +}; + + +/** + * Pre-defined hash and equality functions + */ +extern unsigned long lh_ptr_hash(void *k); +extern int lh_ptr_equal(void *k1, void *k2); + +extern unsigned long lh_char_hash(void *k); +extern int lh_char_equal(void *k1, void *k2); + + +/** + * Convenience list iterator. + */ +#define lh_foreach(table, entry) \ +for(entry = table->head; entry; entry = entry->next) + +/** + * lh_foreach_safe allows calling of deletion routine while iterating. + */ +#define lh_foreach_safe(table, entry, tmp) \ +for(entry = table->head; entry && ((tmp = entry->next) || 1); entry = tmp) + + + +/** + * Create a new linkhash table. + * @param size initial table size. The table is automatically resized + * although this incurs a performance penalty. + * @param name the table name. + * @param free_fn callback function used to free memory for entries + * when lh_table_free or lh_table_delete is called. + * If NULL is provided, then memory for keys and values + * must be freed by the caller. + * @param hash_fn function used to hash keys. 2 standard ones are defined: + * lh_ptr_hash and lh_char_hash for hashing pointer values + * and C strings respectively. + * @param equal_fn comparison function to compare keys. 2 standard ones defined: + * lh_ptr_hash and lh_char_hash for comparing pointer values + * and C strings respectively. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_table_new(int size, char *name, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn); + +/** + * Convenience function to create a new linkhash + * table with char keys. + * @param size initial table size. + * @param name table name. + * @param free_fn callback function used to free memory for entries. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_kchar_table_new(int size, char *name, + lh_entry_free_fn *free_fn); + + +/** + * Convenience function to create a new linkhash + * table with ptr keys. + * @param size initial table size. + * @param name table name. + * @param free_fn callback function used to free memory for entries. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_kptr_table_new(int size, char *name, + lh_entry_free_fn *free_fn); + + +/** + * Free a linkhash table. + * If a callback free function is provided then it is called for all + * entries in the table. + * @param t table to free. + */ +extern void lh_table_free(struct lh_table *t); + + +/** + * Insert a record into the table. + * @param t the table to insert into. + * @param k a pointer to the key to insert. + * @param v a pointer to the value to insert. + */ +extern int lh_table_insert(struct lh_table *t, void *k, void *v); + + +/** + * Lookup a record into the table. + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the record structure of the value or NULL if it does not exist. + */ +extern struct lh_entry* lh_table_lookup_entry(struct lh_table *t, void *k); + +/** + * Lookup a record into the table + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the found value or NULL if it does not exist. + */ +extern void* lh_table_lookup(struct lh_table *t, void *k); + + +/** + * Delete a record from the table. + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param e a pointer to the entry to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e); + + +/** + * Delete a record from the table. + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param k a pointer to the key to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete(struct lh_table *t, void *k); + + +#endif diff --git a/src/mod/applications/mod_http/mod_http.c b/src/mod/applications/mod_http/mod_http.c new file mode 100644 index 0000000000..cc0f5aa6e4 --- /dev/null +++ b/src/mod/applications/mod_http/mod_http.c @@ -0,0 +1,356 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2008, Eric des Courtis + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Eric des Courtis + * Copyright (C) Benbria. All Rights Reserved. + * + * Contributor(s): + * + * Eric des Courtis + * + * + * mod_http.c -- HTTP client implementation for FreeSWITCH + * + * The purpose is to provide laguages like LUA with a _fast_ HTTP + * client implementation. + * + * Support for SSL will be provided in future versions. + * Initial release does not include win32 support. + * + */ + +#include +#include +#include +#include +#include +#include +#include "json.h" + +#include "http_req.h" +#include "url_encoding.h" + +#define HTTP_SYNTAX " [urlejson_body]" +#define HTTP_PARAMS 4 +#define HTTP_BUFFER_SIZE (256 * 1024) + +/* SWITCH_STANDARD_API(http_api_main); */ + +#define MAX_MEMLOCS 512 +#define GARBAGE_TYPES_INIT() \ +typedef struct memloc{\ + void *p;\ + LIST_ENTRY(memloc) memlocs;\ +} memloc_t + +#define GARBAGE_CLEANUP() \ +do{\ + for(memloc_p = head.lh_first; memloc_p != NULL; \ + memloc_p = memloc_p->memlocs.le_next){\ + free(memloc_p->p);\ + }\ +}while(0) + +#define GARBAGE_ADD(a) \ +do {\ + if(memloc_i >= MAX_MEMLOCS){\ + switch_safe_free(ccmd);\ + GARBAGE_CLEANUP();\ + stream->write_function(stream, "-ERR\n");\ + return SWITCH_STATUS_SUCCESS;\ + }\ + memloc_a[memloc_i].p = (void *)a;\ + LIST_INSERT_HEAD(&head, memloc_a + memloc_i, memlocs);\ + memloc_i++;\ +}while(0) + +#define GARBAGE_INIT() \ + LIST_HEAD(listhead, memloc) head;\ + memloc_t memloc_a[MAX_MEMLOCS];\ + memloc_t *memloc_p;\ + size_t memloc_i;\ + LIST_INIT(&head);\ + memloc_i = 0 + +GARBAGE_TYPES_INIT(); + +SWITCH_STANDARD_API(http_api_main) +{ + char *ccmd; + int argc; + char *argv[HTTP_PARAMS]; + char *buf; + char *method; + char *url; + char *headers_str; + char *value; + char *body; + char *body_dec; + char *t; + char *json_response; + struct json_object *json_http_headers; + char *key; + struct json_object *val; + struct lh_entry *entry; + int i; + int j; + int f; + size_t l; + size_t m; + size_t a = 0; + int ret; + + http_header_t *headers; + http_request_t request; + http_response_t response; + + GARBAGE_INIT(); + + (void)memset(&response, 0, sizeof(response)); + + if(cmd == NULL){ + stream->write_function(stream, "-USAGE: %s\n", HTTP_SYNTAX); + return SWITCH_STATUS_SUCCESS; + } + + ccmd = strdup(cmd); + argc = switch_separate_string(ccmd, ' ', argv, HTTP_PARAMS); + + if(argc != HTTP_PARAMS && argc != (HTTP_PARAMS - 1)){ + switch_safe_free(ccmd); + stream->write_function(stream, "-ERR\n"); + return SWITCH_STATUS_SUCCESS; + } + + method = argv[0]; + url = argv[1]; + headers_str = argv[2]; + if(argc == HTTP_PARAMS){ + body = argv[3]; + }else{ + body = (char *)malloc(1 * sizeof(char)); + if(body == NULL){ + switch_safe_free(ccmd); + stream->write_function(stream, "-ERR\n"); + return SWITCH_STATUS_SUCCESS; + } + body[0] = '\0'; + GARBAGE_ADD(body); + } + + buf = (char *)malloc(HTTP_BUFFER_SIZE * sizeof(char)); + if(buf == NULL){ + switch_safe_free(ccmd); + stream->write_function(stream, "-ERR\n"); + GARBAGE_CLEANUP(); + return SWITCH_STATUS_SUCCESS; + } + + GARBAGE_ADD(buf); + + request.version = DEFAULT_HTTP_VERSION; + l = strlen(url); + request.url = (char *)malloc((l + 1) * sizeof(char)); + if(request.url == NULL){ + switch_safe_free(ccmd); + stream->write_function(stream, "-ERR\n"); + GARBAGE_CLEANUP(); + return SWITCH_STATUS_SUCCESS; + } + + GARBAGE_ADD(request.url); + strcpy(request.url, url); + json_http_headers = json_tokener_parse(headers_str); + + i = 0; + json_object_object_foreach(json_http_headers, key, val){ + i++; + } + + request.header_len = i; + headers = (http_header_t *)malloc(i * sizeof(http_header_t)); + GARBAGE_ADD(headers); + + i = 0; + json_object_object_foreach(json_http_headers, key, val){ + l = strlen(key); + request.headers[i].field_name = (char *)malloc((l + 1) * sizeof(char)); + if(request.headers[i].field_name == NULL){ + switch_safe_free(ccmd); + stream->write_function(stream, "-ERR\n"); + GARBAGE_CLEANUP(); + return SWITCH_STATUS_SUCCESS; + } + GARBAGE_ADD(request.headers[i].field_name); + strcpy(request.headers[i].field_name, key); + a += strlen(key); + + value = json_object_to_json_string(val); + l = strlen(value); + request.headers[i].value = (char *)malloc((l + 1) * sizeof(char)); + if(request.headers[i].value == NULL){ + switch_safe_free(ccmd); + stream->write_function(stream, "-ERR\n"); + GARBAGE_CLEANUP(); + return SWITCH_STATUS_SUCCESS; + } + GARBAGE_ADD(request.headers[i].value); + strcpy(request.headers[i].value, value); + a += strlen(value); + i++; + } + + if(argc == HTTP_PARAMS){ + l = strlen(body); + body_dec = url_decode(body, l); + GARBAGE_ADD(body_dec); + l = strlen(body_dec); + request.body_len = l; + request.body = body_dec; + }else request.body_len = 0; + + ret = http_req(&request, &response); + if(response.version != NULL) GARBAGE_ADD(response.version); + if(response.phrase != NULL) GARBAGE_ADD(response.phrase); + if(response.headers != NULL) GARBAGE_ADD(response.headers); + if(response.body != NULL) GARBAGE_ADD(response.body); + for(i = 0; i < response.header_len; i++){ + GARBAGE_ADD(response.headers[i].field_name); + GARBAGE_ADD(response.headers[i].value); + } + + + if(ret == ERROR){ + switch_safe_free(ccmd); + stream->write_function(stream, "-ERR\n"); + GARBAGE_CLEANUP(); + return SWITCH_STATUS_SUCCESS; + } + + /* This is evil and should be changed in the future */ + l = 128 + (256 * response.header_len) + (a * 2) + + strlen("version") + strlen(response.version) + + strlen("status_code") + 3 + + strlen("phrase") + strlen(response.phrase) + + strlen("body") + (response.body_len * 3) + 1 + + strlen("headers") + + 1; + + /* to be safe */ + l <<= 2; + + json_response = (char *)malloc(l * sizeof(char)); + if(json_response == NULL){ + switch_safe_free(ccmd); + stream->write_function(stream, "-ERR\n"); + GARBAGE_CLEANUP(); + return SWITCH_STATUS_SUCCESS; + } + GARBAGE_ADD(json_response); + + if(response.body_len != 0){ + t = (char *)malloc((response.body_len + 1) * sizeof(char)); + if(t == NULL){ + switch_safe_free(ccmd); + stream->write_function(stream, "-ERR\n"); + GARBAGE_CLEANUP(); + return SWITCH_STATUS_SUCCESS; + } + GARBAGE_ADD(t); + (void)memcpy(t, response.body, response.body_len); + t[response.body_len] = '\0'; + response.body = url_encode(t, response.body_len); + GARBAGE_ADD(response.body); + } + + + m = snprintf(json_response, l, + "{" + "\"version\": \"%s\"," + "\"status_code\": \"%3d\"," + "\"phrase\": \"%s\"," + "\"body\": \"%s\"," + "\"headers\": [", + response.version, + response.status_code, + response.phrase, + ((response.body_len <= 0)? "":response.body) + ); + + + for(f = HTTP_FALSE, j = 0; j < response.header_len; j++){ + if(f != HTTP_FALSE){ + m += snprintf(json_response + m, l - m, + "," + ); + }else f = HTTP_TRUE; + + m += snprintf(json_response + m, l - m, + "{\"key\": \"%s\",\"value\": \"%s\"}", + response.headers[j].field_name, + response.headers[j].value + ); + } + + + m += snprintf(json_response + m, l - m, "]}"); + json_response[m] = '\0'; + + + switch_log_printf( + SWITCH_CHANNEL_LOG, + SWITCH_LOG_NOTICE, + "RESERVED %d BYTES, USED %d BYTES, HTTP Response as JSON: %s\n", + l, + m, + json_response + ); + + + stream->write_function(stream, "%s\n", json_response); + + switch_safe_free(ccmd); + GARBAGE_CLEANUP(); + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_LOAD_FUNCTION(mod_http_load); +SWITCH_MODULE_DEFINITION(mod_http, mod_http_load, NULL, NULL); + +SWITCH_MODULE_LOAD_FUNCTION(mod_http_load) +{ + switch_api_interface_t *api_interface; + + *module_interface = + switch_loadable_module_create_module_interface(pool, modname); + + switch_log_printf( + SWITCH_CHANNEL_LOG, + SWITCH_LOG_NOTICE, + "HTTP request mod enabled\n" + ); + + SWITCH_ADD_API( + api_interface, + "http", + "Make HTTP requests", + http_api_main, + HTTP_SYNTAX + ); + + return SWITCH_STATUS_SUCCESS; +} + diff --git a/src/mod/applications/mod_http/printbuf.c b/src/mod/applications/mod_http/printbuf.c new file mode 100644 index 0000000000..79bdaf554a --- /dev/null +++ b/src/mod/applications/mod_http/printbuf.c @@ -0,0 +1,144 @@ +/* + * $Id: printbuf.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include + +#if HAVE_STDARG_H +# include +#else /* !HAVE_STDARG_H */ +# error Not enough var arg support! +#endif /* HAVE_STDARG_H */ + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" + +struct printbuf* printbuf_new() +{ + struct printbuf *p; + + if(!(p = calloc(1, sizeof(struct printbuf)))) return NULL; + p->size = 32; + p->bpos = 0; + if(!(p->buf = malloc(p->size))) { + free(p); + return NULL; + } + return p; +} + + +int printbuf_memappend(struct printbuf *p, char *buf, int size) +{ + char *t; + if(p->size - p->bpos <= size) { + int new_size = max(p->size * 2, p->bpos + size + 8); +#ifdef PRINTBUF_DEBUG + mc_debug("printbuf_memappend: realloc " + "bpos=%d wrsize=%d old_size=%d new_size=%d\n", + p->bpos, size, p->size, new_size); +#endif /* PRINTBUF_DEBUG */ + if(!(t = realloc(p->buf, new_size))) return -1; + p->size = new_size; + p->buf = t; + } + memcpy(p->buf + p->bpos, buf, size); + p->bpos += size; + p->buf[p->bpos]= '\0'; + return size; +} + +#if !HAVE_VSNPRINTF && defined(WIN32) +# define vsnprintf _vsnprintf +#elif !HAVE_VSNPRINTF /* !HAVE_VSNPRINTF */ +# error Need vsnprintf! +#endif /* !HAVE_VSNPRINTF && defined(WIN32) */ + +#if !HAVE_VASPRINTF +/* CAW: compliant version of vasprintf */ +static int vasprintf(char **buf, const char *fmt, va_list ap) +{ +#ifndef WIN32 + static char _T_emptybuffer = '\0'; +#endif /* !defined(WIN32) */ + int chars; + char *b; + + if(!buf) { return -1; } + +#ifdef WIN32 + chars = _vscprintf(fmt, ap)+1; +#else /* !defined(WIN32) */ + /* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite + our buffer like on some 64bit sun systems.... but hey, its time to move on */ + chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap)+1; + if(chars < 0) { chars *= -1; } /* CAW: old glibc versions have this problem */ +#endif /* defined(WIN32) */ + + b = (char*)malloc(sizeof(char)*chars); + if(!b) { return -1; } + + if((chars = vsprintf(b, fmt, ap)) < 0) + { + free(b); + } else { + *buf = b; + } + + return chars; +} +#endif /* !HAVE_VASPRINTF */ + +int sprintbuf(struct printbuf *p, const char *msg, ...) +{ + va_list ap; + char *t; + int size; + char buf[128]; + + /* user stack buffer first */ + va_start(ap, msg); + size = vsnprintf(buf, 128, msg, ap); + va_end(ap); + /* if string is greater than stack buffer, then use dynamic string + with vasprintf. Note: some implementation of vsnprintf return -1 + if output is truncated whereas some return the number of bytes that + would have been writen - this code handles both cases. */ + if(size == -1 || size > 127) { + int ret; + va_start(ap, msg); + if((size = vasprintf(&t, msg, ap)) == -1) return -1; + va_end(ap); + ret = printbuf_memappend(p, t, size); + free(t); + return ret; + } else { + return printbuf_memappend(p, buf, size); + } +} + +void printbuf_reset(struct printbuf *p) +{ + p->buf[0] = '\0'; + p->bpos = 0; +} + +void printbuf_free(struct printbuf *p) +{ + if(p) { + free(p->buf); + free(p); + } +} diff --git a/src/mod/applications/mod_http/printbuf.h b/src/mod/applications/mod_http/printbuf.h new file mode 100644 index 0000000000..bc1669bd19 --- /dev/null +++ b/src/mod/applications/mod_http/printbuf.h @@ -0,0 +1,38 @@ +/* + * $Id: printbuf.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _printbuf_h_ +#define _printbuf_h_ + +#undef PRINTBUF_DEBUG + +struct printbuf { + char *buf; + int bpos; + int size; +}; + +extern struct printbuf* +printbuf_new(); + +extern int +printbuf_memappend(struct printbuf *p, char *buf, int size); + +extern int +sprintbuf(struct printbuf *p, const char *msg, ...); + +extern void +printbuf_reset(struct printbuf *p); + +extern void +printbuf_free(struct printbuf *p); + +#endif diff --git a/src/mod/applications/mod_http/url_encoding.c b/src/mod/applications/mod_http/url_encoding.c new file mode 100644 index 0000000000..3529f5e424 --- /dev/null +++ b/src/mod/applications/mod_http/url_encoding.c @@ -0,0 +1,125 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2008, Eric des Courtis + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Eric des Courtis + * Copyright (C) Benbria. All Rights Reserved. + * + * Contributor(s): + * + * Eric des Courtis + * + * + * url_encoding.c -- url encoding/decoding + * + */ +#include "url_encoding.h" + +#ifdef DEBUG +int main(int argc, char *argv[]) +{ + char *buf1; + char *buf2; + + + buf1 = url_encode("This is a test #$ "); + buf2 = url_decode(buf1); + + printf("%s\n", buf2); + + free(buf1); + free(buf2); + return EXIT_FAILURE; +} + +#endif + +char *url_encode(char *url, size_t l) +{ + int i; + int j; + char *buf; + unsigned char c; + + buf = (char *)malloc((l * 3) + 1); + if(buf == NULL){ + perror("Could not allocate memory url encoding"); + return NULL; + } + + for(i = 0, j = 0; i < l; i++){ + c = (unsigned char)url[i]; + if(c <= 31 || c >= 127 + || c == '$' || c == '&' || c == '+' || c == ',' || c == '/' + || c == ':' || c == ';' || c == '=' || c == '?' || c == '@' + || c == ' ' || c == '"' || c == '<' || c == '>' || c == '#' + || c == '%' || c == '{' || c == '}' || c == '|' || c == '\\' + || c == '^' || c == '~' || c == '[' || c == ']' || c == '`'){ + + (void)sprintf(buf + j, "%%%X%X", c >> 4, c & 0x0F); + j += 3; + }else{ + buf[j] = url[i]; + j++; + } + } + + buf[j] = '\0'; + + return buf; +} + +char *url_decode(char *url, size_t l) +{ + int i; + int j; + char *buf; + char c; + char d0; + char d1; + + buf = (char *)malloc((l + 1) * sizeof(char)); + if(buf == NULL){ + perror("Could not allocate memory for decoding"); + return NULL; + } + + for(i = 0, j = 0; i < l; j++){ + c = url[i]; + if(c == '%'){ + d0 = url[i + 2]; + d1 = url[i + 1]; + d0 = toupper(d0); + d1 = toupper(d1); + + if(d0 >= 'A' && d0 <= 'F') d0 = d0 - 'A' + 10; + else if(d0 >= '0' && d0 <= '9') d0 = d0 - '0'; + if(d1 >= 'A' && d1 <= 'F') d1 = d1 - 'A' + 10; + else if(d1 >= '0' && d1 <= '9') d1 = d1 - '0'; + + buf[j] = (d1 << 4) + d0; + i += 3; + }else{ + buf[j] = url[i]; + i++; + } + } + + buf[j] = '\0'; + + return buf; +} + + diff --git a/src/mod/applications/mod_http/url_encoding.h b/src/mod/applications/mod_http/url_encoding.h new file mode 100644 index 0000000000..49723c3a7a --- /dev/null +++ b/src/mod/applications/mod_http/url_encoding.h @@ -0,0 +1,42 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2008, Eric des Courtis + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Eric des Courtis + * Copyright (C) Benbria. All Rights Reserved. + * + * Contributor(s): + * + * Eric des Courtis + * + * + * url_encoding.h -- url encoding/decoding + * + */ + +#ifndef __URL_ENCODING_H__ +#define __URL_ENCODING_H__ + +#include +#include +#include +#include +#include + +char *url_encode(char *url, size_t l); +char *url_decode(char *url, size_t l); + +#endif +