00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include "asterisk.h"
00032
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 183866 $")
00034
00035 #include "asterisk/paths.h"
00036 #include "asterisk/network.h"
00037 #include <time.h>
00038 #include <sys/time.h>
00039 #include <sys/stat.h>
00040 #include <sys/signal.h>
00041 #include <fcntl.h>
00042
00043 #ifdef ENABLE_UPLOADS
00044 #include <gmime/gmime.h>
00045 #endif
00046
00047 #include "asterisk/cli.h"
00048 #include "asterisk/tcptls.h"
00049 #include "asterisk/http.h"
00050 #include "asterisk/utils.h"
00051 #include "asterisk/strings.h"
00052 #include "asterisk/config.h"
00053 #include "asterisk/stringfields.h"
00054 #include "asterisk/ast_version.h"
00055 #include "asterisk/manager.h"
00056
00057 #define MAX_PREFIX 80
00058
00059
00060 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
00061 #define DO_SSL
00062 #endif
00063
00064 static struct ast_tls_config http_tls_cfg;
00065
00066 static void *httpd_helper_thread(void *arg);
00067
00068
00069
00070
00071 static struct server_args http_desc = {
00072 .accept_fd = -1,
00073 .master = AST_PTHREADT_NULL,
00074 .tls_cfg = NULL,
00075 .poll_timeout = -1,
00076 .name = "http server",
00077 .accept_fn = ast_tcptls_server_root,
00078 .worker_fn = httpd_helper_thread,
00079 };
00080
00081 static struct server_args https_desc = {
00082 .accept_fd = -1,
00083 .master = AST_PTHREADT_NULL,
00084 .tls_cfg = &http_tls_cfg,
00085 .poll_timeout = -1,
00086 .name = "https server",
00087 .accept_fn = ast_tcptls_server_root,
00088 .worker_fn = httpd_helper_thread,
00089 };
00090
00091 static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri);
00092
00093 #ifdef ENABLE_UPLOADS
00094 struct ast_http_post_mapping {
00095 AST_RWLIST_ENTRY(ast_http_post_mapping) entry;
00096 char *from;
00097 char *to;
00098 };
00099
00100 static AST_RWLIST_HEAD_STATIC(post_mappings, ast_http_post_mapping);
00101
00102 struct mime_cbinfo {
00103 int count;
00104 const char *post_dir;
00105 };
00106 #endif
00107
00108
00109 static char prefix[MAX_PREFIX];
00110 static int enablestatic;
00111
00112
00113 static struct {
00114 const char *ext;
00115 const char *mtype;
00116 } mimetypes[] = {
00117 { "png", "image/png" },
00118 { "jpg", "image/jpeg" },
00119 { "js", "application/x-javascript" },
00120 { "wav", "audio/x-wav" },
00121 { "mp3", "audio/mpeg" },
00122 { "svg", "image/svg+xml" },
00123 { "svgz", "image/svg+xml" },
00124 { "gif", "image/gif" },
00125 };
00126
00127 struct http_uri_redirect {
00128 AST_LIST_ENTRY(http_uri_redirect) entry;
00129 char *dest;
00130 char target[0];
00131 };
00132
00133 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
00134
00135 static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
00136 {
00137 int x;
00138 if (ftype) {
00139 for (x=0;x<sizeof(mimetypes) / sizeof(mimetypes[0]); x++) {
00140 if (!strcasecmp(ftype, mimetypes[x].ext))
00141 return mimetypes[x].mtype;
00142 }
00143 }
00144 snprintf(wkspace, wkspacelen, "text/%s", ftype ? ftype : "plain");
00145 return wkspace;
00146 }
00147
00148 static uint32_t manid_from_vars(struct ast_variable *sid) {
00149 uint32_t mngid;
00150
00151 while (sid && strcmp(sid->name, "mansession_id"))
00152 sid = sid->next;
00153
00154 if (!sid || sscanf(sid->value, "%x", &mngid) != 1)
00155 return 0;
00156
00157 return mngid;
00158 }
00159
00160 static struct ast_str *static_callback(struct ast_tcptls_session_instance *ser, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
00161 {
00162 char *path;
00163 char *ftype;
00164 const char *mtype;
00165 char wkspace[80];
00166 struct stat st;
00167 int len;
00168 int fd;
00169 struct timeval tv = ast_tvnow();
00170 char buf[256];
00171 struct ast_tm tm;
00172
00173
00174
00175 if (!enablestatic || ast_strlen_zero(uri))
00176 goto out403;
00177
00178 if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0]))
00179 goto out403;
00180 if (strstr(uri, "/.."))
00181 goto out403;
00182
00183 if ((ftype = strrchr(uri, '.')))
00184 ftype++;
00185 mtype = ftype2mtype(ftype, wkspace, sizeof(wkspace));
00186
00187
00188 len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5;
00189 if (len > 1024)
00190 goto out403;
00191
00192 path = alloca(len);
00193 sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
00194 if (stat(path, &st))
00195 goto out404;
00196 if (S_ISDIR(st.st_mode))
00197 goto out404;
00198 fd = open(path, O_RDONLY);
00199 if (fd < 0)
00200 goto out403;
00201
00202 if (strstr(path, "/private/") && !astman_is_authed(manid_from_vars(vars))) {
00203 goto out403;
00204 }
00205
00206 ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&tv, &tm, "GMT"));
00207 fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
00208 "Server: Asterisk/%s\r\n"
00209 "Date: %s\r\n"
00210 "Connection: close\r\n"
00211 "Cache-Control: private\r\n"
00212 "Content-Length: %d\r\n"
00213 "Content-type: %s\r\n\r\n",
00214 ast_get_version(), buf, (int) st.st_size, mtype);
00215
00216 while ((len = read(fd, buf, sizeof(buf))) > 0)
00217 if (fwrite(buf, 1, len, ser->f) != len) {
00218 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00219 }
00220
00221 close(fd);
00222 return NULL;
00223
00224 out404:
00225 return ast_http_error((*status = 404),
00226 (*title = ast_strdup("Not Found")),
00227 NULL, "The requested URL was not found on this server.");
00228
00229 out403:
00230 return ast_http_error((*status = 403),
00231 (*title = ast_strdup("Access Denied")),
00232 NULL, "You do not have permission to access the requested URL.");
00233 }
00234
00235
00236 static struct ast_str *httpstatus_callback(struct ast_tcptls_session_instance *ser, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
00237 {
00238 struct ast_str *out = ast_str_create(512);
00239 struct ast_variable *v;
00240
00241 if (out == NULL)
00242 return out;
00243
00244 ast_str_append(&out, 0,
00245 "\r\n"
00246 "<title>Asterisk HTTP Status</title>\r\n"
00247 "<body bgcolor=\"#ffffff\">\r\n"
00248 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
00249 "<h2> Asterisk™ HTTP Status</h2></td></tr>\r\n");
00250
00251 ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
00252 ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
00253 ast_inet_ntoa(http_desc.oldsin.sin_addr));
00254 ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
00255 ntohs(http_desc.oldsin.sin_port));
00256 if (http_tls_cfg.enabled)
00257 ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
00258 ntohs(https_desc.oldsin.sin_port));
00259 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00260 for (v = vars; v; v = v->next) {
00261 if (strncasecmp(v->name, "cookie_", 7))
00262 ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
00263 }
00264 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00265 for (v = vars; v; v = v->next) {
00266 if (!strncasecmp(v->name, "cookie_", 7))
00267 ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
00268 }
00269 ast_str_append(&out, 0, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
00270 return out;
00271 }
00272
00273 static struct ast_http_uri statusuri = {
00274 .callback = httpstatus_callback,
00275 .description = "Asterisk HTTP General Status",
00276 .uri = "httpstatus",
00277 .has_subtree = 0,
00278 };
00279
00280 static struct ast_http_uri staticuri = {
00281 .callback = static_callback,
00282 .description = "Asterisk HTTP Static Delivery",
00283 .uri = "static",
00284 .has_subtree = 1,
00285 .static_content = 1,
00286 };
00287
00288 struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
00289 {
00290 struct ast_str *out = ast_str_create(512);
00291 if (out == NULL)
00292 return out;
00293 ast_str_set(&out, 0,
00294 "Content-type: text/html\r\n"
00295 "%s"
00296 "\r\n"
00297 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
00298 "<html><head>\r\n"
00299 "<title>%d %s</title>\r\n"
00300 "</head><body>\r\n"
00301 "<h1>%s</h1>\r\n"
00302 "<p>%s</p>\r\n"
00303 "<hr />\r\n"
00304 "<address>Asterisk Server</address>\r\n"
00305 "</body></html>\r\n",
00306 (extra_header ? extra_header : ""), status, title, title, text);
00307 return out;
00308 }
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319 int ast_http_uri_link(struct ast_http_uri *urih)
00320 {
00321 struct ast_http_uri *uri;
00322 int len = strlen(urih->uri);
00323
00324 AST_RWLIST_WRLOCK(&uris);
00325
00326 if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
00327 AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
00328 AST_RWLIST_UNLOCK(&uris);
00329 return 0;
00330 }
00331
00332 AST_RWLIST_TRAVERSE(&uris, uri, entry) {
00333 if ( AST_RWLIST_NEXT(uri, entry)
00334 && strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len ) {
00335 AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
00336 AST_RWLIST_UNLOCK(&uris);
00337 return 0;
00338 }
00339 }
00340
00341 AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
00342
00343 AST_RWLIST_UNLOCK(&uris);
00344
00345 return 0;
00346 }
00347
00348 void ast_http_uri_unlink(struct ast_http_uri *urih)
00349 {
00350 AST_RWLIST_WRLOCK(&uris);
00351 AST_RWLIST_REMOVE(&uris, urih, entry);
00352 AST_RWLIST_UNLOCK(&uris);
00353 }
00354
00355 #ifdef ENABLE_UPLOADS
00356
00357 static struct ast_http_post_mapping *find_post_mapping(const char *uri)
00358 {
00359 struct ast_http_post_mapping *post_map;
00360
00361 if (!ast_strlen_zero(prefix) && strncmp(prefix, uri, strlen(prefix))) {
00362 ast_debug(1, "URI %s does not have prefix %s\n", uri, prefix);
00363 return NULL;
00364 }
00365
00366 uri += strlen(prefix);
00367 if (*uri == '/')
00368 uri++;
00369
00370 AST_RWLIST_TRAVERSE(&post_mappings, post_map, entry) {
00371 if (!strcmp(uri, post_map->from))
00372 return post_map;
00373 }
00374
00375 return NULL;
00376 }
00377
00378 static void post_raw(GMimePart *part, const char *post_dir, const char *fn)
00379 {
00380 char filename[PATH_MAX];
00381 GMimeDataWrapper *content;
00382 GMimeStream *stream;
00383 int fd;
00384
00385 snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn);
00386
00387 ast_debug(1, "Posting raw data to %s\n", filename);
00388
00389 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666)) == -1) {
00390 ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename);
00391 return;
00392 }
00393
00394 stream = g_mime_stream_fs_new(fd);
00395
00396 content = g_mime_part_get_content_object(part);
00397 g_mime_data_wrapper_write_to_stream(content, stream);
00398 g_mime_stream_flush(stream);
00399
00400 g_object_unref(content);
00401 g_object_unref(stream);
00402 }
00403
00404 static GMimeMessage *parse_message(FILE *f)
00405 {
00406 GMimeMessage *message;
00407 GMimeParser *parser;
00408 GMimeStream *stream;
00409
00410 stream = g_mime_stream_file_new(f);
00411
00412 parser = g_mime_parser_new_with_stream(stream);
00413 g_mime_parser_set_respect_content_length(parser, 1);
00414
00415 g_object_unref(stream);
00416
00417 message = g_mime_parser_construct_message(parser);
00418
00419 g_object_unref(parser);
00420
00421 return message;
00422 }
00423
00424 static void process_message_callback(GMimeObject *part, gpointer user_data)
00425 {
00426 struct mime_cbinfo *cbinfo = user_data;
00427
00428 cbinfo->count++;
00429
00430
00431 if (GMIME_IS_MESSAGE_PART(part)) {
00432 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PART\n");
00433 return;
00434 } else if (GMIME_IS_MESSAGE_PARTIAL(part)) {
00435 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PARTIAL\n");
00436 return;
00437 } else if (GMIME_IS_MULTIPART(part)) {
00438 GList *l;
00439
00440 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MULTIPART, trying to process subparts\n");
00441 l = GMIME_MULTIPART (part)->subparts;
00442 while (l) {
00443 process_message_callback(l->data, cbinfo);
00444 l = l->next;
00445 }
00446 } else if (GMIME_IS_PART(part)) {
00447 const char *filename;
00448
00449 ast_debug(3, "Got mime part\n");
00450 if (ast_strlen_zero(filename = g_mime_part_get_filename(GMIME_PART(part)))) {
00451 ast_debug(1, "Skipping part with no filename\n");
00452 return;
00453 }
00454
00455 post_raw(GMIME_PART(part), cbinfo->post_dir, filename);
00456 } else {
00457 ast_log(LOG_ERROR, "Encountered unknown MIME part. This should never happen!\n");
00458 }
00459 }
00460
00461 static int process_message(GMimeMessage *message, const char *post_dir)
00462 {
00463 struct mime_cbinfo cbinfo = {
00464 .count = 0,
00465 .post_dir = post_dir,
00466 };
00467
00468 g_mime_message_foreach_part(message, process_message_callback, &cbinfo);
00469
00470 return cbinfo.count;
00471 }
00472
00473 static struct ast_str *handle_post(struct ast_tcptls_session_instance *ser, char *uri,
00474 int *status, char **title, int *contentlength, struct ast_variable *headers,
00475 struct ast_variable *cookies)
00476 {
00477 char buf[4096];
00478 FILE *f;
00479 size_t res;
00480 struct ast_variable *var;
00481 int content_len = 0;
00482 struct ast_http_post_mapping *post_map;
00483 const char *post_dir;
00484 unsigned long ident = 0;
00485 GMimeMessage *message;
00486 int message_count = 0;
00487
00488 for (var = cookies; var; var = var->next) {
00489 if (strcasecmp(var->name, "mansession_id"))
00490 continue;
00491
00492 if (sscanf(var->value, "%lx", &ident) != 1) {
00493 *status = 400;
00494 *title = ast_strdup("Bad Request");
00495 return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
00496 }
00497
00498 if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
00499 *status = 401;
00500 *title = ast_strdup("Unauthorized");
00501 return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
00502 }
00503
00504 break;
00505 }
00506 if (!var) {
00507 *status = 401;
00508 *title = ast_strdup("Unauthorized");
00509 return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
00510 }
00511
00512 if (!(f = tmpfile()))
00513 return NULL;
00514
00515 for (var = headers; var; var = var->next) {
00516 if (!strcasecmp(var->name, "Content-Length")) {
00517 if ((sscanf(var->value, "%u", &content_len)) != 1) {
00518 ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
00519 fclose(f);
00520 return NULL;
00521 }
00522 ast_debug(1, "Got a Content-Length of %d\n", content_len);
00523 } else if (!strcasecmp(var->name, "Content-Type"))
00524 fprintf(f, "Content-Type: %s\r\n\r\n", var->value);
00525 }
00526
00527 for(res = sizeof(buf);content_len;content_len -= res) {
00528 if (content_len < res)
00529 res = content_len;
00530 if (fread(buf, 1, res, ser->f) != res) {
00531 ast_log(LOG_WARNING, "fread() failed: %s\n", strerror(errno));
00532 }
00533 if (fwrite(buf, 1, res, f) != res) {
00534 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00535 }
00536 }
00537
00538 if (fseek(f, SEEK_SET, 0)) {
00539 ast_debug(1, "Failed to seek temp file back to beginning.\n");
00540 fclose(f);
00541 return NULL;
00542 }
00543
00544 AST_RWLIST_RDLOCK(&post_mappings);
00545 if (!(post_map = find_post_mapping(uri))) {
00546 ast_debug(1, "%s is not a valid URI for POST\n", uri);
00547 AST_RWLIST_UNLOCK(&post_mappings);
00548 *status = 404;
00549 *title = ast_strdup("Not Found");
00550 return ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
00551 }
00552 post_dir = ast_strdupa(post_map->to);
00553 post_map = NULL;
00554 AST_RWLIST_UNLOCK(&post_mappings);
00555
00556 ast_debug(1, "Going to post files to dir %s\n", post_dir);
00557
00558 message = parse_message(f);
00559
00560 if (!message) {
00561 ast_log(LOG_ERROR, "Error parsing MIME data\n");
00562 *status = 400;
00563 *title = ast_strdup("Bad Request");
00564 return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
00565 }
00566
00567 if (!(message_count = process_message(message, post_dir))) {
00568 ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
00569 *status = 400;
00570 *title = ast_strdup("Bad Request");
00571 return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
00572 }
00573
00574 *status = 200;
00575 *title = ast_strdup("OK");
00576 return ast_http_error(200, "OK", NULL, "File successfully uploaded.");
00577 }
00578 #endif
00579
00580
00581
00582
00583
00584
00585 static void http_decode(char *s)
00586 {
00587 char *t;
00588
00589 for (t = s; *t; t++) {
00590 if (*t == '+')
00591 *t = ' ';
00592 }
00593
00594 ast_uri_decode(s);
00595 }
00596
00597 static struct ast_str *handle_uri(struct ast_tcptls_session_instance *ser, char *uri, int *status,
00598 char **title, int *contentlength, struct ast_variable **cookies,
00599 unsigned int *static_content)
00600 {
00601 char *c;
00602 struct ast_str *out = NULL;
00603 char *params = uri;
00604 struct ast_http_uri *urih=NULL;
00605 int l;
00606 struct ast_variable *vars=NULL, *v, *prev = NULL;
00607 struct http_uri_redirect *redirect;
00608
00609 strsep(¶ms, "?");
00610
00611 if (params) {
00612 char *var, *val;
00613
00614 while ((val = strsep(¶ms, "&"))) {
00615 var = strsep(&val, "=");
00616 if (val)
00617 http_decode(val);
00618 else
00619 val = "";
00620 http_decode(var);
00621 if ((v = ast_variable_new(var, val, ""))) {
00622 if (vars)
00623 prev->next = v;
00624 else
00625 vars = v;
00626 prev = v;
00627 }
00628 }
00629 }
00630
00631
00632
00633
00634
00635 if (prev)
00636 prev->next = *cookies;
00637 else
00638 vars = *cookies;
00639 *cookies = NULL;
00640 http_decode(uri);
00641
00642 AST_RWLIST_RDLOCK(&uri_redirects);
00643 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
00644 if (!strcasecmp(uri, redirect->target)) {
00645 char buf[512];
00646 snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
00647 out = ast_http_error((*status = 302),
00648 (*title = ast_strdup("Moved Temporarily")),
00649 buf, "Redirecting...");
00650
00651 break;
00652 }
00653 }
00654 AST_RWLIST_UNLOCK(&uri_redirects);
00655 if (redirect)
00656 goto cleanup;
00657
00658
00659 l = strlen(prefix);
00660 if (!strncasecmp(uri, prefix, l) && uri[l] == '/') {
00661 uri += l + 1;
00662
00663 AST_RWLIST_RDLOCK(&uris);
00664 AST_RWLIST_TRAVERSE(&uris, urih, entry) {
00665 l = strlen(urih->uri);
00666 c = uri + l;
00667 if (strncasecmp(urih->uri, uri, l)
00668 || (*c && *c != '/'))
00669 continue;
00670 if (*c == '/')
00671 c++;
00672 if (!*c || urih->has_subtree) {
00673 uri = c;
00674 break;
00675 }
00676 }
00677 if (!urih)
00678 AST_RWLIST_UNLOCK(&uris);
00679 }
00680 if (urih) {
00681 if (urih->static_content)
00682 *static_content = 1;
00683 out = urih->callback(ser, uri, vars, status, title, contentlength);
00684 AST_RWLIST_UNLOCK(&uris);
00685 } else {
00686 out = ast_http_error(404, "Not Found", NULL,
00687 "The requested URL was not found on this server.");
00688 *status = 404;
00689 *title = ast_strdup("Not Found");
00690 }
00691
00692 cleanup:
00693 ast_variables_destroy(vars);
00694 return out;
00695 }
00696
00697 #ifdef DO_SSL
00698 #if defined(HAVE_FUNOPEN)
00699 #define HOOK_T int
00700 #define LEN_T int
00701 #else
00702 #define HOOK_T ssize_t
00703 #define LEN_T size_t
00704 #endif
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739 #endif
00740
00741 static struct ast_variable *parse_cookies(char *cookies)
00742 {
00743 char *cur;
00744 struct ast_variable *vars = NULL, *var;
00745
00746
00747 cookies += 8;
00748
00749 while ((cur = strsep(&cookies, ";"))) {
00750 char *name, *val;
00751
00752 name = val = cur;
00753 strsep(&val, "=");
00754
00755 if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00756 continue;
00757 }
00758
00759 name = ast_strip(name);
00760 val = ast_strip_quoted(val, "\"", "\"");
00761
00762 if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00763 continue;
00764 }
00765
00766 if (option_debug) {
00767 ast_log(LOG_DEBUG, "mmm ... cookie! Name: '%s' Value: '%s'\n", name, val);
00768 }
00769
00770 var = ast_variable_new(name, val, __FILE__);
00771 var->next = vars;
00772 vars = var;
00773 }
00774
00775 return vars;
00776 }
00777
00778 static void *httpd_helper_thread(void *data)
00779 {
00780 char buf[4096];
00781 char cookie[4096];
00782 struct ast_tcptls_session_instance *ser = data;
00783 struct ast_variable *vars=NULL, *headers = NULL;
00784 char *uri, *title=NULL;
00785 int status = 200, contentlength = 0;
00786 struct ast_str *out = NULL;
00787 unsigned int static_content = 0;
00788
00789 if (!fgets(buf, sizeof(buf), ser->f))
00790 goto done;
00791
00792 uri = ast_skip_nonblanks(buf);
00793 if (*uri)
00794 *uri++ = '\0';
00795
00796 uri = ast_skip_blanks(uri);
00797
00798 if (*uri) {
00799 char *c = ast_skip_nonblanks(uri);
00800 if (*c)
00801 *c = '\0';
00802 }
00803
00804
00805 while (fgets(cookie, sizeof(cookie), ser->f)) {
00806
00807 ast_trim_blanks(cookie);
00808 if (ast_strlen_zero(cookie))
00809 break;
00810 if (strncasecmp(cookie, "Cookie: ", 8)) {
00811 char *name, *value;
00812 struct ast_variable *var;
00813
00814 value = ast_strdupa(cookie);
00815 name = strsep(&value, ":");
00816 if (!value)
00817 continue;
00818 value = ast_skip_blanks(value);
00819 if (ast_strlen_zero(value))
00820 continue;
00821 var = ast_variable_new(name, value, "");
00822 if (!var)
00823 continue;
00824 var->next = headers;
00825 headers = var;
00826 continue;
00827 }
00828
00829 if (vars) {
00830 ast_variables_destroy(vars);
00831 }
00832 vars = parse_cookies(cookie);
00833 }
00834
00835 if (!*uri) {
00836 out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
00837 } else if (!strcasecmp(buf, "post")) {
00838 #ifdef ENABLE_UPLOADS
00839 out = handle_post(ser, uri, &status, &title, &contentlength, headers, vars);
00840 #else
00841 out = ast_http_error(501, "Not Implemented", NULL,
00842 "Attempt to use unimplemented / unsupported method");
00843 #endif
00844 } else if (strcasecmp(buf, "get")) {
00845 out = ast_http_error(501, "Not Implemented", NULL,
00846 "Attempt to use unimplemented / unsupported method");
00847 } else {
00848 out = handle_uri(ser, uri, &status, &title, &contentlength, &vars, &static_content);
00849 }
00850
00851
00852 if (vars)
00853 ast_variables_destroy(vars);
00854
00855 if (headers)
00856 ast_variables_destroy(headers);
00857
00858 if (out) {
00859 struct timeval tv = ast_tvnow();
00860 char timebuf[256];
00861 struct ast_tm tm;
00862
00863 ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&tv, &tm, "GMT"));
00864 fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
00865 "Server: Asterisk/%s\r\n"
00866 "Date: %s\r\n"
00867 "Connection: close\r\n"
00868 "%s",
00869 status, title ? title : "OK", ast_get_version(), timebuf,
00870 static_content ? "" : "Cache-Control: no-cache, no-store\r\n");
00871
00872
00873
00874
00875 if (!contentlength) {
00876 fprintf(ser->f, "%s", out->str);
00877 } else {
00878 char *tmp = strstr(out->str, "\r\n\r\n");
00879
00880 if (tmp) {
00881 fprintf(ser->f, "Content-length: %d\r\n", contentlength);
00882
00883 if (fwrite(out->str, 1, (tmp + 4 - out->str), ser->f) != tmp + 4 - out->str) {
00884 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00885 }
00886 if (fwrite(tmp + 4, 1, contentlength, ser->f) != contentlength ) {
00887 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00888 }
00889 }
00890 }
00891 ast_free(out);
00892 }
00893 if (title)
00894 ast_free(title);
00895
00896 done:
00897 fclose(ser->f);
00898 ao2_ref(ser, -1);
00899 ser = NULL;
00900 return NULL;
00901 }
00902
00903
00904
00905
00906
00907
00908 static void add_redirect(const char *value)
00909 {
00910 char *target, *dest;
00911 struct http_uri_redirect *redirect, *cur;
00912 unsigned int target_len;
00913 unsigned int total_len;
00914
00915 dest = ast_strdupa(value);
00916 dest = ast_skip_blanks(dest);
00917 target = strsep(&dest, " ");
00918 target = ast_skip_blanks(target);
00919 target = strsep(&target, " ");
00920
00921 if (!dest) {
00922 ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
00923 return;
00924 }
00925
00926 target_len = strlen(target) + 1;
00927 total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
00928
00929 if (!(redirect = ast_calloc(1, total_len)))
00930 return;
00931
00932 redirect->dest = redirect->target + target_len;
00933 strcpy(redirect->target, target);
00934 strcpy(redirect->dest, dest);
00935
00936 AST_RWLIST_WRLOCK(&uri_redirects);
00937
00938 target_len--;
00939 if ( AST_RWLIST_EMPTY(&uri_redirects)
00940 || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
00941 AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
00942 AST_RWLIST_UNLOCK(&uri_redirects);
00943 return;
00944 }
00945
00946 AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
00947 if ( AST_RWLIST_NEXT(cur, entry)
00948 && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
00949 AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
00950 AST_RWLIST_UNLOCK(&uri_redirects);
00951 return;
00952 }
00953 }
00954
00955 AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
00956
00957 AST_RWLIST_UNLOCK(&uri_redirects);
00958 }
00959
00960 #ifdef ENABLE_UPLOADS
00961 static void destroy_post_mapping(struct ast_http_post_mapping *post_map)
00962 {
00963 if (post_map->from)
00964 ast_free(post_map->from);
00965 if (post_map->to)
00966 ast_free(post_map->to);
00967 ast_free(post_map);
00968 }
00969
00970 static void destroy_post_mappings(void)
00971 {
00972 struct ast_http_post_mapping *post_map;
00973
00974 AST_RWLIST_WRLOCK(&post_mappings);
00975 while ((post_map = AST_RWLIST_REMOVE_HEAD(&post_mappings, entry)))
00976 destroy_post_mapping(post_map);
00977 AST_RWLIST_UNLOCK(&post_mappings);
00978 }
00979
00980 static void add_post_mapping(const char *from, const char *to)
00981 {
00982 struct ast_http_post_mapping *post_map;
00983
00984 if (!(post_map = ast_calloc(1, sizeof(*post_map))))
00985 return;
00986
00987 if (!(post_map->from = ast_strdup(from))) {
00988 destroy_post_mapping(post_map);
00989 return;
00990 }
00991
00992 if (!(post_map->to = ast_strdup(to))) {
00993 destroy_post_mapping(post_map);
00994 return;
00995 }
00996
00997 AST_RWLIST_WRLOCK(&post_mappings);
00998 AST_RWLIST_INSERT_TAIL(&post_mappings, post_map, entry);
00999 AST_RWLIST_UNLOCK(&post_mappings);
01000 }
01001 #endif
01002
01003 static int __ast_http_load(int reload)
01004 {
01005 struct ast_config *cfg;
01006 struct ast_variable *v;
01007 int enabled=0;
01008 int newenablestatic=0;
01009 struct hostent *hp;
01010 struct ast_hostent ahp;
01011 char newprefix[MAX_PREFIX] = "";
01012 int have_sslbindaddr = 0;
01013 struct http_uri_redirect *redirect;
01014 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01015
01016 if ((cfg = ast_config_load("http.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
01017 return 0;
01018
01019
01020 memset(&http_desc.sin, 0, sizeof(http_desc.sin));
01021 http_desc.sin.sin_port = htons(8088);
01022
01023 memset(&https_desc.sin, 0, sizeof(https_desc.sin));
01024 https_desc.sin.sin_port = htons(8089);
01025
01026 http_tls_cfg.enabled = 0;
01027 if (http_tls_cfg.certfile)
01028 ast_free(http_tls_cfg.certfile);
01029 http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
01030 if (http_tls_cfg.cipher)
01031 ast_free(http_tls_cfg.cipher);
01032 http_tls_cfg.cipher = ast_strdup("");
01033
01034 AST_RWLIST_WRLOCK(&uri_redirects);
01035 while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry)))
01036 ast_free(redirect);
01037 AST_RWLIST_UNLOCK(&uri_redirects);
01038
01039 #ifdef ENABLE_UPLOADS
01040 destroy_post_mappings();
01041 #endif
01042
01043 if (cfg) {
01044 v = ast_variable_browse(cfg, "general");
01045 for (; v; v = v->next) {
01046 if (!strcasecmp(v->name, "enabled"))
01047 enabled = ast_true(v->value);
01048 else if (!strcasecmp(v->name, "sslenable"))
01049 http_tls_cfg.enabled = ast_true(v->value);
01050 else if (!strcasecmp(v->name, "sslbindport"))
01051 https_desc.sin.sin_port = htons(atoi(v->value));
01052 else if (!strcasecmp(v->name, "sslcert")) {
01053 ast_free(http_tls_cfg.certfile);
01054 http_tls_cfg.certfile = ast_strdup(v->value);
01055 } else if (!strcasecmp(v->name, "sslcipher")) {
01056 ast_free(http_tls_cfg.cipher);
01057 http_tls_cfg.cipher = ast_strdup(v->value);
01058 }
01059 else if (!strcasecmp(v->name, "enablestatic"))
01060 newenablestatic = ast_true(v->value);
01061 else if (!strcasecmp(v->name, "bindport"))
01062 http_desc.sin.sin_port = htons(atoi(v->value));
01063 else if (!strcasecmp(v->name, "sslbindaddr")) {
01064 if ((hp = ast_gethostbyname(v->value, &ahp))) {
01065 memcpy(&https_desc.sin.sin_addr, hp->h_addr, sizeof(https_desc.sin.sin_addr));
01066 have_sslbindaddr = 1;
01067 } else {
01068 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
01069 }
01070 } else if (!strcasecmp(v->name, "bindaddr")) {
01071 if ((hp = ast_gethostbyname(v->value, &ahp))) {
01072 memcpy(&http_desc.sin.sin_addr, hp->h_addr, sizeof(http_desc.sin.sin_addr));
01073 } else {
01074 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
01075 }
01076 } else if (!strcasecmp(v->name, "prefix")) {
01077 if (!ast_strlen_zero(v->value)) {
01078 newprefix[0] = '/';
01079 ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
01080 } else {
01081 newprefix[0] = '\0';
01082 }
01083 } else if (!strcasecmp(v->name, "redirect")) {
01084 add_redirect(v->value);
01085 } else {
01086 ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
01087 }
01088 }
01089
01090 #ifdef ENABLE_UPLOADS
01091 for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next)
01092 add_post_mapping(v->name, v->value);
01093 #endif
01094
01095 ast_config_destroy(cfg);
01096 }
01097 if (!have_sslbindaddr)
01098 https_desc.sin.sin_addr = http_desc.sin.sin_addr;
01099 if (enabled)
01100 http_desc.sin.sin_family = https_desc.sin.sin_family = AF_INET;
01101 if (strcmp(prefix, newprefix))
01102 ast_copy_string(prefix, newprefix, sizeof(prefix));
01103 enablestatic = newenablestatic;
01104 ast_tcptls_server_start(&http_desc);
01105 if (ast_ssl_setup(https_desc.tls_cfg))
01106 ast_tcptls_server_start(&https_desc);
01107
01108 return 0;
01109 }
01110
01111 static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01112 {
01113 struct ast_http_uri *urih;
01114 struct http_uri_redirect *redirect;
01115
01116 #ifdef ENABLE_UPLOADS
01117 struct ast_http_post_mapping *post_map;
01118 #endif
01119
01120 switch (cmd) {
01121 case CLI_INIT:
01122 e->command = "http show status";
01123 e->usage =
01124 "Usage: http show status\n"
01125 " Lists status of internal HTTP engine\n";
01126 return NULL;
01127 case CLI_GENERATE:
01128 return NULL;
01129 }
01130
01131 if (a->argc != 3)
01132 return CLI_SHOWUSAGE;
01133 ast_cli(a->fd, "HTTP Server Status:\n");
01134 ast_cli(a->fd, "Prefix: %s\n", prefix);
01135 if (!http_desc.oldsin.sin_family)
01136 ast_cli(a->fd, "Server Disabled\n\n");
01137 else {
01138 ast_cli(a->fd, "Server Enabled and Bound to %s:%d\n\n",
01139 ast_inet_ntoa(http_desc.oldsin.sin_addr),
01140 ntohs(http_desc.oldsin.sin_port));
01141 if (http_tls_cfg.enabled)
01142 ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s:%d\n\n",
01143 ast_inet_ntoa(https_desc.oldsin.sin_addr),
01144 ntohs(https_desc.oldsin.sin_port));
01145 }
01146
01147 ast_cli(a->fd, "Enabled URI's:\n");
01148 AST_RWLIST_RDLOCK(&uris);
01149 if (AST_RWLIST_EMPTY(&uris)) {
01150 ast_cli(a->fd, "None.\n");
01151 } else {
01152 AST_RWLIST_TRAVERSE(&uris, urih, entry)
01153 ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
01154 }
01155 AST_RWLIST_UNLOCK(&uris);
01156
01157 ast_cli(a->fd, "\nEnabled Redirects:\n");
01158 AST_RWLIST_RDLOCK(&uri_redirects);
01159 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
01160 ast_cli(a->fd, " %s => %s\n", redirect->target, redirect->dest);
01161 if (AST_RWLIST_EMPTY(&uri_redirects))
01162 ast_cli(a->fd, " None.\n");
01163 AST_RWLIST_UNLOCK(&uri_redirects);
01164
01165
01166 #ifdef ENABLE_UPLOADS
01167 ast_cli(a->fd, "\nPOST mappings:\n");
01168 AST_RWLIST_RDLOCK(&post_mappings);
01169 AST_LIST_TRAVERSE(&post_mappings, post_map, entry) {
01170 ast_cli(a->fd, "%s/%s => %s\n", prefix, post_map->from, post_map->to);
01171 }
01172 ast_cli(a->fd, "%s\n", AST_LIST_EMPTY(&post_mappings) ? "None.\n" : "");
01173 AST_RWLIST_UNLOCK(&post_mappings);
01174 #endif
01175
01176 return CLI_SUCCESS;
01177 }
01178
01179 int ast_http_reload(void)
01180 {
01181 return __ast_http_load(1);
01182 }
01183
01184 static struct ast_cli_entry cli_http[] = {
01185 AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
01186 };
01187
01188 int ast_http_init(void)
01189 {
01190 #ifdef ENABLE_UPLOADS
01191 g_mime_init(0);
01192 #endif
01193
01194 ast_http_uri_link(&statusuri);
01195 ast_http_uri_link(&staticuri);
01196 ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
01197
01198 return __ast_http_load(0);
01199 }