fix indentation of a large block after changes in previous commit
[asterisk/asterisk.git] / main / http.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file 
21  * \brief http server for AMI access
22  *
23  * \author Mark Spencer <markster@digium.com>
24  * This program implements a tiny http server supporting the "get" method
25  * only and was inspired by micro-httpd by Jef Poskanzer 
26  * 
27  * \ref AstHTTP - AMI over the http protocol
28  */
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include <sys/types.h>
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <time.h>
39 #include <string.h>
40 #include <netinet/in.h>
41 #include <sys/time.h>
42 #include <sys/socket.h>
43 #include <sys/stat.h>
44 #include <sys/signal.h>
45 #include <arpa/inet.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <pthread.h>
49
50 #include "asterisk/cli.h"
51 #include "asterisk/http.h"
52 #include "asterisk/utils.h"
53 #include "asterisk/strings.h"
54 #include "asterisk/options.h"
55 #include "asterisk/config.h"
56
57 #define MAX_PREFIX 80
58 #define DEFAULT_PREFIX "/asterisk"
59
60 struct ast_http_server_instance {
61         FILE *f;
62         int fd;
63         struct sockaddr_in requestor;
64         ast_http_callback callback;
65 };
66
67 static struct ast_http_uri *uris;
68
69 static int httpfd = -1;
70 static pthread_t master = AST_PTHREADT_NULL;
71 static char prefix[MAX_PREFIX];
72 static int prefix_len = 0;
73 static struct sockaddr_in oldsin;
74 static int enablestatic=0;
75
76 /*! \brief Limit the kinds of files we're willing to serve up */
77 static struct {
78         char *ext;
79         char *mtype;
80 } mimetypes[] = {
81         { "png", "image/png" },
82         { "jpg", "image/jpeg" },
83         { "js", "application/x-javascript" },
84         { "wav", "audio/x-wav" },
85         { "mp3", "audio/mpeg" },
86 };
87
88 static char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
89 {
90         int x;
91         if (ftype) {
92                 for (x=0;x<sizeof(mimetypes) / sizeof(mimetypes[0]); x++) {
93                         if (!strcasecmp(ftype, mimetypes[x].ext))
94                                 return mimetypes[x].mtype;
95                 }
96         }
97         snprintf(wkspace, wkspacelen, "text/%s", ftype ? ftype : "plain");
98         return wkspace;
99 }
100
101 static char *static_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
102 {
103         char result[4096];
104         char *c=result;
105         char *path;
106         char *ftype, *mtype;
107         char wkspace[80];
108         struct stat st;
109         int len;
110         int fd;
111         void *blob;
112
113         /* Yuck.  I'm not really sold on this, but if you don't deliver static content it makes your configuration 
114            substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
115         if (!enablestatic || ast_strlen_zero(uri))
116                 goto out403;
117         /* Disallow any funny filenames at all */
118         if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0]))
119                 goto out403;
120         if (strstr(uri, "/.."))
121                 goto out403;
122                 
123         if ((ftype = strrchr(uri, '.')))
124                 ftype++;
125         mtype=ftype2mtype(ftype, wkspace, sizeof(wkspace));
126         
127         /* Cap maximum length */
128         len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5;
129         if (len > 1024)
130                 goto out403;
131                 
132         path = alloca(len);
133         sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
134         if (stat(path, &st))
135                 goto out404;
136         if (S_ISDIR(st.st_mode))
137                 goto out404;
138         fd = open(path, O_RDONLY);
139         if (fd < 0)
140                 goto out403;
141         
142         len = st.st_size + strlen(mtype) + 40;
143         
144         blob = malloc(len);
145         if (blob) {
146                 c = blob;
147                 sprintf(c, "Content-type: %s\r\n\r\n", mtype);
148                 c += strlen(c);
149                 *contentlength = read(fd, c, st.st_size);
150                 if (*contentlength < 0) {
151                         close(fd);
152                         free(blob);
153                         goto out403;
154                 }
155         }
156         close(fd);
157         return blob;
158
159 out404:
160         *status = 404;
161         *title = strdup("Not Found");
162         return ast_http_error(404, "Not Found", NULL, "Nothing to see here.  Move along.");
163
164 out403:
165         *status = 403;
166         *title = strdup("Access Denied");
167         return ast_http_error(403, "Access Denied", NULL, "Sorry, I cannot let you do that, Dave.");
168 }
169
170
171 static char *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
172 {
173         char result[4096];
174         size_t reslen = sizeof(result);
175         char *c=result;
176         struct ast_variable *v;
177
178         ast_build_string(&c, &reslen,
179                 "\r\n"
180                 "<title>Asterisk HTTP Status</title>\r\n"
181                 "<body bgcolor=\"#ffffff\">\r\n"
182                 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
183                 "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
184
185         ast_build_string(&c, &reslen, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
186         ast_build_string(&c, &reslen, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
187                         ast_inet_ntoa(oldsin.sin_addr));
188         ast_build_string(&c, &reslen, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
189                         ntohs(oldsin.sin_port));
190         ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
191         v = vars;
192         while(v) {
193                 if (strncasecmp(v->name, "cookie_", 7))
194                         ast_build_string(&c, &reslen, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
195                 v = v->next;
196         }
197         ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
198         v = vars;
199         while(v) {
200                 if (!strncasecmp(v->name, "cookie_", 7))
201                         ast_build_string(&c, &reslen, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
202                 v = v->next;
203         }
204         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");
205         return strdup(result);
206 }
207
208 static struct ast_http_uri statusuri = {
209         .callback = httpstatus_callback,
210         .description = "Asterisk HTTP General Status",
211         .uri = "httpstatus",
212         .has_subtree = 0,
213 };
214         
215 static struct ast_http_uri staticuri = {
216         .callback = static_callback,
217         .description = "Asterisk HTTP Static Delivery",
218         .uri = "static",
219         .has_subtree = 1,
220 };
221         
222 char *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
223 {
224         char *c = NULL;
225         asprintf(&c,
226                 "Content-type: text/html\r\n"
227                 "%s"
228                 "\r\n"
229                 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
230                 "<html><head>\r\n"
231                 "<title>%d %s</title>\r\n"
232                 "</head><body>\r\n"
233                 "<h1>%s</h1>\r\n"
234                 "<p>%s</p>\r\n"
235                 "<hr />\r\n"
236                 "<address>Asterisk Server</address>\r\n"
237                 "</body></html>\r\n",
238                         (extra_header ? extra_header : ""), status, title, title, text);
239         return c;
240 }
241
242 int ast_http_uri_link(struct ast_http_uri *urih)
243 {
244         struct ast_http_uri *prev=uris;
245         if (!uris || strlen(uris->uri) <= strlen(urih->uri)) {
246                 urih->next = uris;
247                 uris = urih;
248         } else {
249                 while (prev->next && (strlen(prev->next->uri) > strlen(urih->uri)))
250                         prev = prev->next;
251                 /* Insert it here */
252                 urih->next = prev->next;
253                 prev->next = urih;
254         }
255         return 0;
256 }       
257
258 void ast_http_uri_unlink(struct ast_http_uri *urih)
259 {
260         struct ast_http_uri *prev = uris;
261         if (!uris)
262                 return;
263         if (uris == urih) {
264                 uris = uris->next;
265         }
266         while(prev->next) {
267                 if (prev->next == urih) {
268                         prev->next = urih->next;
269                         break;
270                 }
271                 prev = prev->next;
272         }
273 }
274
275 static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength, struct ast_variable **cookies)
276 {
277         char *c;
278         char *turi;
279         char *params;
280         char *var;
281         char *val;
282         struct ast_http_uri *urih=NULL;
283         int len;
284         struct ast_variable *vars=NULL, *v, *prev = NULL;
285         
286         
287         params = strchr(uri, '?');
288         if (params) {
289                 *params++ = '\0';
290                 while ((var = strsep(&params, "&"))) {
291                         val = strchr(var, '=');
292                         if (val) {
293                                 *val++ = '\0';
294                                 ast_uri_decode(val);
295                         } else 
296                                 val = "";
297                         ast_uri_decode(var);
298                         if ((v = ast_variable_new(var, val))) {
299                                 if (vars)
300                                         prev->next = v;
301                                 else
302                                         vars = v;
303                                 prev = v;
304                         }
305                 }
306         }
307         if (prev)
308                 prev->next = *cookies;
309         else
310                 vars = *cookies;
311         *cookies = NULL;
312         ast_uri_decode(uri);
313         if (!strncasecmp(uri, prefix, prefix_len)) {
314                 uri += prefix_len;
315                 if (!*uri || (*uri == '/')) {
316                         if (*uri == '/')
317                                 uri++;
318                         urih = uris;
319                         while(urih) {
320                                 len = strlen(urih->uri);
321                                 if (!strncasecmp(urih->uri, uri, len)) {
322                                         if (!uri[len] || uri[len] == '/') {
323                                                 turi = uri + len;
324                                                 if (*turi == '/')
325                                                         turi++;
326                                                 if (!*turi || urih->has_subtree) {
327                                                         uri = turi;
328                                                         break;
329                                                 }
330                                         }
331                                 }
332                                 urih = urih->next;
333                         }
334                 }
335         }
336         if (urih) {
337                 c = urih->callback(sin, uri, vars, status, title, contentlength);
338                 ast_variables_destroy(vars);
339         } else if (ast_strlen_zero(uri) && ast_strlen_zero(prefix)) {
340                 /* Special case: If no prefix, and no URI, send to /static/index.html */
341                 c = ast_http_error(302, "Moved Temporarily", "Location: /static/index.html\r\n", "This is not the page you are looking for...");
342                 *status = 302;
343                 *title = strdup("Moved Temporarily");
344         } else {
345                 c = ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
346                 *status = 404;
347                 *title = strdup("Not Found");
348         }
349         return c;
350 }
351
352 static void *ast_httpd_helper_thread(void *data)
353 {
354         char buf[4096];
355         char cookie[4096];
356         char timebuf[256];
357         struct ast_http_server_instance *ser = data;
358         struct ast_variable *var, *prev=NULL, *vars=NULL;
359         char *uri, *c, *title=NULL;
360         char *vname, *vval;
361         int status = 200, contentlength = 0;
362         time_t t;
363
364         if (fgets(buf, sizeof(buf), ser->f)) {
365                 uri = ast_skip_nonblanks(buf);  /* Skip method */
366                 if (*uri)
367                         *uri++ = '\0';
368
369                 uri = ast_skip_blanks(uri);     /* Skip white space */
370
371                 if (*uri) {                     /* terminate at the first blank */
372                         c = ast_skip_nonblanks(uri);
373                         if (*c)
374                                 *c = '\0';
375                 }
376
377                 /* process "Cookie: " lines */
378                 while (fgets(cookie, sizeof(cookie), ser->f)) {
379                         /* Trim trailing characters */
380                         ast_trim_blanks(cookie);
381                         if (ast_strlen_zero(cookie))
382                                 break;
383                         if (strncasecmp(cookie, "Cookie: ", 8))
384                                 continue;
385
386                                 /* XXX fix indentation */
387
388                         /* TODO - The cookie parsing code below seems to work   
389                            in IE6 and FireFox 1.5.  However, it is not entirely 
390                            correct, and therefore may not work in all           
391                            circumstances.                                       
392                               For more details see RFC 2109 and RFC 2965        */
393                 
394                         /* FireFox cookie strings look like:                    
395                              Cookie: mansession_id="********"                   
396                            InternetExplorer's look like:                        
397                              Cookie: $Version="1"; mansession_id="********"     */
398                         
399                         /* If we got a FireFox cookie string, the name's right  
400                             after "Cookie: "                                    */
401                         vname = cookie + 8;
402                                 
403                         /* If we got an IE cookie string, we need to skip to    
404                             past the version to get to the name                 */
405                         if (*vname == '$') {
406                                 vname = strchr(vname, ';');
407                                 if (vname) { 
408                                         vname++;
409                                         if (*vname == ' ')
410                                                 vname++;
411                                 }
412                         }
413                                 
414                         if (vname) {
415                                 vval = strchr(vname, '=');
416                                 if (vval) {
417                                         /* Ditch the = and the quotes */
418                                         *vval++ = '\0';
419                                         if (*vval)
420                                                 vval++;
421                                         if (strlen(vval))
422                                                 vval[strlen(vval) - 1] = '\0';
423                                         var = ast_variable_new(vname, vval);
424                                         if (var) {
425                                                 if (prev)
426                                                         prev->next = var;
427                                                 else
428                                                         vars = var;
429                                                 prev = var;
430                                         }
431                                 }
432                         }
433                 }
434
435                 if (*uri) {
436                         if (!strcasecmp(buf, "get")) 
437                                 c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars);
438                         else 
439                                 c = ast_http_error(501, "Not Implemented", NULL, "Attempt to use unimplemented / unsupported method");\
440                 } else 
441                         c = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
442
443                 /* If they aren't mopped up already, clean up the cookies */
444                 if (vars)
445                         ast_variables_destroy(vars);
446
447                 if (!c)
448                         c = ast_http_error(500, "Internal Error", NULL, "Internal Server Error");
449                 if (c) {
450                         time(&t);
451                         strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
452                         ast_cli(ser->fd, "HTTP/1.1 %d %s\r\n", status, title ? title : "OK");
453                         ast_cli(ser->fd, "Server: Asterisk\r\n");
454                         ast_cli(ser->fd, "Date: %s\r\n", timebuf);
455                         ast_cli(ser->fd, "Connection: close\r\n");
456                         if (contentlength) {
457                                 char *tmp;
458                                 tmp = strstr(c, "\r\n\r\n");
459                                 if (tmp) {
460                                         ast_cli(ser->fd, "Content-length: %d\r\n", contentlength);
461                                         write(ser->fd, c, (tmp + 4 - c));
462                                         write(ser->fd, tmp + 4, contentlength);
463                                 }
464                         } else
465                                 ast_cli(ser->fd, "%s", c);
466                         free(c);
467                 }
468                 if (title)
469                         free(title);
470         }
471         fclose(ser->f);
472         free(ser);
473         return NULL;
474 }
475
476 static void *http_root(void *data)
477 {
478         int fd;
479         struct sockaddr_in sin;
480         socklen_t sinlen;
481         struct ast_http_server_instance *ser;
482         pthread_t launched;
483         pthread_attr_t attr;
484         
485         for (;;) {
486                 int flags;
487
488                 ast_wait_for_input(httpfd, -1);
489                 sinlen = sizeof(sin);
490                 fd = accept(httpfd, (struct sockaddr *)&sin, &sinlen);
491                 if (fd < 0) {
492                         if ((errno != EAGAIN) && (errno != EINTR))
493                                 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
494                         continue;
495                 }
496                 ser = ast_calloc(1, sizeof(*ser));
497                 if (!ser) {
498                         ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
499                         close(fd);
500                         continue;
501                 }
502                 flags = fcntl(fd, F_GETFL);
503                 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
504                 ser->fd = fd;
505                 memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
506                 if ((ser->f = fdopen(ser->fd, "w+"))) {
507                         pthread_attr_init(&attr);
508                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
509                         
510                         if (ast_pthread_create_background(&launched, &attr, ast_httpd_helper_thread, ser)) {
511                                 ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
512                                 fclose(ser->f);
513                                 free(ser);
514                         }
515                 } else {
516                         ast_log(LOG_WARNING, "fdopen failed!\n");
517                         close(ser->fd);
518                         free(ser);
519                 }
520         }
521         return NULL;
522 }
523
524 char *ast_http_setcookie(const char *var, const char *val, int expires, char *buf, size_t buflen)
525 {
526         char *c;
527         c = buf;
528         ast_build_string(&c, &buflen, "Set-Cookie: %s=\"%s\"; Version=\"1\"", var, val);
529         if (expires)
530                 ast_build_string(&c, &buflen, "; Max-Age=%d", expires);
531         ast_build_string(&c, &buflen, "\r\n");
532         return buf;
533 }
534
535
536 static void http_server_start(struct sockaddr_in *sin)
537 {
538         int flags;
539         int x = 1;
540         
541         /* Do nothing if nothing has changed */
542         if (!memcmp(&oldsin, sin, sizeof(oldsin))) {
543                 if (option_debug)
544                         ast_log(LOG_DEBUG, "Nothing changed in http\n");
545                 return;
546         }
547         
548         memcpy(&oldsin, sin, sizeof(oldsin));
549         
550         /* Shutdown a running server if there is one */
551         if (master != AST_PTHREADT_NULL) {
552                 pthread_cancel(master);
553                 pthread_kill(master, SIGURG);
554                 pthread_join(master, NULL);
555         }
556         
557         if (httpfd != -1)
558                 close(httpfd);
559
560         /* If there's no new server, stop here */
561         if (!sin->sin_family)
562                 return;
563         
564         
565         httpfd = socket(AF_INET, SOCK_STREAM, 0);
566         if (httpfd < 0) {
567                 ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
568                 return;
569         }
570         
571         setsockopt(httpfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
572         if (bind(httpfd, (struct sockaddr *)sin, sizeof(*sin))) {
573                 ast_log(LOG_NOTICE, "Unable to bind http server to %s:%d: %s\n",
574                         ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
575                         strerror(errno));
576                 close(httpfd);
577                 httpfd = -1;
578                 return;
579         }
580         if (listen(httpfd, 10)) {
581                 ast_log(LOG_NOTICE, "Unable to listen!\n");
582                 close(httpfd);
583                 httpfd = -1;
584                 return;
585         }
586         flags = fcntl(httpfd, F_GETFL);
587         fcntl(httpfd, F_SETFL, flags | O_NONBLOCK);
588         if (ast_pthread_create_background(&master, NULL, http_root, NULL)) {
589                 ast_log(LOG_NOTICE, "Unable to launch http server on %s:%d: %s\n",
590                                 ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
591                                 strerror(errno));
592                 close(httpfd);
593                 httpfd = -1;
594         }
595 }
596
597 static int __ast_http_load(int reload)
598 {
599         struct ast_config *cfg;
600         struct ast_variable *v;
601         int enabled=0;
602         int newenablestatic=0;
603         struct sockaddr_in sin;
604         struct hostent *hp;
605         struct ast_hostent ahp;
606         char newprefix[MAX_PREFIX];
607
608         memset(&sin, 0, sizeof(sin));
609         sin.sin_port = 8088;
610         strcpy(newprefix, DEFAULT_PREFIX);
611         cfg = ast_config_load("http.conf");
612         if (cfg) {
613                 v = ast_variable_browse(cfg, "general");
614                 while(v) {
615                         if (!strcasecmp(v->name, "enabled"))
616                                 enabled = ast_true(v->value);
617                         else if (!strcasecmp(v->name, "enablestatic"))
618                                 newenablestatic = ast_true(v->value);
619                         else if (!strcasecmp(v->name, "bindport"))
620                                 sin.sin_port = ntohs(atoi(v->value));
621                         else if (!strcasecmp(v->name, "bindaddr")) {
622                                 if ((hp = ast_gethostbyname(v->value, &ahp))) {
623                                         memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
624                                 } else {
625                                         ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
626                                 }
627                         } else if (!strcasecmp(v->name, "prefix")) {
628                                 if (!ast_strlen_zero(v->value)) {
629                                         newprefix[0] = '/';
630                                         ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
631                                 } else {
632                                         newprefix[0] = '\0';
633                                 }
634                                         
635                         }
636                         v = v->next;
637                 }
638                 ast_config_destroy(cfg);
639         }
640         if (enabled)
641                 sin.sin_family = AF_INET;
642         if (strcmp(prefix, newprefix)) {
643                 ast_copy_string(prefix, newprefix, sizeof(prefix));
644                 prefix_len = strlen(prefix);
645         }
646         enablestatic = newenablestatic;
647         http_server_start(&sin);
648         return 0;
649 }
650
651 static int handle_show_http(int fd, int argc, char *argv[])
652 {
653         struct ast_http_uri *urih;
654         if (argc != 3)
655                 return RESULT_SHOWUSAGE;
656         ast_cli(fd, "HTTP Server Status:\n");
657         ast_cli(fd, "Prefix: %s\n", prefix);
658         if (oldsin.sin_family)
659                 ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n",
660                         ast_inet_ntoa(oldsin.sin_addr),
661                         ntohs(oldsin.sin_port));
662         else
663                 ast_cli(fd, "Server Disabled\n\n");
664         ast_cli(fd, "Enabled URI's:\n");
665         urih = uris;
666         while(urih){
667                 ast_cli(fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
668                 urih = urih->next;
669         }
670         if (!uris)
671                 ast_cli(fd, "None.\n");
672         return RESULT_SUCCESS;
673 }
674
675 int ast_http_reload(void)
676 {
677         return __ast_http_load(1);
678 }
679
680 static char show_http_help[] =
681 "Usage: http list status\n"
682 "       Lists status of internal HTTP engine\n";
683
684 static struct ast_cli_entry cli_http[] = {
685         { { "http", "list", "status", NULL },
686         handle_show_http, "Display HTTP server status",
687         show_http_help },
688 };
689
690 int ast_http_init(void)
691 {
692         ast_http_uri_link(&statusuri);
693         ast_http_uri_link(&staticuri);
694         ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
695         return __ast_http_load(0);
696 }