From 20b382cfff4a276156d1bac863ae3b6cc9d2a384 Mon Sep 17 00:00:00 2001 From: Luigi Rizzo Date: Sat, 16 Dec 2006 09:33:31 +0000 Subject: replace ast_build_string() with ast_str_*() functions. This makes the code easier to follow and saves some copies to intermediate buffers. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@48515 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- include/asterisk/http.h | 8 +-- include/asterisk/strings.h | 21 ++++++ main/http.c | 129 +++++++++++++++++------------------- main/manager.c | 158 ++++++++++++++++++++++----------------------- 4 files changed, 159 insertions(+), 157 deletions(-) diff --git a/include/asterisk/http.h b/include/asterisk/http.h index bfd39c039..c3e2ba0fa 100644 --- a/include/asterisk/http.h +++ b/include/asterisk/http.h @@ -144,7 +144,7 @@ int ssl_setup(struct tls_config *cfg); The return value may include additional headers at the front and MUST include a blank line with \r\n to provide separation between user headers and content (even if no content is specified) */ -typedef char *(*ast_http_callback)(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength); +typedef struct ast_str *(*ast_http_callback)(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength); struct ast_http_uri { struct ast_http_uri *next; @@ -157,14 +157,12 @@ struct ast_http_uri { /*! \brief Link into the Asterisk HTTP server */ int ast_http_uri_link(struct ast_http_uri *urihandler); -/*! \brief Return a malloc()'d string containing an HTTP error message */ -char *ast_http_error(int status, const char *title, const char *extra_header, const char *text); +/*! \brief Return an ast_str malloc()'d string containing an HTTP error message */ +struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text); /*! \brief Destroy an HTTP server */ void ast_http_uri_unlink(struct ast_http_uri *urihandler); -char *ast_http_setcookie(const char *var, const char *val, int expires, char *buf, size_t buflen); - int ast_http_init(void); int ast_http_reload(void); diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h index 7bfd385a4..832a964ca 100644 --- a/include/asterisk/strings.h +++ b/include/asterisk/strings.h @@ -336,6 +336,27 @@ struct ast_str * attribute_malloc ast_str_create(size_t init_len), } ) +/*! + * Make space in a new string (e.g. to read in data from a file) + */ +AST_INLINE_API( +int ast_str_make_space(struct ast_str **buf, size_t new_len), +{ + if (new_len <= (*buf)->len) + return 0; /* success */ + if ((*buf)->ts == DS_ALLOCA || (*buf)->ts == DS_STATIC) + return -1; /* cannot extend */ + *buf = ast_realloc(*buf, new_len + sizeof(struct ast_str)); + if (*buf == NULL) /* XXX watch out, we leak memory here */ + return -1; + if ((*buf)->ts != DS_MALLOC) + pthread_setspecific((*buf)->ts->key, *buf); + + (*buf)->len = new_len; + return 0; +} +) + #define ast_str_alloca(init_len) \ ({ \ struct ast_str *buf; \ diff --git a/main/http.c b/main/http.c index 96e0bd82b..62add3eab 100644 --- a/main/http.c +++ b/main/http.c @@ -148,17 +148,15 @@ static char *uri_decode(char *buf) } return buf; } -static char *static_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength) +static struct ast_str *static_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength) { - char result[4096]; - char *c=result; + struct ast_str *result; char *path; char *ftype, *mtype; char wkspace[80]; struct stat st; int len; int fd; - void *blob; /* Yuck. I'm not really sold on this, but if you don't deliver static content it makes your configuration substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */ @@ -188,23 +186,22 @@ static char *static_callback(struct sockaddr_in *req, const char *uri, struct as fd = open(path, O_RDONLY); if (fd < 0) goto out403; - + len = st.st_size + strlen(mtype) + 40; - - blob = malloc(len); - if (blob) { - c = blob; - sprintf(c, "Content-type: %s\r\n\r\n", mtype); - c += strlen(c); - *contentlength = read(fd, c, st.st_size); - if (*contentlength < 0) { - close(fd); - free(blob); - goto out403; - } + result = ast_str_create(len); + if (result == NULL) /* XXX not really but... */ + goto out403; + + ast_str_append(&result, 0, "Content-type: %s\r\n\r\n", mtype); + *contentlength = read(fd, result->str + result->used, st.st_size); + if (*contentlength < 0) { + close(fd); + free(result); + goto out403; } + result->used += *contentlength; close(fd); - return blob; + return result; out404: *status = 404; @@ -218,44 +215,41 @@ out403: } -static char *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength) +static struct ast_str *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength) { - char result[4096]; - size_t reslen = sizeof(result); - char *c=result; + struct ast_str *out = ast_str_create(512); struct ast_variable *v; - ast_build_string(&c, &reslen, + if (out == NULL) + return out; + + ast_str_append(&out, 0, "\r\n" "Asterisk HTTP Status\r\n" "\r\n" "\r\n"); - ast_build_string(&c, &reslen, "\r\n", prefix); - ast_build_string(&c, &reslen, "\r\n", + ast_str_append(&out, 0, "\r\n", prefix); + ast_str_append(&out, 0, "\r\n", ast_inet_ntoa(http_desc.oldsin.sin_addr)); - ast_build_string(&c, &reslen, "\r\n", + ast_str_append(&out, 0, "\r\n", ntohs(http_desc.oldsin.sin_port)); if (http_tls_cfg.enabled) - ast_build_string(&c, &reslen, "\r\n", + ast_str_append(&out, 0, "\r\n", ntohs(https_desc.oldsin.sin_port)); - ast_build_string(&c, &reslen, "\r\n"); - v = vars; - while(v) { + ast_str_append(&out, 0, "\r\n"); + for (v = vars; v; v = v->next) { if (strncasecmp(v->name, "cookie_", 7)) - ast_build_string(&c, &reslen, "\r\n", v->name, v->value); - v = v->next; + ast_str_append(&out, 0, "\r\n", v->name, v->value); } - ast_build_string(&c, &reslen, "\r\n"); - v = vars; - while(v) { + ast_str_append(&out, 0, "\r\n"); + for (v = vars; v; v = v->next) { if (!strncasecmp(v->name, "cookie_", 7)) - ast_build_string(&c, &reslen, "\r\n", v->name, v->value); - v = v->next; + ast_str_append(&out, 0, "\r\n", v->name, v->value); } - ast_build_string(&c, &reslen, "
\r\n" "

  Asterisk™ HTTP Status

