replace ast_build_string() with ast_str_*() functions.
authorLuigi Rizzo <rizzo@icir.org>
Sat, 16 Dec 2006 09:33:31 +0000 (09:33 +0000)
committerLuigi Rizzo <rizzo@icir.org>
Sat, 16 Dec 2006 09:33:31 +0000 (09:33 +0000)
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
include/asterisk/strings.h
main/http.c
main/manager.c

index bfd39c0..c3e2ba0 100644 (file)
@@ -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);
 
index 7bfd385..832a964 100644 (file)
@@ -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;                    \
index 96e0bd8..62add3e 100644 (file)
@@ -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"
                "<title>Asterisk HTTP Status</title>\r\n"
                "<body bgcolor=\"#ffffff\">\r\n"
                "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
                "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
 
-       ast_build_string(&c, &reslen, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
-       ast_build_string(&c, &reslen, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
+       ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
+       ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
                        ast_inet_ntoa(http_desc.oldsin.sin_addr));
-       ast_build_string(&c, &reslen, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
+       ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
                        ntohs(http_desc.oldsin.sin_port));
        if (http_tls_cfg.enabled)
-               ast_build_string(&c, &reslen, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
+               ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
                        ntohs(https_desc.oldsin.sin_port));
-       ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
-       v = vars;
-       while(v) {
+       ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+       for (v = vars; v; v = v->next) {
                if (strncasecmp(v->name, "cookie_", 7))
-                       ast_build_string(&c, &reslen, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
-               v = v->next;
+                       ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
        }
-       ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
-       v = vars;
-       while(v) {
+       ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+       for (v = vars; v; v = v->next) {
                if (!strncasecmp(v->name, "cookie_", 7))
-                       ast_build_string(&c, &reslen, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
-               v = v->next;
+                       ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
        }
-       ast_build_string(&c, &reslen, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
-       return strdup(result);
+       ast_str_append(&out, 0, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\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
                "<address>Asterisk Server</address>\r\n"
                "</body></html>\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
index 4247e22..22d587f 100644 (file)
@@ -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, "&lt;");
-                       (*dst) += 4;
-                       *maxlen -= 4;
+                       strcpy(dst, "&lt;");
+                       dst += 4;
+                       space -= 4;
                        break;
                case '>':
-                       strcpy(*dst, "&gt;");
-                       (*dst) += 4;
-                       *maxlen -= 4;
+                       strcpy(dst, "&gt;");
+                       dst += 4;
+                       space -= 4;
                        break;
                case '\"':
-                       strcpy(*dst, "&quot;");
-                       (*dst) += 6;
-                       *maxlen -= 6;
+                       strcpy(dst, "&quot;");
+                       dst += 6;
+                       space -= 6;
                        break;
                case '\'':
-                       strcpy(*dst, "&apos;");
-                       (*dst) += 6;
-                       *maxlen -= 6;
+                       strcpy(dst, "&apos;");
+                       dst += 6;
+                       space -= 6;
                        break;
                case '&':
-                       strcpy(*dst, "&amp;");
-                       (*dst) += 5;
-                       *maxlen -= 5;
+                       strcpy(dst, "&amp;");
+                       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 <HR>
  *
  */
-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 ? "'" : "</td></tr>\n");
+                               ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
                                in_data = 0;
                        }
-                       ast_build_string(&tmp, &len, xml ? " /></response>\n" :
+                       ast_str_append(out, 0, xml ? " /></response>\n" :
                                "<tr><td colspan=\"2\"><hr></td></tr>\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, "<response type='object' id='%s'><%s", dest, objtype);
+                               ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
                        else
-                               ast_build_string(&tmp, &len, "<body>\n");
+                               ast_str_append(out, 0, "<body>\n");
                        inobj = 1;
                }
                if (!in_data) { /* build appropriate line start */
-                       ast_build_string(&tmp, &len, xml ? " " : "<tr><td>");
-                       xml_copy_escape(&tmp, &len, var, xml ? 1 | 2 : 0);
-                       ast_build_string(&tmp, &len, xml ? "='" : "</td><td>");
+                       ast_str_append(out, 0, xml ? " " : "<tr><td>");
+                       xml_copy_escape(out, var, xml ? 1 | 2 : 0);
+                       ast_str_append(out, 0, xml ? "='" : "</td><td>");
                        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 ? "'" : "</td></tr>\n");
+                       ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
                else
-                       ast_build_string(&tmp, &len, xml ? "\n" : "<br>\n");
+                       ast_str_append(out, 0, xml ? "\n" : "<br>\n");
        }
        if (inobj)
-               ast_build_string(&tmp, &len, xml ? " /></response>\n" :
+               ast_str_append(out, 0, xml ? " /></response>\n" :
                        "<tr><td colspan=\"2\"><hr></td></tr>\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, "<title>Asterisk&trade; Manager Test Interface</title>");
+               ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Test Interface</title>");
        vars2msg(&m, params);
 
        if (format == FORMAT_XML) {
-               ast_build_string(&c, &len, "<ajax-response>\n");
+               ast_str_append(&out, 0, "<ajax-response>\n");
        } else if (format == FORMAT_HTML) {
 
 #define ROW_FMT        "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
@@ -2733,9 +2743,9 @@ static char *generic_http_callback(enum output_format format,
        user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
        <input type=\"submit\"></form>"
 
-               ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
-               ast_build_string(&c, &len, ROW_FMT, "<h1>Manager Tester</h1>");
-               ast_build_string(&c, &len, ROW_FMT, TEST_STRING);
+               ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
+               ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
+               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, "</ajax-response>\n");
+               ast_str_append(&out, 0, "</ajax-response>\n");
        } else if (format == FORMAT_HTML)
-               ast_build_string(&c, &len, "</table></body>\r\n");
+               ast_str_append(&out, 0, "</table></body>\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);
 }