Remove deprecated CLI apps from the core
[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                 params++;
291                 while ((var = strsep(&params, "&"))) {
292                         val = strchr(var, '=');
293                         if (val) {
294                                 *val = '\0';
295                                 val++;
296                                 ast_uri_decode(val);
297                         } else 
298                                 val = "";
299                         ast_uri_decode(var);
300                         if ((v = ast_variable_new(var, val))) {
301                                 if (vars)
302                                         prev->next = v;
303                                 else
304                                         vars = v;
305                                 prev = v;
306                         }
307                 }
308         }
309         if (prev)
310                 prev->next = *cookies;
311         else
312                 vars = *cookies;
313         *cookies = NULL;
314         ast_uri_decode(uri);
315         if (!strncasecmp(uri, prefix, prefix_len)) {
316                 uri += prefix_len;
317                 if (!*uri || (*uri == '/')) {
318                         if (*uri == '/')
319                                 uri++;
320                         urih = uris;
321                         while(urih) {
322                                 len = strlen(urih->uri);
323                                 if (!strncasecmp(urih->uri, uri, len)) {
324                                         if (!uri[len] || uri[len] == '/') {
325                                                 turi = uri + len;
326                                                 if (*turi == '/')
327                                                         turi++;
328                                                 if (!*turi || urih->has_subtree) {
329                                                         uri = turi;
330                                                         break;
331                                                 }
332                                         }
333                                 }
334                                 urih = urih->next;
335                         }
336                 }
337         }
338         if (urih) {
339                 c = urih->callback(sin, uri, vars, status, title, contentlength);
340                 ast_variables_destroy(vars);
341         } else if (ast_strlen_zero(uri) && ast_strlen_zero(prefix)) {
342                 /* Special case: If no prefix, and no URI, send to /static/index.html */
343                 c = ast_http_error(302, "Moved Temporarily", "Location: /static/index.html\r\n", "This is not the page you are looking for...");
344                 *status = 302;
345                 *title = strdup("Moved Temporarily");
346         } else {
347                 c = ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this serer.");
348                 *status = 404;
349                 *title = strdup("Not Found");
350         }
351         return c;
352 }
353
354 static void *ast_httpd_helper_thread(void *data)
355 {
356         char buf[4096];
357         char cookie[4096];
358         char timebuf[256];
359         struct ast_http_server_instance *ser = data;
360         struct ast_variable *var, *prev=NULL, *vars=NULL;
361         char *uri, *c, *title=NULL;
362         char *vname, *vval;
363         int status = 200, contentlength = 0;
364         time_t t;
365
366         if (fgets(buf, sizeof(buf), ser->f)) {
367                 /* Skip method */
368                 uri = buf;
369                 while(*uri && (*uri > 32))
370                         uri++;
371                 if (*uri) {
372                         *uri = '\0';
373                         uri++;
374                 }
375
376                 /* Skip white space */
377                 while (*uri && (*uri < 33))
378                         uri++;
379
380                 if (*uri) {
381                         c = uri;
382                         while (*c && (*c > 32))
383                                  c++;
384                         if (*c) {
385                                 *c = '\0';
386                         }
387                 }
388
389                 while (fgets(cookie, sizeof(cookie), ser->f)) {
390                         /* Trim trailing characters */
391                         while(!ast_strlen_zero(cookie) && (cookie[strlen(cookie) - 1] < 33)) {
392                                 cookie[strlen(cookie) - 1] = '\0';
393                         }
394                         if (ast_strlen_zero(cookie))
395                                 break;
396                         if (!strncasecmp(cookie, "Cookie: ", 8)) {
397
398                                 /* TODO - The cookie parsing code below seems to work   
399                                    in IE6 and FireFox 1.5.  However, it is not entirely 
400                                    correct, and therefore may not work in all           
401                                    circumstances.                                       
402                                       For more details see RFC 2109 and RFC 2965        */
403                         
404                                 /* FireFox cookie strings look like:                    
405                                      Cookie: mansession_id="********"                   
406                                    InternetExplorer's look like:                        
407                                      Cookie: $Version="1"; mansession_id="********"     */
408                                 
409                                 /* If we got a FireFox cookie string, the name's right  
410                                     after "Cookie: "                                    */
411                                 vname = cookie + 8;
412                                 
413                                 /* If we got an IE cookie string, we need to skip to    
414                                     past the version to get to the name                 */
415                                 if (*vname == '$') {
416                                         vname = strchr(vname, ';');
417                                         if (vname) { 
418                                                 vname++;
419                                                 if (*vname == ' ')
420                                                         vname++;
421                                         }
422                                 }
423                                 
424                                 if (vname) {
425                                         vval = strchr(vname, '=');
426                                         if (vval) {
427                                                 /* Ditch the = and the quotes */
428                                                 *vval++ = '\0';
429                                                 if (*vval)
430                                                         vval++;
431                                                 if (strlen(vval))
432                                                         vval[strlen(vval) - 1] = '\0';
433                                                 var = ast_variable_new(vname, vval);
434                                                 if (var) {
435                                                         if (prev)
436                                                                 prev->next = var;
437                                                         else
438                                                                 vars = var;
439                                                         prev = var;
440                                                 }
441                                         }
442                                 }
443                         }
444                 }
445
446                 if (*uri) {
447                         if (!strcasecmp(buf, "get")) 
448                                 c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars);
449                         else 
450                                 c = ast_http_error(501, "Not Implemented", NULL, "Attempt to use unimplemented / unsupported method");\
451                 } else 
452                         c = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
453
454                 /* If they aren't mopped up already, clean up the cookies */
455                 if (vars)
456                         ast_variables_destroy(vars);
457
458                 if (!c)
459                         c = ast_http_error(500, "Internal Error", NULL, "Internal Server Error");
460                 if (c) {
461                         time(&t);
462                         strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
463                         ast_cli(ser->fd, "HTTP/1.1 %d %s\r\n", status, title ? title : "OK");
464                         ast_cli(ser->fd, "Server: Asterisk\r\n");
465                         ast_cli(ser->fd, "Date: %s\r\n", timebuf);
466                         ast_cli(ser->fd, "Connection: close\r\n");
467                         if (contentlength) {
468                                 char *tmp;
469                                 tmp = strstr(c, "\r\n\r\n");
470                                 if (tmp) {
471                                         ast_cli(ser->fd, "Content-length: %d\r\n", contentlength);
472                                         write(ser->fd, c, (tmp + 4 - c));
473                                         write(ser->fd, tmp + 4, contentlength);
474                                 }
475                         } else
476                                 ast_cli(ser->fd, "%s", c);
477                         free(c);
478                 }
479                 if (title)
480                         free(title);
481         }
482         fclose(ser->f);
483         free(ser);
484         return NULL;
485 }
486
487 static void *http_root(void *data)
488 {
489         int fd;
490         struct sockaddr_in sin;
491         socklen_t sinlen;
492         struct ast_http_server_instance *ser;
493         pthread_t launched;
494         pthread_attr_t attr;
495         
496         for (;;) {
497                 ast_wait_for_input(httpfd, -1);
498                 sinlen = sizeof(sin);
499                 fd = accept(httpfd, (struct sockaddr *)&sin, &sinlen);
500                 if (fd < 0) {
501                         if ((errno != EAGAIN) && (errno != EINTR))
502                                 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
503                         continue;
504                 }
505                 ser = ast_calloc(1, sizeof(*ser));
506                 if (ser) {
507                         ser->fd = fd;
508                         memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
509                         if ((ser->f = fdopen(ser->fd, "w+"))) {
510                                 pthread_attr_init(&attr);
511                                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
512                                 
513                                 if (ast_pthread_create(&launched, &attr, ast_httpd_helper_thread, ser)) {
514                                         ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
515                                         fclose(ser->f);
516                                         free(ser);
517                                 }
518                         } else {
519                                 ast_log(LOG_WARNING, "fdopen failed!\n");
520                                 close(ser->fd);
521                                 free(ser);
522                         }
523                 }
524         }
525         return NULL;
526 }
527
528 char *ast_http_setcookie(const char *var, const char *val, int expires, char *buf, size_t buflen)
529 {
530         char *c;
531         c = buf;
532         ast_build_string(&c, &buflen, "Set-Cookie: %s=\"%s\"; Version=\"1\"", var, val);
533         if (expires)
534                 ast_build_string(&c, &buflen, "; Max-Age=%d", expires);
535         ast_build_string(&c, &buflen, "\r\n");
536         return buf;
537 }
538
539
540 static void http_server_start(struct sockaddr_in *sin)
541 {
542         int flags;
543         int x = 1;
544         
545         /* Do nothing if nothing has changed */
546         if (!memcmp(&oldsin, sin, sizeof(oldsin))) {
547                 ast_log(LOG_DEBUG, "Nothing changed in http\n");
548                 return;
549         }
550         
551         memcpy(&oldsin, sin, sizeof(oldsin));
552         
553         /* Shutdown a running server if there is one */
554         if (master != AST_PTHREADT_NULL) {
555                 pthread_cancel(master);
556                 pthread_kill(master, SIGURG);
557                 pthread_join(master, NULL);
558         }
559         
560         if (httpfd != -1)
561                 close(httpfd);
562
563         /* If there's no new server, stop here */
564         if (!sin->sin_family)
565                 return;
566         
567         
568         httpfd = socket(AF_INET, SOCK_STREAM, 0);
569         if (httpfd < 0) {
570                 ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
571                 return;
572         }
573         
574         setsockopt(httpfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
575         if (bind(httpfd, (struct sockaddr *)sin, sizeof(*sin))) {
576                 ast_log(LOG_NOTICE, "Unable to bind http server to %s:%d: %s\n",
577                         ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
578                         strerror(errno));
579                 close(httpfd);
580                 httpfd = -1;
581                 return;
582         }
583         if (listen(httpfd, 10)) {
584                 ast_log(LOG_NOTICE, "Unable to listen!\n");
585                 close(httpfd);
586                 httpfd = -1;
587                 return;
588         }
589         flags = fcntl(httpfd, F_GETFL);
590         fcntl(httpfd, F_SETFL, flags | O_NONBLOCK);
591         if (ast_pthread_create(&master, NULL, http_root, NULL)) {
592                 ast_log(LOG_NOTICE, "Unable to launch http server on %s:%d: %s\n",
593                                 ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
594                                 strerror(errno));
595                 close(httpfd);
596                 httpfd = -1;
597         }
598 }
599
600 static int __ast_http_load(int reload)
601 {
602         struct ast_config *cfg;
603         struct ast_variable *v;
604         int enabled=0;
605         int newenablestatic=0;
606         struct sockaddr_in sin;
607         struct hostent *hp;
608         struct ast_hostent ahp;
609         char newprefix[MAX_PREFIX];
610
611         memset(&sin, 0, sizeof(sin));
612         sin.sin_port = 8088;
613         strcpy(newprefix, DEFAULT_PREFIX);
614         cfg = ast_config_load("http.conf");
615         if (cfg) {
616                 v = ast_variable_browse(cfg, "general");
617                 while(v) {
618                         if (!strcasecmp(v->name, "enabled"))
619                                 enabled = ast_true(v->value);
620                         else if (!strcasecmp(v->name, "enablestatic"))
621                                 newenablestatic = ast_true(v->value);
622                         else if (!strcasecmp(v->name, "bindport"))
623                                 sin.sin_port = ntohs(atoi(v->value));
624                         else if (!strcasecmp(v->name, "bindaddr")) {
625                                 if ((hp = ast_gethostbyname(v->value, &ahp))) {
626                                         memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
627                                 } else {
628                                         ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
629                                 }
630                         } else if (!strcasecmp(v->name, "prefix")) {
631                                 if (!ast_strlen_zero(v->value)) {
632                                         newprefix[0] = '/';
633                                         ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
634                                 } else {
635                                         newprefix[0] = '\0';
636                                 }
637                                         
638                         }
639                         v = v->next;
640                 }
641                 ast_config_destroy(cfg);
642         }
643         if (enabled)
644                 sin.sin_family = AF_INET;
645         if (strcmp(prefix, newprefix)) {
646                 ast_copy_string(prefix, newprefix, sizeof(prefix));
647                 prefix_len = strlen(prefix);
648         }
649         enablestatic = newenablestatic;
650         http_server_start(&sin);
651         return 0;
652 }
653
654 static int handle_show_http(int fd, int argc, char *argv[])
655 {
656         struct ast_http_uri *urih;
657         if (argc != 3)
658                 return RESULT_SHOWUSAGE;
659         ast_cli(fd, "HTTP Server Status:\n");
660         ast_cli(fd, "Prefix: %s\n", prefix);
661         if (oldsin.sin_family)
662                 ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n",
663                         ast_inet_ntoa(oldsin.sin_addr),
664                         ntohs(oldsin.sin_port));
665         else
666                 ast_cli(fd, "Server Disabled\n\n");
667         ast_cli(fd, "Enabled URI's:\n");
668         urih = uris;
669         while(urih){
670                 ast_cli(fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
671                 urih = urih->next;
672         }
673         if (!uris)
674                 ast_cli(fd, "None.\n");
675         return RESULT_SUCCESS;
676 }
677
678 int ast_http_reload(void)
679 {
680         return __ast_http_load(1);
681 }
682
683 static char show_http_help[] =
684 "Usage: http list status\n"
685 "       Lists status of internal HTTP engine\n";
686
687 static struct ast_cli_entry cli_http[] = {
688         { { "http", "list", "status", NULL },
689         handle_show_http, "Display HTTP server status",
690         show_http_help },
691 };
692
693 int ast_http_init(void)
694 {
695         ast_http_uri_link(&statusuri);
696         ast_http_uri_link(&staticuri);
697         ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
698         return __ast_http_load(0);
699 }