document the support for running a server on TCP/TLS and
[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 /*!
61  * In order to have TLS/SSL support, we need the openssl libraries.
62  * Still we can decide whether or not to use them by commenting
63  * in or out the DO_SSL macro.
64  * TLS/SSL support is basically implemented by reading from a config file
65  * (currently http.conf) the names of the certificate and cipher to use,
66  * and then run ssl_setup() to create an appropriate SSL_CTX (ssl_ctx)
67  * If we support multiple domains, presumably we need to read multiple
68  * certificates.
69  * When we are requested to open a TLS socket, we run make_file_from_fd()
70  * on the socket, to do the necessary setup. At the moment the context's name
71  * is hardwired in the function, but we can certainly make it into an extra
72  * parameter to the function.
73  *
74  * We declare most of ssl support variables unconditionally,
75  * because their number is small and this simplifies the code.
76  *
77  * NOTE: the ssl-support variables (ssl_ctx, do_ssl, certfile, cipher)
78  * and their setup should be moved to a more central place, e.g. asterisk.conf
79  * and the source files that processes it. Similarly, ssl_setup() should
80  * be run earlier in the startup process so modules have it available.
81  */
82
83 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
84 #define DO_SSL  /* comment in/out if you want to support ssl */
85 #endif
86
87 #ifdef DO_SSL
88 #include <openssl/ssl.h>
89 #include <openssl/err.h>
90 static SSL_CTX* ssl_ctx;
91 #else
92 typedef struct {} SSL;  /* so we can define a pointer to it */
93 #endif /* DO_SSL */
94
95 /* SSL support */
96 #define AST_CERTFILE "asterisk.pem"
97 static int do_ssl;
98 static char *certfile;
99 static char *cipher;
100
101 /*!
102  * The following code implements a generic mechanism for starting
103  * services on a TCP or TLS socket.
104  * The service is configured in the struct server_args, and
105  * then started by calling server_start(desc) on the descriptor.
106  * server_start() first verifies if an instance of the service is active,
107  * and in case shuts it down. Then, if the service must be started, creates
108  * a socket and a thread in charge of doing the accept().
109  *
110  * The body of the thread is desc->accept_fn(desc), which the user can define
111  * freely. We supply a sample implementation, server_root(), structured as an
112  * infinite loop. At the beginning of each iteration it runs periodic_fn()
113  * if defined (e.g. to perform some cleanup etc.) then issues a poll()
114  * or equivalent with a timeout of 'poll_timeout' milliseconds, and if the
115  * following accept() is successful it creates a thread in charge of
116  * running the session, whose body is desc->worker_fn(). The argument of
117  * worker_fn() is a struct server_instance, which contains the address
118  * of the other party, a pointer to desc, the file descriptors (fd) on which
119  * we can do a select/poll (but NOT IO/, and a FILE * on which we can do I/O.
120  * We have both because we want to support plain and SSL sockets, and
121  * going through a FILE * lets us provide the encryption/decryption
122  * on the stream without using an auxiliary thread.
123  *
124  * NOTE: in order to let other parts of asterisk use these services,
125  * we need to do the following:
126  *    + move struct server_instance and struct server_args to
127  *      a common header file, together with prototypes for
128  *      server_start() and server_root().
129  *    +
130  */
131  
132 /*!
133  * describes a server instance
134  */
135 struct server_instance {
136         FILE *f;        /* fopen/funopen result */
137         int fd;         /* the socket returned by accept() */
138         SSL *ssl;       /* ssl state */
139         struct sockaddr_in requestor;
140         struct server_args *parent;
141 };
142
143 /*!
144  * arguments for the accepting thread
145  */
146 struct server_args {
147         struct sockaddr_in sin;
148         struct sockaddr_in oldsin;
149         int is_ssl;             /* is this an SSL accept ? */
150         int accept_fd;
151         int poll_timeout;
152         pthread_t master;
153         void *(*accept_fn)(void *);     /* the function in charge of doing the accept */
154         void (*periodic_fn)(void *);    /* something we may want to run before after select on the accept socket */
155         void *(*worker_fn)(void *);     /* the function in charge of doing the actual work */
156         const char *name;
157 };
158
159 static void *server_root(void *arg);
160 static void *httpd_helper_thread(void *arg);
161
162 /*!
163  * we have up to two accepting threads, one for http, one for https
164  */
165 static struct server_args http_desc = {
166         .accept_fd = -1,
167         .master = AST_PTHREADT_NULL,
168         .is_ssl = 0,
169         .name = "http server",
170         .accept_fn = server_root,
171         .worker_fn = httpd_helper_thread,
172 };
173
174 static struct server_args https_desc = {
175         .accept_fd = -1,
176         .master = AST_PTHREADT_NULL,
177         .is_ssl = 1,
178         .name = "https server",
179         .accept_fn = server_root,
180         .worker_fn = httpd_helper_thread,
181 };
182
183 static struct ast_http_uri *uris;       /*!< list of supported handlers */
184
185 /* all valid URIs must be prepended by the string in prefix. */
186 static char prefix[MAX_PREFIX];
187 static int enablestatic=0;
188
189 /*! \brief Limit the kinds of files we're willing to serve up */
190 static struct {
191         char *ext;
192         char *mtype;
193 } mimetypes[] = {
194         { "png", "image/png" },
195         { "jpg", "image/jpeg" },
196         { "js", "application/x-javascript" },
197         { "wav", "audio/x-wav" },
198         { "mp3", "audio/mpeg" },
199 };
200
201 static char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
202 {
203         int x;
204         if (ftype) {
205                 for (x=0;x<sizeof(mimetypes) / sizeof(mimetypes[0]); x++) {
206                         if (!strcasecmp(ftype, mimetypes[x].ext))
207                                 return mimetypes[x].mtype;
208                 }
209         }
210         snprintf(wkspace, wkspacelen, "text/%s", ftype ? ftype : "plain");
211         return wkspace;
212 }
213
214 /* like ast_uri_decode, but replace '+' with ' ' */
215 static char *uri_decode(char *buf)
216 {
217         char *c;
218         ast_uri_decode(buf);
219         for (c = buf; *c; c++) {
220                 if (*c == '+')
221                         *c = ' ';
222         }
223         return buf;
224 }
225 static char *static_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
226 {
227         char result[4096];
228         char *c=result;
229         char *path;
230         char *ftype, *mtype;
231         char wkspace[80];
232         struct stat st;
233         int len;
234         int fd;
235         void *blob;
236
237         /* Yuck.  I'm not really sold on this, but if you don't deliver static content it makes your configuration 
238            substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
239         if (!enablestatic || ast_strlen_zero(uri))
240                 goto out403;
241         /* Disallow any funny filenames at all */
242         if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0]))
243                 goto out403;
244         if (strstr(uri, "/.."))
245                 goto out403;
246                 
247         if ((ftype = strrchr(uri, '.')))
248                 ftype++;
249         mtype=ftype2mtype(ftype, wkspace, sizeof(wkspace));
250         
251         /* Cap maximum length */
252         len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5;
253         if (len > 1024)
254                 goto out403;
255                 
256         path = alloca(len);
257         sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
258         if (stat(path, &st))
259                 goto out404;
260         if (S_ISDIR(st.st_mode))
261                 goto out404;
262         fd = open(path, O_RDONLY);
263         if (fd < 0)
264                 goto out403;
265         
266         len = st.st_size + strlen(mtype) + 40;
267         
268         blob = malloc(len);
269         if (blob) {
270                 c = blob;
271                 sprintf(c, "Content-type: %s\r\n\r\n", mtype);
272                 c += strlen(c);
273                 *contentlength = read(fd, c, st.st_size);
274                 if (*contentlength < 0) {
275                         close(fd);
276                         free(blob);
277                         goto out403;
278                 }
279         }
280         close(fd);
281         return blob;
282
283 out404:
284         *status = 404;
285         *title = strdup("Not Found");
286         return ast_http_error(404, "Not Found", NULL, "Nothing to see here.  Move along.");
287
288 out403:
289         *status = 403;
290         *title = strdup("Access Denied");
291         return ast_http_error(403, "Access Denied", NULL, "Sorry, I cannot let you do that, Dave.");
292 }
293
294
295 static char *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
296 {
297         char result[4096];
298         size_t reslen = sizeof(result);
299         char *c=result;
300         struct ast_variable *v;
301
302         ast_build_string(&c, &reslen,
303                 "\r\n"
304                 "<title>Asterisk HTTP Status</title>\r\n"
305                 "<body bgcolor=\"#ffffff\">\r\n"
306                 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
307                 "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
308
309         ast_build_string(&c, &reslen, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
310         ast_build_string(&c, &reslen, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
311                         ast_inet_ntoa(http_desc.oldsin.sin_addr));
312         ast_build_string(&c, &reslen, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
313                         ntohs(http_desc.oldsin.sin_port));
314         if (do_ssl)
315                 ast_build_string(&c, &reslen, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
316                         ntohs(https_desc.oldsin.sin_port));
317         ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
318         v = vars;
319         while(v) {
320                 if (strncasecmp(v->name, "cookie_", 7))
321                         ast_build_string(&c, &reslen, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
322                 v = v->next;
323         }
324         ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
325         v = vars;
326         while(v) {
327                 if (!strncasecmp(v->name, "cookie_", 7))
328                         ast_build_string(&c, &reslen, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
329                 v = v->next;
330         }
331         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");
332         return strdup(result);
333 }
334
335 static struct ast_http_uri statusuri = {
336         .callback = httpstatus_callback,
337         .description = "Asterisk HTTP General Status",
338         .uri = "httpstatus",
339         .has_subtree = 0,
340 };
341         
342 static struct ast_http_uri staticuri = {
343         .callback = static_callback,
344         .description = "Asterisk HTTP Static Delivery",
345         .uri = "static",
346         .has_subtree = 1,
347 };
348         
349 char *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
350 {
351         char *c = NULL;
352         asprintf(&c,
353                 "Content-type: text/html\r\n"
354                 "%s"
355                 "\r\n"
356                 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
357                 "<html><head>\r\n"
358                 "<title>%d %s</title>\r\n"
359                 "</head><body>\r\n"
360                 "<h1>%s</h1>\r\n"
361                 "<p>%s</p>\r\n"
362                 "<hr />\r\n"
363                 "<address>Asterisk Server</address>\r\n"
364                 "</body></html>\r\n",
365                         (extra_header ? extra_header : ""), status, title, title, text);
366         return c;
367 }
368
369 /*! \brief 
370  * Link the new uri into the list. They are sorted by length of
371  * the string, not alphabetically. Duplicate entries are not replaced,
372  * but the insertion order (using <= and not just <) makes sure that
373  * more recent insertions hide older ones.
374  * On a lookup, we just scan the list and stop at the first matching entry.
375  */
376 int ast_http_uri_link(struct ast_http_uri *urih)
377 {
378         struct ast_http_uri *prev=uris;
379         int len = strlen(urih->uri);
380
381         if (!uris || strlen(uris->uri) <= len ) {
382                 urih->next = uris;
383                 uris = urih;
384         } else {
385                 while (prev->next && strlen(prev->next->uri) > len)
386                         prev = prev->next;
387                 /* Insert it here */
388                 urih->next = prev->next;
389                 prev->next = urih;
390         }
391         return 0;
392 }       
393
394 void ast_http_uri_unlink(struct ast_http_uri *urih)
395 {
396         struct ast_http_uri *prev = uris;
397         if (!uris)
398                 return;
399         if (uris == urih) {
400                 uris = uris->next;
401         }
402         while(prev->next) {
403                 if (prev->next == urih) {
404                         prev->next = urih->next;
405                         break;
406                 }
407                 prev = prev->next;
408         }
409 }
410
411 static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength, struct ast_variable **cookies)
412 {
413         char *c;
414         char *params = uri;
415         struct ast_http_uri *urih=NULL;
416         int l;
417         struct ast_variable *vars=NULL, *v, *prev = NULL;
418
419         strsep(&params, "?");
420         /* Extract arguments from the request and store them in variables. */
421         if (params) {
422                 char *var, *val;
423
424                 while ((val = strsep(&params, "&"))) {
425                         var = strsep(&val, "=");
426                         if (val)
427                                 uri_decode(val);
428                         else 
429                                 val = "";
430                         ast_uri_decode(var);
431                         if ((v = ast_variable_new(var, val))) {
432                                 if (vars)
433                                         prev->next = v;
434                                 else
435                                         vars = v;
436                                 prev = v;
437                         }
438                 }
439         }
440         /*
441          * Append the cookies to the variables (the only reason to have them
442          * at the end is to avoid another pass of the cookies list to find
443          * the tail).
444          */
445         if (prev)
446                 prev->next = *cookies;
447         else
448                 vars = *cookies;
449         *cookies = NULL;
450         ast_uri_decode(uri);
451
452         /* We want requests to start with the prefix and '/' */
453         l = strlen(prefix);
454         if (l && !strncasecmp(uri, prefix, l) && uri[l] == '/') {
455                 uri += l + 1;
456                 /* scan registered uris to see if we match one. */
457                 for (urih = uris; urih; urih = urih->next) {
458                         l = strlen(urih->uri);
459                         c = uri + l;    /* candidate */
460                         if (strncasecmp(urih->uri, uri, l) /* no match */
461                             || (*c && *c != '/')) /* substring */
462                                 continue;
463                         if (*c == '/')
464                                 c++;
465                         if (!*c || urih->has_subtree) {
466                                 uri = c;
467                                 break;
468                         }
469                 }
470         }
471         if (urih) {
472                 c = urih->callback(sin, uri, vars, status, title, contentlength);
473         } else if (ast_strlen_zero(uri) && ast_strlen_zero(prefix)) {
474                 /* Special case: no prefix, no URI, send to /static/index.html */
475                 c = ast_http_error(302, "Moved Temporarily",
476                         "Location: /static/index.html\r\n",
477                         "This is not the page you are looking for...");
478                 *status = 302;
479                 *title = strdup("Moved Temporarily");
480         } else {
481                 c = ast_http_error(404, "Not Found", NULL,
482                         "The requested URL was not found on this server.");
483                 *status = 404;
484                 *title = strdup("Not Found");
485         }
486         ast_variables_destroy(vars);
487         return c;
488 }
489
490 #ifdef DO_SSL
491 #if defined(HAVE_FUNOPEN)
492 #define HOOK_T int
493 #define LEN_T int
494 #else
495 #define HOOK_T ssize_t
496 #define LEN_T size_t
497 #endif
498 /*!
499  * replacement read/write functions for SSL support.
500  * We use wrappers rather than SSL_read/SSL_write directly so
501  * we can put in some debugging.
502  */
503 static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
504 {
505         int i = SSL_read(cookie, buf, len-1);
506 #if 0
507         if (i >= 0)
508                 buf[i] = '\0';
509         ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
510 #endif
511         return i;
512 }
513
514 static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
515 {
516 #if 0
517         char *s = alloca(len+1);
518         strncpy(s, buf, len);
519         s[len] = '\0';
520         ast_verbose("ssl write size %d <%s>\n", (int)len, s);
521 #endif
522         return SSL_write(cookie, buf, len);
523 }
524
525 static int ssl_close(void *cookie)
526 {
527         close(SSL_get_fd(cookie));
528         SSL_shutdown(cookie);
529         SSL_free(cookie);
530         return 0;
531 }
532 #endif  /* DO_SSL */
533
534 /*!
535  * creates a FILE * from the fd passed by the accept thread.
536  * This operation is potentially expensive (certificate verification),
537  * so we do it in the child thread context.
538  */
539 static void *make_file_from_fd(void *data)
540 {
541         struct server_instance *ser = data;
542
543         /*
544          * open a FILE * as appropriate.
545          */
546         if (!ser->parent->is_ssl)
547                 ser->f = fdopen(ser->fd, "w+");
548 #ifdef DO_SSL
549         else if ( (ser->ssl = SSL_new(ssl_ctx)) ) {
550                 SSL_set_fd(ser->ssl, ser->fd);
551                 if (SSL_accept(ser->ssl) == 0)
552                         ast_verbose(" error setting up ssl connection");
553                 else {
554 #if defined(HAVE_FUNOPEN)       /* the BSD interface */
555                         ser->f = funopen(ser->ssl, ssl_read, ssl_write, NULL, ssl_close);
556
557 #elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */
558                         static const cookie_io_functions_t cookie_funcs = {
559                                 ssl_read, ssl_write, NULL, ssl_close
560                         };
561                         ser->f = fopencookie(ser->ssl, "w+", cookie_funcs);
562 #else
563                         /* could add other methods here */
564 #endif
565                 }
566                 if (!ser->f)    /* no success opening descriptor stacking */
567                         SSL_free(ser->ssl);
568         }
569 #endif /* DO_SSL */
570
571         if (!ser->f) {
572                 close(ser->fd);
573                 ast_log(LOG_WARNING, "FILE * open failed!\n");
574                 free(ser);
575                 return NULL;
576         }
577         return ser->parent->worker_fn(ser);
578 }
579
580 static void *httpd_helper_thread(void *data)
581 {
582         char buf[4096];
583         char cookie[4096];
584         struct server_instance *ser = data;
585         struct ast_variable *var, *prev=NULL, *vars=NULL;
586         char *uri, *c, *title=NULL;
587         int status = 200, contentlength = 0;
588
589         if (!fgets(buf, sizeof(buf), ser->f))
590                 goto done;
591
592         uri = ast_skip_nonblanks(buf);  /* Skip method */
593         if (*uri)
594                 *uri++ = '\0';
595
596         uri = ast_skip_blanks(uri);     /* Skip white space */
597
598         if (*uri) {                     /* terminate at the first blank */
599                 c = ast_skip_nonblanks(uri);
600                 if (*c)
601                         *c = '\0';
602         }
603
604         /* process "Cookie: " lines */
605         while (fgets(cookie, sizeof(cookie), ser->f)) {
606                 char *vname, *vval;
607                 int l;
608
609                 /* Trim trailing characters */
610                 ast_trim_blanks(cookie);
611                 if (ast_strlen_zero(cookie))
612                         break;
613                 if (strncasecmp(cookie, "Cookie: ", 8))
614                         continue;
615
616                 /* TODO - The cookie parsing code below seems to work   
617                    in IE6 and FireFox 1.5.  However, it is not entirely 
618                    correct, and therefore may not work in all           
619                    circumstances.                                       
620                       For more details see RFC 2109 and RFC 2965        */
621         
622                 /* FireFox cookie strings look like:                    
623                      Cookie: mansession_id="********"                   
624                    InternetExplorer's look like:                        
625                      Cookie: $Version="1"; mansession_id="********"     */
626                 
627                 /* If we got a FireFox cookie string, the name's right  
628                     after "Cookie: "                                    */
629                 vname = ast_skip_blanks(cookie + 8);
630                         
631                 /* If we got an IE cookie string, we need to skip to    
632                     past the version to get to the name                 */
633                 if (*vname == '$') {
634                         strsep(&vname, ";");
635                         if (!vname)     /* no name ? */
636                                 continue;
637                         vname = ast_skip_blanks(vname);
638                 }
639                 vval = strchr(vname, '=');
640                 if (!vval)
641                         continue;
642                 /* Ditch the = and the quotes */
643                 *vval++ = '\0';
644                 if (*vval)
645                         vval++;
646                 if ( (l = strlen(vval)) )
647                         vval[l - 1] = '\0';     /* trim trailing quote */
648                 var = ast_variable_new(vname, vval);
649                 if (var) {
650                         if (prev)
651                                 prev->next = var;
652                         else
653                                 vars = var;
654                         prev = var;
655                 }
656         }
657
658         if (!*uri)
659                 c = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
660         else if (strcasecmp(buf, "get")) 
661                 c = ast_http_error(501, "Not Implemented", NULL,
662                         "Attempt to use unimplemented / unsupported method");
663         else    /* try to serve it */
664                 c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars);
665
666         /* If they aren't mopped up already, clean up the cookies */
667         if (vars)
668                 ast_variables_destroy(vars);
669
670         if (!c)
671                 c = ast_http_error(500, "Internal Error", NULL, "Internal Server Error");
672         if (c) {
673                 time_t t = time(NULL);
674                 char timebuf[256];
675
676                 strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
677                 fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
678                                 "Server: Asterisk\r\n"
679                                 "Date: %s\r\n"
680                                 "Connection: close\r\n",
681                         status, title ? title : "OK", timebuf);
682                 if (!contentlength) {   /* opaque body ? just dump it hoping it is properly formatted */
683                         fprintf(ser->f, "%s", c);
684                 } else {
685                         char *tmp = strstr(c, "\r\n\r\n");
686
687                         if (tmp) {
688                                 fprintf(ser->f, "Content-length: %d\r\n", contentlength);
689                                 /* first write the header, then the body */
690                                 fwrite(c, 1, (tmp + 4 - c), ser->f);
691                                 fwrite(tmp + 4, 1, contentlength, ser->f);
692                         }
693                 }
694                 free(c);
695         }
696         if (title)
697                 free(title);
698
699 done:
700         if (ser->f)
701                 fclose(ser->f);
702         free(ser);
703         return NULL;
704 }
705
706 static void *server_root(void *data)
707 {
708         struct server_args *desc = data;
709         int fd;
710         struct sockaddr_in sin;
711         socklen_t sinlen;
712         struct server_instance *ser;
713         pthread_t launched;
714         pthread_attr_t attr;
715         
716         for (;;) {
717                 int i, flags;
718
719                 if (desc->periodic_fn)
720                         desc->periodic_fn(desc);
721                 i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
722                 if (i <= 0)
723                         continue;
724                 sinlen = sizeof(sin);
725                 fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen);
726                 if (fd < 0) {
727                         if ((errno != EAGAIN) && (errno != EINTR))
728                                 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
729                         continue;
730                 }
731                 ser = ast_calloc(1, sizeof(*ser));
732                 if (!ser) {
733                         ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
734                         close(fd);
735                         continue;
736                 }
737                 flags = fcntl(fd, F_GETFL);
738                 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
739                 ser->fd = fd;
740                 ser->parent = desc;
741                 memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
742
743                 pthread_attr_init(&attr);
744                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
745                         
746                 if (ast_pthread_create_background(&launched, &attr, make_file_from_fd, ser)) {
747                         ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
748                         close(ser->fd);
749                         free(ser);
750                 }
751         }
752         return NULL;
753 }
754
755 char *ast_http_setcookie(const char *var, const char *val, int expires, char *buf, size_t buflen)
756 {
757         char *c;
758         c = buf;
759         ast_build_string(&c, &buflen, "Set-Cookie: %s=\"%s\"; Version=\"1\"", var, val);
760         if (expires)
761                 ast_build_string(&c, &buflen, "; Max-Age=%d", expires);
762         ast_build_string(&c, &buflen, "\r\n");
763         return buf;
764 }
765
766 static int ssl_setup(void)
767 {
768 #ifndef DO_SSL
769         do_ssl = 0;
770         return 0;
771 #else
772         if (!do_ssl)
773                 return 0;
774         SSL_load_error_strings();
775         SSLeay_add_ssl_algorithms();
776         ssl_ctx = SSL_CTX_new( SSLv23_server_method() );
777         if (!ast_strlen_zero(certfile)) {
778                 if (SSL_CTX_use_certificate_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) == 0 ||
779                     SSL_CTX_use_PrivateKey_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) == 0 ||
780                     SSL_CTX_check_private_key(ssl_ctx) == 0 ) {
781                         ast_verbose("ssl cert error <%s>", certfile);
782                         sleep(2);
783                         do_ssl = 0;
784                         return 0;
785                 }
786         }
787         if (!ast_strlen_zero(cipher)) {
788                 if (SSL_CTX_set_cipher_list(ssl_ctx, cipher) == 0 ) {
789                         ast_verbose("ssl cipher error <%s>", cipher);
790                         sleep(2);
791                         do_ssl = 0;
792                         return 0;
793                 }
794         }
795         ast_verbose("ssl cert ok");
796         return 1;
797 #endif
798 }
799
800 /*!
801  * This is a generic (re)start routine for a TCP server,
802  * which does the socket/bind/listen and starts a thread for handling
803  * accept().
804  */
805 static void server_start(struct server_args *desc)
806 {
807         int flags;
808         int x = 1;
809         
810         /* Do nothing if nothing has changed */
811         if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
812                 if (option_debug)
813                         ast_log(LOG_DEBUG, "Nothing changed in %s\n", desc->name);
814                 return;
815         }
816         
817         desc->oldsin = desc->sin;
818         
819         /* Shutdown a running server if there is one */
820         if (desc->master != AST_PTHREADT_NULL) {
821                 pthread_cancel(desc->master);
822                 pthread_kill(desc->master, SIGURG);
823                 pthread_join(desc->master, NULL);
824         }
825         
826         if (desc->accept_fd != -1)
827                 close(desc->accept_fd);
828
829         /* If there's no new server, stop here */
830         if (desc->sin.sin_family == 0)
831                 return;
832
833         desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0);
834         if (desc->accept_fd < 0) {
835                 ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
836                         desc->name, strerror(errno));
837                 return;
838         }
839         
840         setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
841         if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
842                 ast_log(LOG_NOTICE, "Unable to bind %s to %s:%d: %s\n",
843                         desc->name,
844                         ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
845                         strerror(errno));
846                 goto error;
847         }
848         if (listen(desc->accept_fd, 10)) {
849                 ast_log(LOG_NOTICE, "Unable to listen for %s!\n", desc->name);
850                 goto error;
851         }
852         flags = fcntl(desc->accept_fd, F_GETFL);
853         fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
854         if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
855                 ast_log(LOG_NOTICE, "Unable to launch %s on %s:%d: %s\n",
856                         desc->name,
857                         ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
858                         strerror(errno));
859                 goto error;
860         }
861         return;
862
863 error:
864         close(desc->accept_fd);
865         desc->accept_fd = -1;
866 }
867
868 static int __ast_http_load(int reload)
869 {
870         struct ast_config *cfg;
871         struct ast_variable *v;
872         int enabled=0;
873         int newenablestatic=0;
874         struct hostent *hp;
875         struct ast_hostent ahp;
876         char newprefix[MAX_PREFIX];
877         int have_sslbindaddr = 0;
878
879         /* default values */
880         memset(&http_desc.sin, 0, sizeof(http_desc.sin));
881         http_desc.sin.sin_port = htons(8088);
882
883         memset(&https_desc.sin, 0, sizeof(https_desc.sin));
884         https_desc.sin.sin_port = htons(8089);
885         strcpy(newprefix, DEFAULT_PREFIX);
886         cfg = ast_config_load("http.conf");
887
888         do_ssl = 0;
889         if (certfile)
890                 free(certfile);
891         certfile = ast_strdup(AST_CERTFILE);
892         if (cipher)
893                 free(cipher);
894         cipher = ast_strdup("");
895
896         if (cfg) {
897                 v = ast_variable_browse(cfg, "general");
898                 while(v) {
899                         if (!strcasecmp(v->name, "enabled"))
900                                 enabled = ast_true(v->value);
901                         else if (!strcasecmp(v->name, "sslenable"))
902                                 do_ssl = ast_true(v->value);
903                         else if (!strcasecmp(v->name, "sslbindport"))
904                                 https_desc.sin.sin_port = htons(atoi(v->value));
905                         else if (!strcasecmp(v->name, "sslcert")) {
906                                 free(certfile);
907                                 certfile = ast_strdup(v->value);
908                         } else if (!strcasecmp(v->name, "sslcipher")) {
909                                 free(cipher);
910                                 cipher = ast_strdup(v->value);
911                         }
912                         else if (!strcasecmp(v->name, "enablestatic"))
913                                 newenablestatic = ast_true(v->value);
914                         else if (!strcasecmp(v->name, "bindport"))
915                                 http_desc.sin.sin_port = htons(atoi(v->value));
916                         else if (!strcasecmp(v->name, "sslbindaddr")) {
917                                 if ((hp = ast_gethostbyname(v->value, &ahp))) {
918                                         memcpy(&https_desc.sin.sin_addr, hp->h_addr, sizeof(https_desc.sin.sin_addr));
919                                         have_sslbindaddr = 1;
920                                 } else {
921                                         ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
922                                 }
923                         } else if (!strcasecmp(v->name, "bindaddr")) {
924                                 if ((hp = ast_gethostbyname(v->value, &ahp))) {
925                                         memcpy(&http_desc.sin.sin_addr, hp->h_addr, sizeof(http_desc.sin.sin_addr));
926                                 } else {
927                                         ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
928                                 }
929                         } else if (!strcasecmp(v->name, "prefix")) {
930                                 if (!ast_strlen_zero(v->value)) {
931                                         newprefix[0] = '/';
932                                         ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
933                                 } else {
934                                         newprefix[0] = '\0';
935                                 }
936                                         
937                         }
938                         v = v->next;
939                 }
940                 ast_config_destroy(cfg);
941         }
942         if (!have_sslbindaddr)
943                 https_desc.sin.sin_addr = http_desc.sin.sin_addr;
944         if (enabled)
945                 http_desc.sin.sin_family = https_desc.sin.sin_family = AF_INET;
946         if (strcmp(prefix, newprefix))
947                 ast_copy_string(prefix, newprefix, sizeof(prefix));
948         enablestatic = newenablestatic;
949         server_start(&http_desc);
950         if (ssl_setup())
951                 server_start(&https_desc);
952         return 0;
953 }
954
955 static int handle_show_http(int fd, int argc, char *argv[])
956 {
957         struct ast_http_uri *urih;
958         if (argc != 3)
959                 return RESULT_SHOWUSAGE;
960         ast_cli(fd, "HTTP Server Status:\n");
961         ast_cli(fd, "Prefix: %s\n", prefix);
962         if (!http_desc.oldsin.sin_family)
963                 ast_cli(fd, "Server Disabled\n\n");
964         else {
965                 ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n",
966                         ast_inet_ntoa(http_desc.oldsin.sin_addr),
967                         ntohs(http_desc.oldsin.sin_port));
968                 if (do_ssl)
969                         ast_cli(fd, "HTTPS Server Enabled and Bound to %s:%d\n\n",
970                                 ast_inet_ntoa(https_desc.oldsin.sin_addr),
971                                 ntohs(https_desc.oldsin.sin_port));
972         }
973         ast_cli(fd, "Enabled URI's:\n");
974         urih = uris;
975         while(urih){
976                 ast_cli(fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
977                 urih = urih->next;
978         }
979         if (!uris)
980                 ast_cli(fd, "None.\n");
981         return RESULT_SUCCESS;
982 }
983
984 int ast_http_reload(void)
985 {
986         return __ast_http_load(1);
987 }
988
989 static char show_http_help[] =
990 "Usage: http show status\n"
991 "       Lists status of internal HTTP engine\n";
992
993 static struct ast_cli_entry cli_http[] = {
994         { { "http", "show", "status", NULL },
995         handle_show_http, "Display HTTP server status",
996         show_http_help },
997 };
998
999 int ast_http_init(void)
1000 {
1001         ast_http_uri_link(&statusuri);
1002         ast_http_uri_link(&staticuri);
1003         ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
1004         return __ast_http_load(0);
1005 }