Prefix%s
Bind Address%s
Prefix%s
Bind Address%s
Bind Port%d
Bind Port%d
SSL Bind Port%d
SSL Bind Port%d


Submitted Variable '%s'%s
Submitted Variable '%s'%s


Cookie '%s'%s
Cookie '%s'%s
Asterisk and Digium are registered trademarks of Digium, Inc.
\r\n"); - return strdup(result); + ast_str_append(&out, 0, "
Asterisk and Digium are registered trademarks of Digium, Inc.
\r\n"); + return out; } static struct ast_http_uri statusuri = { @@ -272,10 +266,12 @@ static struct ast_http_uri staticuri = { .has_subtree = 1, }; -char *ast_http_error(int status, const char *title, const char *extra_header, const char *text) +struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text) { - char *c = NULL; - asprintf(&c, + struct ast_str *out = ast_str_create(512); + if (out == NULL) + return out; + ast_str_set(&out, 0, "Content-type: text/html\r\n" "%s" "\r\n" @@ -289,7 +285,7 @@ char *ast_http_error(int status, const char *title, const char *extra_header, co "
Asterisk Server
\r\n" "\r\n", (extra_header ? extra_header : ""), status, title, title, text); - return c; + return out; } /*! \brief @@ -334,9 +330,10 @@ void ast_http_uri_unlink(struct ast_http_uri *urih) } } -static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength, struct ast_variable **cookies) +static struct ast_str *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength, struct ast_variable **cookies) { char *c; + struct ast_str *out = NULL; char *params = uri; struct ast_http_uri *urih=NULL; int l; @@ -395,22 +392,22 @@ static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char ** } } if (urih) { - c = urih->callback(sin, uri, vars, status, title, contentlength); + out = urih->callback(sin, uri, vars, status, title, contentlength); } else if (ast_strlen_zero(uri) && ast_strlen_zero(prefix)) { /* Special case: no prefix, no URI, send to /static/index.html */ - c = ast_http_error(302, "Moved Temporarily", + out = ast_http_error(302, "Moved Temporarily", "Location: /static/index.html\r\n", "This is not the page you are looking for..."); *status = 302; *title = strdup("Moved Temporarily"); } else { - c = ast_http_error(404, "Not Found", NULL, + out = ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server."); *status = 404; *title = strdup("Not Found"); } ast_variables_destroy(vars); - return c; + return out; } #ifdef DO_SSL @@ -509,8 +506,9 @@ static void *httpd_helper_thread(void *data) char cookie[4096]; struct server_instance *ser = data; struct ast_variable *var, *prev=NULL, *vars=NULL; - char *uri, *c, *title=NULL; + char *uri, *title=NULL; int status = 200, contentlength = 0; + struct ast_str *out = NULL; if (!fgets(buf, sizeof(buf), ser->f)) goto done; @@ -522,7 +520,7 @@ static void *httpd_helper_thread(void *data) uri = ast_skip_blanks(uri); /* Skip white space */ if (*uri) { /* terminate at the first blank */ - c = ast_skip_nonblanks(uri); + char *c = ast_skip_nonblanks(uri); if (*c) *c = '\0'; } @@ -582,20 +580,20 @@ static void *httpd_helper_thread(void *data) } if (!*uri) - c = ast_http_error(400, "Bad Request", NULL, "Invalid Request"); + out = ast_http_error(400, "Bad Request", NULL, "Invalid Request"); else if (strcasecmp(buf, "get")) - c = ast_http_error(501, "Not Implemented", NULL, + out = ast_http_error(501, "Not Implemented", NULL, "Attempt to use unimplemented / unsupported method"); else /* try to serve it */ - c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars); + out = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars); /* If they aren't mopped up already, clean up the cookies */ if (vars) ast_variables_destroy(vars); - if (!c) - c = ast_http_error(500, "Internal Error", NULL, "Internal Server Error"); - if (c) { + if (out == NULL) + out = ast_http_error(500, "Internal Error", NULL, "Internal Server Error"); + if (out) { time_t t = time(NULL); char timebuf[256]; @@ -606,18 +604,18 @@ static void *httpd_helper_thread(void *data) "Connection: close\r\n", status, title ? title : "OK", timebuf); if (!contentlength) { /* opaque body ? just dump it hoping it is properly formatted */ - fprintf(ser->f, "%s", c); + fprintf(ser->f, "%s", out->str); } else { - char *tmp = strstr(c, "\r\n\r\n"); + char *tmp = strstr(out->str, "\r\n\r\n"); if (tmp) { fprintf(ser->f, "Content-length: %d\r\n", contentlength); /* first write the header, then the body */ - fwrite(c, 1, (tmp + 4 - c), ser->f); + fwrite(out->str, 1, (tmp + 4 - out->str), ser->f); fwrite(tmp + 4, 1, contentlength, ser->f); } } - free(c); + free(out); } if (title) free(title); @@ -678,17 +676,6 @@ void *server_root(void *data) return NULL; } -char *ast_http_setcookie(const char *var, const char *val, int expires, char *buf, size_t buflen) -{ - char *c; - c = buf; - ast_build_string(&c, &buflen, "Set-Cookie: %s=\"%s\"; Version=\"1\"", var, val); - if (expires) - ast_build_string(&c, &buflen, "; Max-Age=%d", expires); - ast_build_string(&c, &buflen, "\r\n"); - return buf; -} - int ssl_setup(struct tls_config *cfg) { #ifndef DO_SSL diff --git a/main/manager.c b/main/manager.c index 4247e2241..22d587f47 100644 --- a/main/manager.c +++ b/main/manager.c @@ -2491,44 +2491,58 @@ static void vars2msg(struct message *m, struct ast_variable *vars) * mode & 1 -> lowercase; * mode & 2 -> replace non-alphanumeric chars with underscore */ -static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int mode) -{ - for ( ; *src && *maxlen > 6; src++) { +static void xml_copy_escape(struct ast_str **out, const char *src, int mode) +{ + /* store in a local buffer to avoid calling ast_str_append too often */ + char buf[256]; + char *dst = buf; + int space = sizeof(buf); + /* repeat until done and nothing to flush */ + for ( ; *src || dst != buf ; src++) { + if (*src == '\0' || space < 10) { /* flush */ + *dst++ = '\0'; + ast_str_append(out, 0, "%s", buf); + dst = buf; + space = sizeof(buf); + if (*src == '\0') + break; + } + if ( (mode & 2) && !isalnum(*src)) { - *(*dst)++ = '_'; - (*maxlen)--; + *dst++ = '_'; + space--; continue; } switch (*src) { case '<': - strcpy(*dst, "<"); - (*dst) += 4; - *maxlen -= 4; + strcpy(dst, "<"); + dst += 4; + space -= 4; break; case '>': - strcpy(*dst, ">"); - (*dst) += 4; - *maxlen -= 4; + strcpy(dst, ">"); + dst += 4; + space -= 4; break; case '\"': - strcpy(*dst, """); - (*dst) += 6; - *maxlen -= 6; + strcpy(dst, """); + dst += 6; + space -= 6; break; case '\'': - strcpy(*dst, "'"); - (*dst) += 6; - *maxlen -= 6; + strcpy(dst, "'"); + dst += 6; + space -= 6; break; case '&': - strcpy(*dst, "&"); - (*dst) += 5; - *maxlen -= 5; + strcpy(dst, "&"); + dst += 5; + space -= 5; break; default: - *(*dst)++ = mode ? tolower(*src) : *src; - (*maxlen)--; + *dst++ = mode ? tolower(*src) : *src; + space--; } } } @@ -2558,19 +2572,14 @@ static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int mod * Sections (blank lines in the input) are separated by a
* */ -static char *xml_translate(char *in, struct ast_variable *vars, enum output_format format) +static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format) { struct ast_variable *v; char *dest = NULL; - char *out, *tmp, *var, *val; + char *var, *val; char *objtype = NULL; - int colons = 0; - int breaks = 0; - size_t len; int in_data = 0; /* parsing data */ - int escaped = 0; int inobj = 0; - int x; int xml = (format == FORMAT_XML); for (v = vars; v; v = v->next) { @@ -2583,7 +2592,7 @@ static char *xml_translate(char *in, struct ast_variable *vars, enum output_form dest = "unknown"; if (!objtype) objtype = "generic"; - +#if 0 /* determine how large is the response. * This is a heuristic - counting colons (for headers), * newlines (for extra arguments), and escaped chars. @@ -2604,6 +2613,7 @@ static char *xml_translate(char *in, struct ast_variable *vars, enum output_form return NULL; tmp = out; *tmp = '\0'; +#endif /* we want to stop when we find an empty line */ while (in && *in) { val = strsep(&in, "\r\n"); /* mark start and end of line */ @@ -2614,10 +2624,10 @@ static char *xml_translate(char *in, struct ast_variable *vars, enum output_form ast_verbose("inobj %d in_data %d line <%s>\n", inobj, in_data, val); if (ast_strlen_zero(val)) { if (in_data) { /* close data */ - ast_build_string(&tmp, &len, xml ? "'" : "\n"); + ast_str_append(out, 0, xml ? "'" : "\n"); in_data = 0; } - ast_build_string(&tmp, &len, xml ? " />\n" : + ast_str_append(out, 0, xml ? " />\n" : "
\r\n"); inobj = 0; continue; @@ -2637,45 +2647,41 @@ static char *xml_translate(char *in, struct ast_variable *vars, enum output_form } if (!inobj) { if (xml) - ast_build_string(&tmp, &len, "<%s", dest, objtype); + ast_str_append(out, 0, "<%s", dest, objtype); else - ast_build_string(&tmp, &len, "\n"); + ast_str_append(out, 0, "\n"); inobj = 1; } if (!in_data) { /* build appropriate line start */ - ast_build_string(&tmp, &len, xml ? " " : ""); - xml_copy_escape(&tmp, &len, var, xml ? 1 | 2 : 0); - ast_build_string(&tmp, &len, xml ? "='" : ""); + ast_str_append(out, 0, xml ? " " : ""); + xml_copy_escape(out, var, xml ? 1 | 2 : 0); + ast_str_append(out, 0, xml ? "='" : ""); if (!strcmp(var, "Opaque-data")) in_data = 1; } - xml_copy_escape(&tmp, &len, val, 0); /* data field */ + xml_copy_escape(out, val, 0); /* data field */ if (!in_data) - ast_build_string(&tmp, &len, xml ? "'" : "\n"); + ast_str_append(out, 0, xml ? "'" : "\n"); else - ast_build_string(&tmp, &len, xml ? "\n" : "
\n"); + ast_str_append(out, 0, xml ? "\n" : "
\n"); } if (inobj) - ast_build_string(&tmp, &len, xml ? " />
\n" : + ast_str_append(out, 0, xml ? " />
\n" : "
\r\n"); - return out; } -static char *generic_http_callback(enum output_format format, +static struct ast_str *generic_http_callback(enum output_format format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength) { struct mansession *s = NULL; unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */ - char workspace[1024]; - size_t len = sizeof(workspace); int blastaway = 0; - char *c = workspace; - char *retval = NULL; struct message m; struct ast_variable *v; char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */ + struct ast_str *out = NULL; for (v = params; v; v = v->next) { if (!strcasecmp(v->name, "mansession_id")) { @@ -2708,23 +2714,27 @@ static char *generic_http_callback(enum output_format format, } ast_mutex_unlock(&s->__lock); - memset(&m, 0, sizeof(m)); - { - char tmp[80]; - char cookie[128]; - ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]); - ast_build_string(&c, &len, "Cache-Control: no-cache;\r\n"); - sprintf(tmp, "%08lx", s->managerid); - ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie))); + out = ast_str_create(1024); + if (out == NULL) { + *status = 500; + goto generic_callback_out; } + memset(&m, 0, sizeof(m)); + ast_str_append(&out, 0, + "Content-type: text/%s\r\n" + "Cache-Control: no-cache;\r\n" + "Set-Cookie: mansession_id=\"%08lx\"; Version=\"1\"; Max-Age=%d\r\n" + "\r\n", + contenttype[format], + s->managerid, httptimeout); if (format == FORMAT_HTML) - ast_build_string(&c, &len, "Asterisk™ Manager Test Interface"); + ast_str_append(&out, 0, "Asterisk™ Manager Test Interface"); vars2msg(&m, params); if (format == FORMAT_XML) { - ast_build_string(&c, &len, "\n"); + ast_str_append(&out, 0, "\n"); } else if (format == FORMAT_HTML) { #define ROW_FMT "%s\r\n" @@ -2733,9 +2743,9 @@ static char *generic_http_callback(enum output_format format, user pass
\ " - ast_build_string(&c, &len, "\r\n"); - ast_build_string(&c, &len, ROW_FMT, "

Manager Tester

"); - ast_build_string(&c, &len, ROW_FMT, TEST_STRING); + ast_str_append(&out, 0, "
\r\n"); + ast_str_append(&out, 0, ROW_FMT, "

Manager Tester

"); + ast_str_append(&out, 0, ROW_FMT, TEST_STRING); } s->fd = mkstemp(template); /* create a temporary file for command output */ @@ -2763,26 +2773,12 @@ static char *generic_http_callback(enum output_format format, /* always return something even if len == 0 */ if ((buf = ast_calloc(1, l+1))) { - char *tmp; if (l > 0) { fseek(s->f, 0, SEEK_SET); fread(buf, 1, l, s->f); } if (format == FORMAT_XML || format == FORMAT_HTML) - tmp = xml_translate(buf, params, format); - else - tmp = buf; - if (tmp) { - retval = malloc(strlen(workspace) + strlen(tmp) + 128); - if (retval) { - strcpy(retval, workspace); - strcpy(retval + strlen(retval), tmp); - c = retval + strlen(retval); - len = 120; - } - } - if (tmp != buf) - free(tmp); + xml_translate(&out, buf, params, format); free(buf); } fclose(s->f); @@ -2794,9 +2790,9 @@ static char *generic_http_callback(enum output_format format, /* Still okay because c would safely be pointing to workspace even if retval failed to allocate above */ if (format == FORMAT_XML) { - ast_build_string(&c, &len, "\n"); + ast_str_append(&out, 0, "\n"); } else if (format == FORMAT_HTML) - ast_build_string(&c, &len, "
\r\n"); + ast_str_append(&out, 0, "\r\n"); ast_mutex_lock(&s->__lock); /* Reset HTTP timeout. If we're not authenticated, keep it extremely short */ @@ -2825,20 +2821,20 @@ static char *generic_http_callback(enum output_format format, generic_callback_out: if (*status != 200) return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); - return retval; + return out; } -static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength) +static struct ast_str *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength) { return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength); } -static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength) +static struct ast_str *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength) { return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength); } -static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength) +static struct ast_str *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength) { return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength); } -- cgit v1.2.3