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
00032
00033
00034
00035 #include "asterisk.h"
00036
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 76176 $")
00038
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #include <string.h>
00042 #include <ctype.h>
00043 #include <sys/time.h>
00044 #include <sys/types.h>
00045 #include <netdb.h>
00046 #include <sys/socket.h>
00047 #include <netinet/in.h>
00048 #include <netinet/tcp.h>
00049 #include <arpa/inet.h>
00050 #include <signal.h>
00051 #include <errno.h>
00052 #include <unistd.h>
00053
00054 #include "asterisk/channel.h"
00055 #include "asterisk/file.h"
00056 #include "asterisk/manager.h"
00057 #include "asterisk/config.h"
00058 #include "asterisk/callerid.h"
00059 #include "asterisk/lock.h"
00060 #include "asterisk/logger.h"
00061 #include "asterisk/options.h"
00062 #include "asterisk/cli.h"
00063 #include "asterisk/app.h"
00064 #include "asterisk/pbx.h"
00065 #include "asterisk/md5.h"
00066 #include "asterisk/acl.h"
00067 #include "asterisk/utils.h"
00068 #include "asterisk/http.h"
00069 #include "asterisk/threadstorage.h"
00070 #include "asterisk/linkedlists.h"
00071 #include "asterisk/term.h"
00072
00073 struct fast_originate_helper {
00074 char tech[AST_MAX_EXTENSION];
00075 char data[AST_MAX_EXTENSION];
00076 int timeout;
00077 char app[AST_MAX_APP];
00078 char appdata[AST_MAX_EXTENSION];
00079 char cid_name[AST_MAX_EXTENSION];
00080 char cid_num[AST_MAX_EXTENSION];
00081 char context[AST_MAX_CONTEXT];
00082 char exten[AST_MAX_EXTENSION];
00083 char idtext[AST_MAX_EXTENSION];
00084 char account[AST_MAX_ACCOUNT_CODE];
00085 int priority;
00086 struct ast_variable *vars;
00087 };
00088
00089 struct eventqent {
00090 int usecount;
00091 int category;
00092 struct eventqent *next;
00093 char eventdata[1];
00094 };
00095
00096 static int enabled;
00097 static int portno = DEFAULT_MANAGER_PORT;
00098 static int asock = -1;
00099 static int displayconnects = 1;
00100 static int timestampevents;
00101 static int httptimeout = 60;
00102
00103 static pthread_t t;
00104 static int block_sockets;
00105 static int num_sessions;
00106
00107
00108 struct eventqent *master_eventq = NULL;
00109
00110 AST_THREADSTORAGE(manager_event_buf, manager_event_buf_init);
00111 #define MANAGER_EVENT_BUF_INITSIZE 256
00112
00113 AST_THREADSTORAGE(astman_append_buf, astman_append_buf_init);
00114 #define ASTMAN_APPEND_BUF_INITSIZE 256
00115
00116 static struct permalias {
00117 int num;
00118 char *label;
00119 } perms[] = {
00120 { EVENT_FLAG_SYSTEM, "system" },
00121 { EVENT_FLAG_CALL, "call" },
00122 { EVENT_FLAG_LOG, "log" },
00123 { EVENT_FLAG_VERBOSE, "verbose" },
00124 { EVENT_FLAG_COMMAND, "command" },
00125 { EVENT_FLAG_AGENT, "agent" },
00126 { EVENT_FLAG_USER, "user" },
00127 { EVENT_FLAG_CONFIG, "config" },
00128 { -1, "all" },
00129 { 0, "none" },
00130 };
00131
00132 static const char *command_blacklist[] = {
00133 "module load",
00134 "module unload",
00135 };
00136
00137 struct mansession {
00138
00139 pthread_t t;
00140
00141 ast_mutex_t __lock;
00142
00143 struct sockaddr_in sin;
00144
00145 int fd;
00146
00147 int inuse;
00148
00149 int needdestroy;
00150
00151 pthread_t waiting_thread;
00152
00153 unsigned long managerid;
00154
00155 time_t sessiontimeout;
00156
00157 struct ast_dynamic_str *outputstr;
00158
00159 char username[80];
00160
00161 char challenge[10];
00162
00163 int authenticated;
00164
00165 int readperm;
00166
00167 int writeperm;
00168
00169 char inbuf[1024];
00170 int inlen;
00171 int send_events;
00172 int displaysystemname;
00173
00174 struct eventqent *eventq;
00175
00176 int writetimeout;
00177 AST_LIST_ENTRY(mansession) list;
00178 };
00179
00180 static AST_LIST_HEAD_STATIC(sessions, mansession);
00181
00182 struct ast_manager_user {
00183 char username[80];
00184 char *secret;
00185 char *deny;
00186 char *permit;
00187 char *read;
00188 char *write;
00189 unsigned int displayconnects:1;
00190 int keep;
00191 AST_LIST_ENTRY(ast_manager_user) list;
00192 };
00193
00194 static AST_LIST_HEAD_STATIC(users, ast_manager_user);
00195
00196 static struct manager_action *first_action;
00197 AST_RWLOCK_DEFINE_STATIC(actionlock);
00198
00199
00200 static char *authority_to_str(int authority, char *res, int reslen)
00201 {
00202 int running_total = 0, i;
00203
00204 memset(res, 0, reslen);
00205 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
00206 if (authority & perms[i].num) {
00207 if (*res) {
00208 strncat(res, ",", (reslen > running_total) ? reslen - running_total : 0);
00209 running_total++;
00210 }
00211 strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total : 0);
00212 running_total += strlen(perms[i].label);
00213 }
00214 }
00215
00216 if (ast_strlen_zero(res))
00217 ast_copy_string(res, "<none>", reslen);
00218
00219 return res;
00220 }
00221
00222 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
00223 {
00224 struct manager_action *cur;
00225 int which = 0;
00226 char *ret = NULL;
00227
00228 ast_rwlock_rdlock(&actionlock);
00229 for (cur = first_action; cur; cur = cur->next) {
00230 if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
00231 ret = ast_strdup(cur->action);
00232 break;
00233 }
00234 }
00235 ast_rwlock_unlock(&actionlock);
00236
00237 return ret;
00238 }
00239
00240 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
00241 {
00242 while (*src && (*maxlen > 6)) {
00243 switch (*src) {
00244 case '<':
00245 strcpy(*dst, "<");
00246 (*dst) += 4;
00247 *maxlen -= 4;
00248 break;
00249 case '>':
00250 strcpy(*dst, ">");
00251 (*dst) += 4;
00252 *maxlen -= 4;
00253 break;
00254 case '\"':
00255 strcpy(*dst, """);
00256 (*dst) += 6;
00257 *maxlen -= 6;
00258 break;
00259 case '\'':
00260 strcpy(*dst, "'");
00261 (*dst) += 6;
00262 *maxlen -= 6;
00263 break;
00264 case '&':
00265 strcpy(*dst, "&");
00266 (*dst) += 5;
00267 *maxlen -= 5;
00268 break;
00269 default:
00270 *(*dst)++ = lower ? tolower(*src) : *src;
00271 (*maxlen)--;
00272 }
00273 src++;
00274 }
00275 }
00276
00277 static char *xml_translate(char *in, struct ast_variable *vars)
00278 {
00279 struct ast_variable *v;
00280 char *dest = NULL;
00281 char *out, *tmp, *var, *val;
00282 char *objtype = NULL;
00283 int colons = 0;
00284 int breaks = 0;
00285 size_t len;
00286 int count = 1;
00287 int escaped = 0;
00288 int inobj = 0;
00289 int x;
00290
00291 for (v = vars; v; v = v->next) {
00292 if (!dest && !strcasecmp(v->name, "ajaxdest"))
00293 dest = v->value;
00294 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
00295 objtype = v->value;
00296 }
00297 if (!dest)
00298 dest = "unknown";
00299 if (!objtype)
00300 objtype = "generic";
00301 for (x = 0; in[x]; x++) {
00302 if (in[x] == ':')
00303 colons++;
00304 else if (in[x] == '\n')
00305 breaks++;
00306 else if (strchr("&\"<>", in[x]))
00307 escaped++;
00308 }
00309 len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10);
00310 out = ast_malloc(len);
00311 if (!out)
00312 return 0;
00313 tmp = out;
00314 while (*in) {
00315 var = in;
00316 while (*in && (*in >= 32))
00317 in++;
00318 if (*in) {
00319 if ((count > 3) && inobj) {
00320 ast_build_string(&tmp, &len, " /></response>\n");
00321 inobj = 0;
00322 }
00323 count = 0;
00324 while (*in && (*in < 32)) {
00325 *in = '\0';
00326 in++;
00327 count++;
00328 }
00329 val = strchr(var, ':');
00330 if (val) {
00331 *val = '\0';
00332 val++;
00333 if (*val == ' ')
00334 val++;
00335 if (!inobj) {
00336 ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
00337 inobj = 1;
00338 }
00339 ast_build_string(&tmp, &len, " ");
00340 xml_copy_escape(&tmp, &len, var, 1);
00341 ast_build_string(&tmp, &len, "='");
00342 xml_copy_escape(&tmp, &len, val, 0);
00343 ast_build_string(&tmp, &len, "'");
00344 }
00345 }
00346 }
00347 if (inobj)
00348 ast_build_string(&tmp, &len, " /></response>\n");
00349 return out;
00350 }
00351
00352 static char *html_translate(char *in)
00353 {
00354 int x;
00355 int colons = 0;
00356 int breaks = 0;
00357 size_t len;
00358 int count = 1;
00359 char *tmp, *var, *val, *out;
00360
00361 for (x=0; in[x]; x++) {
00362 if (in[x] == ':')
00363 colons++;
00364 if (in[x] == '\n')
00365 breaks++;
00366 }
00367 len = strlen(in) + colons * 40 + breaks * 40;
00368 out = ast_malloc(len);
00369 if (!out)
00370 return 0;
00371 tmp = out;
00372 while (*in) {
00373 var = in;
00374 while (*in && (*in >= 32))
00375 in++;
00376 if (*in) {
00377 if ((count % 4) == 0){
00378 ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00379 }
00380 count = 0;
00381 while (*in && (*in < 32)) {
00382 *in = '\0';
00383 in++;
00384 count++;
00385 }
00386 val = strchr(var, ':');
00387 if (val) {
00388 *val = '\0';
00389 val++;
00390 if (*val == ' ')
00391 val++;
00392 ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
00393 }
00394 }
00395 }
00396 return out;
00397 }
00398
00399
00400
00401 static struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
00402 {
00403 struct ast_manager_user *user = NULL;
00404
00405 AST_LIST_TRAVERSE(&users, user, list)
00406 if (!strcasecmp(user->username, name))
00407 break;
00408 return user;
00409 }
00410
00411 void astman_append(struct mansession *s, const char *fmt, ...)
00412 {
00413 va_list ap;
00414 struct ast_dynamic_str *buf;
00415
00416 ast_mutex_lock(&s->__lock);
00417
00418 if (!(buf = ast_dynamic_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
00419 ast_mutex_unlock(&s->__lock);
00420 return;
00421 }
00422
00423 va_start(ap, fmt);
00424 ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap);
00425 va_end(ap);
00426
00427 if (s->fd > -1)
00428 ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->writetimeout);
00429 else {
00430 if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr)))) {
00431 ast_mutex_unlock(&s->__lock);
00432 return;
00433 }
00434
00435 ast_dynamic_str_append(&s->outputstr, 0, "%s", buf->str);
00436 }
00437
00438 ast_mutex_unlock(&s->__lock);
00439 }
00440
00441 static int handle_showmancmd(int fd, int argc, char *argv[])
00442 {
00443 struct manager_action *cur;
00444 char authority[80];
00445 int num;
00446
00447 if (argc != 4)
00448 return RESULT_SHOWUSAGE;
00449
00450 ast_rwlock_rdlock(&actionlock);
00451 for (cur = first_action; cur; cur = cur->next) {
00452 for (num = 3; num < argc; num++) {
00453 if (!strcasecmp(cur->action, argv[num])) {
00454 ast_cli(fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n", cur->action, cur->synopsis, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->description ? cur->description : "");
00455 }
00456 }
00457 }
00458 ast_rwlock_unlock(&actionlock);
00459
00460 return RESULT_SUCCESS;
00461 }
00462
00463 static int handle_showmanager(int fd, int argc, char *argv[])
00464 {
00465 struct ast_manager_user *user = NULL;
00466
00467 if (argc != 4)
00468 return RESULT_SHOWUSAGE;
00469
00470 AST_LIST_LOCK(&users);
00471
00472 if (!(user = ast_get_manager_by_name_locked(argv[3]))) {
00473 ast_cli(fd, "There is no manager called %s\n", argv[3]);
00474 AST_LIST_UNLOCK(&users);
00475 return -1;
00476 }
00477
00478 ast_cli(fd,"\n");
00479 ast_cli(fd,
00480 " username: %s\n"
00481 " secret: %s\n"
00482 " deny: %s\n"
00483 " permit: %s\n"
00484 " read: %s\n"
00485 " write: %s\n"
00486 "displayconnects: %s\n",
00487 (user->username ? user->username : "(N/A)"),
00488 (user->secret ? "<Set>" : "(N/A)"),
00489 (user->deny ? user->deny : "(N/A)"),
00490 (user->permit ? user->permit : "(N/A)"),
00491 (user->read ? user->read : "(N/A)"),
00492 (user->write ? user->write : "(N/A)"),
00493 (user->displayconnects ? "yes" : "no"));
00494
00495 AST_LIST_UNLOCK(&users);
00496
00497 return RESULT_SUCCESS;
00498 }
00499
00500
00501 static int handle_showmanagers(int fd, int argc, char *argv[])
00502 {
00503 struct ast_manager_user *user = NULL;
00504 int count_amu = 0;
00505
00506 if (argc != 3)
00507 return RESULT_SHOWUSAGE;
00508
00509 AST_LIST_LOCK(&users);
00510
00511
00512 if (AST_LIST_EMPTY(&users)) {
00513 ast_cli(fd, "There are no manager users.\n");
00514 AST_LIST_UNLOCK(&users);
00515 return RESULT_SUCCESS;
00516 }
00517
00518 ast_cli(fd, "\nusername\n--------\n");
00519
00520 AST_LIST_TRAVERSE(&users, user, list) {
00521 ast_cli(fd, "%s\n", user->username);
00522 count_amu++;
00523 }
00524
00525 AST_LIST_UNLOCK(&users);
00526
00527 ast_cli(fd,"-------------------\n");
00528 ast_cli(fd,"%d manager users configured.\n", count_amu);
00529
00530 return RESULT_SUCCESS;
00531 }
00532
00533
00534
00535
00536 static int handle_showmancmds(int fd, int argc, char *argv[])
00537 {
00538 struct manager_action *cur;
00539 char authority[80];
00540 char *format = " %-15.15s %-15.15s %-55.55s\n";
00541
00542 ast_cli(fd, format, "Action", "Privilege", "Synopsis");
00543 ast_cli(fd, format, "------", "---------", "--------");
00544
00545 ast_rwlock_rdlock(&actionlock);
00546 for (cur = first_action; cur; cur = cur->next)
00547 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
00548 ast_rwlock_unlock(&actionlock);
00549
00550 return RESULT_SUCCESS;
00551 }
00552
00553
00554
00555 static int handle_showmanconn(int fd, int argc, char *argv[])
00556 {
00557 struct mansession *s;
00558 char *format = " %-15.15s %-15.15s\n";
00559
00560 ast_cli(fd, format, "Username", "IP Address");
00561
00562 AST_LIST_LOCK(&sessions);
00563 AST_LIST_TRAVERSE(&sessions, s, list)
00564 ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr));
00565 AST_LIST_UNLOCK(&sessions);
00566
00567 return RESULT_SUCCESS;
00568 }
00569
00570
00571
00572 static int handle_showmaneventq(int fd, int argc, char *argv[])
00573 {
00574 struct eventqent *s;
00575
00576 AST_LIST_LOCK(&sessions);
00577 for (s = master_eventq; s; s = s->next) {
00578 ast_cli(fd, "Usecount: %d\n",s->usecount);
00579 ast_cli(fd, "Category: %d\n", s->category);
00580 ast_cli(fd, "Event:\n%s", s->eventdata);
00581 }
00582 AST_LIST_UNLOCK(&sessions);
00583
00584 return RESULT_SUCCESS;
00585 }
00586
00587 static char showmancmd_help[] =
00588 "Usage: manager show command <actionname>\n"
00589 " Shows the detailed description for a specific Asterisk manager interface command.\n";
00590
00591 static char showmancmds_help[] =
00592 "Usage: manager show commands\n"
00593 " Prints a listing of all the available Asterisk manager interface commands.\n";
00594
00595 static char showmanconn_help[] =
00596 "Usage: manager show connected\n"
00597 " Prints a listing of the users that are currently connected to the\n"
00598 "Asterisk manager interface.\n";
00599
00600 static char showmaneventq_help[] =
00601 "Usage: manager show eventq\n"
00602 " Prints a listing of all events pending in the Asterisk manger\n"
00603 "event queue.\n";
00604
00605 static char showmanagers_help[] =
00606 "Usage: manager show users\n"
00607 " Prints a listing of all managers that are currently configured on that\n"
00608 " system.\n";
00609
00610 static char showmanager_help[] =
00611 " Usage: manager show user <user>\n"
00612 " Display all information related to the manager user specified.\n";
00613
00614 static struct ast_cli_entry cli_show_manager_command_deprecated = {
00615 { "show", "manager", "command", NULL },
00616 handle_showmancmd, NULL,
00617 NULL, complete_show_mancmd };
00618
00619 static struct ast_cli_entry cli_show_manager_commands_deprecated = {
00620 { "show", "manager", "commands", NULL },
00621 handle_showmancmds, NULL,
00622 NULL };
00623
00624 static struct ast_cli_entry cli_show_manager_connected_deprecated = {
00625 { "show", "manager", "connected", NULL },
00626 handle_showmanconn, NULL,
00627 NULL };
00628
00629 static struct ast_cli_entry cli_show_manager_eventq_deprecated = {
00630 { "show", "manager", "eventq", NULL },
00631 handle_showmaneventq, NULL,
00632 NULL };
00633
00634 static struct ast_cli_entry cli_manager[] = {
00635 { { "manager", "show", "command", NULL },
00636 handle_showmancmd, "Show a manager interface command",
00637 showmancmd_help, complete_show_mancmd, &cli_show_manager_command_deprecated },
00638
00639 { { "manager", "show", "commands", NULL },
00640 handle_showmancmds, "List manager interface commands",
00641 showmancmds_help, NULL, &cli_show_manager_commands_deprecated },
00642
00643 { { "manager", "show", "connected", NULL },
00644 handle_showmanconn, "List connected manager interface users",
00645 showmanconn_help, NULL, &cli_show_manager_connected_deprecated },
00646
00647 { { "manager", "show", "eventq", NULL },
00648 handle_showmaneventq, "List manager interface queued events",
00649 showmaneventq_help, NULL, &cli_show_manager_eventq_deprecated },
00650
00651 { { "manager", "show", "users", NULL },
00652 handle_showmanagers, "List configured manager users",
00653 showmanagers_help, NULL, NULL },
00654
00655 { { "manager", "show", "user", NULL },
00656 handle_showmanager, "Display information on a specific manager user",
00657 showmanager_help, NULL, NULL },
00658 };
00659
00660 static void unuse_eventqent(struct eventqent *e)
00661 {
00662 if (ast_atomic_dec_and_test(&e->usecount) && e->next)
00663 pthread_kill(t, SIGURG);
00664 }
00665
00666 static void free_session(struct mansession *s)
00667 {
00668 struct eventqent *eqe;
00669 if (s->fd > -1)
00670 close(s->fd);
00671 if (s->outputstr)
00672 free(s->outputstr);
00673 ast_mutex_destroy(&s->__lock);
00674 while (s->eventq) {
00675 eqe = s->eventq;
00676 s->eventq = s->eventq->next;
00677 unuse_eventqent(eqe);
00678 }
00679 free(s);
00680 }
00681
00682 static void destroy_session(struct mansession *s)
00683 {
00684 AST_LIST_LOCK(&sessions);
00685 AST_LIST_REMOVE(&sessions, s, list);
00686 AST_LIST_UNLOCK(&sessions);
00687
00688 ast_atomic_fetchadd_int(&num_sessions, -1);
00689 free_session(s);
00690 }
00691
00692 const char *astman_get_header(const struct message *m, char *var)
00693 {
00694 char cmp[80];
00695 int x;
00696
00697 snprintf(cmp, sizeof(cmp), "%s: ", var);
00698
00699 for (x = 0; x < m->hdrcount; x++) {
00700 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
00701 return m->headers[x] + strlen(cmp);
00702 }
00703
00704 return "";
00705 }
00706
00707 struct ast_variable *astman_get_variables(const struct message *m)
00708 {
00709 int varlen, x, y;
00710 struct ast_variable *head = NULL, *cur;
00711 char *var, *val;
00712
00713 char *parse;
00714 AST_DECLARE_APP_ARGS(args,
00715 AST_APP_ARG(vars)[32];
00716 );
00717
00718 varlen = strlen("Variable: ");
00719
00720 for (x = 0; x < m->hdrcount; x++) {
00721 if (strncasecmp("Variable: ", m->headers[x], varlen))
00722 continue;
00723
00724 parse = ast_strdupa(m->headers[x] + varlen);
00725
00726 AST_STANDARD_APP_ARGS(args, parse);
00727 if (args.argc) {
00728 for (y = 0; y < args.argc; y++) {
00729 if (!args.vars[y])
00730 continue;
00731 var = val = ast_strdupa(args.vars[y]);
00732 strsep(&val, "=");
00733 if (!val || ast_strlen_zero(var))
00734 continue;
00735 cur = ast_variable_new(var, val);
00736 if (head) {
00737 cur->next = head;
00738 head = cur;
00739 } else
00740 head = cur;
00741 }
00742 }
00743 }
00744
00745 return head;
00746 }
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756 void astman_send_error(struct mansession *s, const struct message *m, char *error)
00757 {
00758 const char *id = astman_get_header(m,"ActionID");
00759
00760 astman_append(s, "Response: Error\r\n");
00761 if (!ast_strlen_zero(id))
00762 astman_append(s, "ActionID: %s\r\n", id);
00763 astman_append(s, "Message: %s\r\n\r\n", error);
00764 }
00765
00766 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
00767 {
00768 const char *id = astman_get_header(m,"ActionID");
00769
00770 astman_append(s, "Response: %s\r\n", resp);
00771 if (!ast_strlen_zero(id))
00772 astman_append(s, "ActionID: %s\r\n", id);
00773 if (msg)
00774 astman_append(s, "Message: %s\r\n\r\n", msg);
00775 else
00776 astman_append(s, "\r\n");
00777 }
00778
00779 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
00780 {
00781 astman_send_response(s, m, "Success", msg);
00782 }
00783
00784
00785
00786
00787
00788
00789 static int ast_instring(const char *bigstr, const char *smallstr, char delim)
00790 {
00791 const char *val = bigstr, *next;
00792
00793 do {
00794 if ((next = strchr(val, delim))) {
00795 if (!strncmp(val, smallstr, (next - val)))
00796 return 1;
00797 else
00798 continue;
00799 } else
00800 return !strcmp(smallstr, val);
00801
00802 } while (*(val = (next + 1)));
00803
00804 return 0;
00805 }
00806
00807 static int get_perm(const char *instr)
00808 {
00809 int x = 0, ret = 0;
00810
00811 if (!instr)
00812 return 0;
00813
00814 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
00815 if (ast_instring(instr, perms[x].label, ','))
00816 ret |= perms[x].num;
00817 }
00818
00819 return ret;
00820 }
00821
00822 static int ast_is_number(const char *string)
00823 {
00824 int ret = 1, x = 0;
00825
00826 if (!string)
00827 return 0;
00828
00829 for (x = 0; x < strlen(string); x++) {
00830 if (!(string[x] >= 48 && string[x] <= 57)) {
00831 ret = 0;
00832 break;
00833 }
00834 }
00835
00836 return ret ? atoi(string) : 0;
00837 }
00838
00839 static int strings_to_mask(const char *string)
00840 {
00841 int x, ret = -1;
00842
00843 x = ast_is_number(string);
00844
00845 if (x)
00846 ret = x;
00847 else if (ast_strlen_zero(string))
00848 ret = -1;
00849 else if (ast_false(string))
00850 ret = 0;
00851 else if (ast_true(string)) {
00852 ret = 0;
00853 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
00854 ret |= perms[x].num;
00855 } else {
00856 ret = 0;
00857 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
00858 if (ast_instring(string, perms[x].label, ','))
00859 ret |= perms[x].num;
00860 }
00861 }
00862
00863 return ret;
00864 }
00865
00866
00867
00868
00869
00870 static int set_eventmask(struct mansession *s, const char *eventmask)
00871 {
00872 int maskint = strings_to_mask(eventmask);
00873
00874 ast_mutex_lock(&s->__lock);
00875 if (maskint >= 0)
00876 s->send_events = maskint;
00877 ast_mutex_unlock(&s->__lock);
00878
00879 return maskint;
00880 }
00881
00882 static int authenticate(struct mansession *s, const struct message *m)
00883 {
00884 struct ast_config *cfg;
00885 char *cat;
00886 const char *user = astman_get_header(m, "Username");
00887 const char *pass = astman_get_header(m, "Secret");
00888 const char *authtype = astman_get_header(m, "AuthType");
00889 const char *key = astman_get_header(m, "Key");
00890 const char *events = astman_get_header(m, "Events");
00891
00892 cfg = ast_config_load("manager.conf");
00893 if (!cfg)
00894 return -1;
00895 cat = ast_category_browse(cfg, NULL);
00896 while (cat) {
00897 if (strcasecmp(cat, "general")) {
00898
00899 if (!strcasecmp(cat, user)) {
00900 struct ast_variable *v;
00901 struct ast_ha *ha = NULL;
00902 char *password = NULL;
00903
00904 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
00905 if (!strcasecmp(v->name, "secret")) {
00906 password = v->value;
00907 } else if (!strcasecmp(v->name, "displaysystemname")) {
00908 if (ast_true(v->value)) {
00909 if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
00910 s->displaysystemname = 1;
00911 } else {
00912 ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
00913 }
00914 }
00915 } else if (!strcasecmp(v->name, "permit") ||
00916 !strcasecmp(v->name, "deny")) {
00917 ha = ast_append_ha(v->name, v->value, ha);
00918 } else if (!strcasecmp(v->name, "writetimeout")) {
00919 int val = atoi(v->value);
00920
00921 if (val < 100)
00922 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
00923 else
00924 s->writetimeout = val;
00925 }
00926
00927 }
00928 if (ha && !ast_apply_ha(ha, &(s->sin))) {
00929 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
00930 ast_free_ha(ha);
00931 ast_config_destroy(cfg);
00932 return -1;
00933 } else if (ha)
00934 ast_free_ha(ha);
00935 if (!strcasecmp(authtype, "MD5")) {
00936 if (!ast_strlen_zero(key) &&
00937 !ast_strlen_zero(s->challenge) && !ast_strlen_zero(password)) {
00938 int x;
00939 int len = 0;
00940 char md5key[256] = "";
00941 struct MD5Context md5;
00942 unsigned char digest[16];
00943 MD5Init(&md5);
00944 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
00945 MD5Update(&md5, (unsigned char *) password, strlen(password));
00946 MD5Final(digest, &md5);
00947 for (x=0; x<16; x++)
00948 len += sprintf(md5key + len, "%2.2x", digest[x]);
00949 if (!strcmp(md5key, key))
00950 break;
00951 else {
00952 ast_config_destroy(cfg);
00953 return -1;
00954 }
00955 } else {
00956 ast_log(LOG_DEBUG, "MD5 authentication is not possible. challenge: '%s'\n",
00957 S_OR(s->challenge, ""));
00958 ast_config_destroy(cfg);
00959 return -1;
00960 }
00961 } else if (password && !strcmp(password, pass)) {
00962 break;
00963 } else {
00964 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
00965 ast_config_destroy(cfg);
00966 return -1;
00967 }
00968 }
00969 }
00970 cat = ast_category_browse(cfg, cat);
00971 }
00972 if (cat) {
00973 ast_copy_string(s->username, cat, sizeof(s->username));
00974 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
00975 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
00976 ast_config_destroy(cfg);
00977 if (events)
00978 set_eventmask(s, events);
00979 return 0;
00980 }
00981 ast_config_destroy(cfg);
00982 cfg = ast_config_load("users.conf");
00983 if (!cfg)
00984 return -1;
00985 cat = ast_category_browse(cfg, NULL);
00986 while (cat) {
00987 struct ast_variable *v;
00988 const char *password = NULL;
00989 int hasmanager = 0;
00990 if (strcasecmp(cat, user) || !strcasecmp(cat, "general")) {
00991 cat = ast_category_browse(cfg, cat);
00992 continue;
00993 }
00994 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
00995 if (!strcasecmp(v->name, "secret"))
00996 password = v->value;
00997 else if (!strcasecmp(v->name, "hasmanager"))
00998 hasmanager = ast_true(v->value);
00999 }
01000 if (!hasmanager)
01001 break;
01002 if (!password || strcmp(password, pass)) {
01003 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
01004 ast_config_destroy(cfg);
01005 return -1;
01006 }
01007 ast_copy_string(s->username, cat, sizeof(s->username));
01008 s->readperm = -1;
01009 s->writeperm = -1;
01010 ast_config_destroy(cfg);
01011 if (events)
01012 set_eventmask(s, events);
01013 return 0;
01014 }
01015 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
01016 ast_config_destroy(cfg);
01017 return -1;
01018 }
01019
01020
01021 static char mandescr_ping[] =
01022 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
01023 " manager connection open.\n"
01024 "Variables: NONE\n";
01025
01026 static int action_ping(struct mansession *s, const struct message *m)
01027 {
01028 astman_send_response(s, m, "Pong", NULL);
01029 return 0;
01030 }
01031
01032 static char mandescr_getconfig[] =
01033 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
01034 "file by category and contents.\n"
01035 "Variables:\n"
01036 " Filename: Configuration filename (e.g. foo.conf)\n";
01037
01038 static int action_getconfig(struct mansession *s, const struct message *m)
01039 {
01040 struct ast_config *cfg;
01041 const char *fn = astman_get_header(m, "Filename");
01042 int catcount = 0;
01043 int lineno = 0;
01044 char *category=NULL;
01045 struct ast_variable *v;
01046 char idText[256] = "";
01047 const char *id = astman_get_header(m, "ActionID");
01048
01049 if (!ast_strlen_zero(id))
01050 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01051
01052 if (ast_strlen_zero(fn)) {
01053 astman_send_error(s, m, "Filename not specified");
01054 return 0;
01055 }
01056 if (!(cfg = ast_config_load_with_comments(fn))) {
01057 astman_send_error(s, m, "Config file not found");
01058 return 0;
01059 }
01060 astman_append(s, "Response: Success\r\n%s", idText);
01061 while ((category = ast_category_browse(cfg, category))) {
01062 lineno = 0;
01063 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
01064 for (v = ast_variable_browse(cfg, category); v; v = v->next)
01065 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
01066 catcount++;
01067 }
01068 ast_config_destroy(cfg);
01069 astman_append(s, "\r\n");
01070
01071 return 0;
01072 }
01073
01074
01075 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg)
01076 {
01077 int x;
01078 char hdr[40];
01079 const char *action, *cat, *var, *value, *match;
01080 struct ast_category *category;
01081 struct ast_variable *v;
01082
01083 for (x=0;x<100000;x++) {
01084 unsigned int object = 0;
01085
01086 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
01087 action = astman_get_header(m, hdr);
01088 if (ast_strlen_zero(action))
01089 break;
01090 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
01091 cat = astman_get_header(m, hdr);
01092 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
01093 var = astman_get_header(m, hdr);
01094 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
01095 value = astman_get_header(m, hdr);
01096 if (!ast_strlen_zero(value) && *value == '>') {
01097 object = 1;
01098 value++;
01099 }
01100 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
01101 match = astman_get_header(m, hdr);
01102 if (!strcasecmp(action, "newcat")) {
01103 if (!ast_strlen_zero(cat)) {
01104 category = ast_category_new(cat);
01105 if (category) {
01106 ast_category_append(cfg, category);
01107 }
01108 }
01109 } else if (!strcasecmp(action, "renamecat")) {
01110 if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
01111 category = ast_category_get(cfg, cat);
01112 if (category)
01113 ast_category_rename(category, value);
01114 }
01115 } else if (!strcasecmp(action, "delcat")) {
01116 if (!ast_strlen_zero(cat))
01117 ast_category_delete(cfg, (char *) cat);
01118 } else if (!strcasecmp(action, "update")) {
01119 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
01120 ast_variable_update(category, var, value, match, object);
01121 } else if (!strcasecmp(action, "delete")) {
01122 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
01123 ast_variable_delete(category, (char *) var, (char *) match);
01124 } else if (!strcasecmp(action, "append")) {
01125 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
01126 (category = ast_category_get(cfg, cat)) &&
01127 (v = ast_variable_new(var, value))){
01128 if (object || (match && !strcasecmp(match, "object")))
01129 v->object = 1;
01130 ast_variable_append(category, v);
01131 }
01132 }
01133 }
01134 }
01135
01136 static char mandescr_updateconfig[] =
01137 "Description: A 'UpdateConfig' action will dump the contents of a configuration\n"
01138 "file by category and contents.\n"
01139 "Variables (X's represent 6 digit number beginning with 000000):\n"
01140 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
01141 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
01142 " Reload: Whether or not a reload should take place (or name of specific module)\n"
01143 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
01144 " Cat-XXXXXX: Category to operate on\n"
01145 " Var-XXXXXX: Variable to work on\n"
01146 " Value-XXXXXX: Value to work on\n"
01147 " Match-XXXXXX: Extra match required to match line\n";
01148
01149 static int action_updateconfig(struct mansession *s, const struct message *m)
01150 {
01151 struct ast_config *cfg;
01152 const char *sfn = astman_get_header(m, "SrcFilename");
01153 const char *dfn = astman_get_header(m, "DstFilename");
01154 int res;
01155 char idText[256] = "";
01156 const char *id = astman_get_header(m, "ActionID");
01157 const char *rld = astman_get_header(m, "Reload");
01158
01159 if (!ast_strlen_zero(id))
01160 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01161
01162 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
01163 astman_send_error(s, m, "Filename not specified");
01164 return 0;
01165 }
01166 if (!(cfg = ast_config_load_with_comments(sfn))) {
01167 astman_send_error(s, m, "Config file not found");
01168 return 0;
01169 }
01170 handle_updates(s, m, cfg);
01171 res = config_text_file_save(dfn, cfg, "Manager");
01172 ast_config_destroy(cfg);
01173 if (res) {
01174 astman_send_error(s, m, "Save of config failed");
01175 return 0;
01176 }
01177 astman_append(s, "Response: Success\r\n%s\r\n", idText);
01178 if (!ast_strlen_zero(rld)) {
01179 if (ast_true(rld))
01180 rld = NULL;
01181 ast_module_reload(rld);
01182 }
01183 return 0;
01184 }
01185
01186
01187 static char mandescr_waitevent[] =
01188 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
01189 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
01190 "session, events will be generated and queued.\n"
01191 "Variables: \n"
01192 " Timeout: Maximum time to wait for events\n";
01193
01194 static int action_waitevent(struct mansession *s, const struct message *m)
01195 {
01196 const char *timeouts = astman_get_header(m, "Timeout");
01197 int timeout = -1, max;
01198 int x;
01199 int needexit = 0;
01200 time_t now;
01201 struct eventqent *eqe;
01202 const char *id = astman_get_header(m,"ActionID");
01203 char idText[256] = "";
01204
01205 if (!ast_strlen_zero(id))
01206 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01207
01208 if (!ast_strlen_zero(timeouts)) {
01209 sscanf(timeouts, "%i", &timeout);
01210 }
01211
01212 ast_mutex_lock(&s->__lock);
01213 if (s->waiting_thread != AST_PTHREADT_NULL) {
01214 pthread_kill(s->waiting_thread, SIGURG);
01215 }
01216 if (s->sessiontimeout) {
01217 time(&now);
01218 max = s->sessiontimeout - now - 10;
01219 if (max < 0)
01220 max = 0;
01221 if ((timeout < 0) || (timeout > max))
01222 timeout = max;
01223 if (!s->send_events)
01224 s->send_events = -1;
01225
01226 }
01227 ast_mutex_unlock(&s->__lock);
01228 s->waiting_thread = pthread_self();
01229 if (option_debug)
01230 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
01231 for (x=0; ((x < timeout) || (timeout < 0)); x++) {
01232 ast_mutex_lock(&s->__lock);
01233 if (s->eventq && s->eventq->next)
01234 needexit = 1;
01235 if (s->waiting_thread != pthread_self())
01236 needexit = 1;
01237 if (s->needdestroy)
01238 needexit = 1;
01239 ast_mutex_unlock(&s->__lock);
01240 if (needexit)
01241 break;
01242 if (s->fd > 0) {
01243 if (ast_wait_for_input(s->fd, 1000))
01244 break;
01245 } else {
01246 sleep(1);
01247 }
01248 }
01249 if (option_debug)
01250 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
01251 ast_mutex_lock(&s->__lock);
01252 if (s->waiting_thread == pthread_self()) {
01253 astman_send_response(s, m, "Success", "Waiting for Event...");
01254
01255 while(s->eventq->next) {
01256 eqe = s->eventq->next;
01257 if (((s->readperm & eqe->category) == eqe->category) &&
01258 ((s->send_events & eqe->category) == eqe->category)) {
01259 astman_append(s, "%s", eqe->eventdata);
01260 }
01261 unuse_eventqent(s->eventq);
01262 s->eventq = eqe;
01263 }
01264 astman_append(s,
01265 "Event: WaitEventComplete\r\n"
01266 "%s"
01267 "\r\n", idText);
01268 s->waiting_thread = AST_PTHREADT_NULL;
01269 } else {
01270 ast_log(LOG_DEBUG, "Abandoning event request!\n");
01271 }
01272 ast_mutex_unlock(&s->__lock);
01273 return 0;
01274 }
01275
01276 static char mandescr_listcommands[] =
01277 "Description: Returns the action name and synopsis for every\n"
01278 " action that is available to the user\n"
01279 "Variables: NONE\n";
01280
01281
01282 static int action_listcommands(struct mansession *s, const struct message *m)
01283 {
01284 struct manager_action *cur;
01285 char idText[256] = "";
01286 char temp[BUFSIZ];
01287 const char *id = astman_get_header(m,"ActionID");
01288
01289 if (!ast_strlen_zero(id))
01290 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01291 astman_append(s, "Response: Success\r\n%s", idText);
01292 for (cur = first_action; cur; cur = cur->next) {
01293 if ((s->writeperm & cur->authority) == cur->authority)
01294 astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
01295 }
01296 astman_append(s, "\r\n");
01297
01298 return 0;
01299 }
01300
01301 static char mandescr_events[] =
01302 "Description: Enable/Disable sending of events to this manager\n"
01303 " client.\n"
01304 "Variables:\n"
01305 " EventMask: 'on' if all events should be sent,\n"
01306 " 'off' if no events should be sent,\n"
01307 " 'system,call,log' to select which flags events should have to be sent.\n";
01308
01309 static int action_events(struct mansession *s, const struct message *m)
01310 {
01311 const char *mask = astman_get_header(m, "EventMask");
01312 int res;
01313
01314 res = set_eventmask(s, mask);
01315 if (res > 0)
01316 astman_send_response(s, m, "Events On", NULL);
01317 else if (res == 0)
01318 astman_send_response(s, m, "Events Off", NULL);
01319
01320 return 0;
01321 }
01322
01323 static char mandescr_logoff[] =
01324 "Description: Logoff this manager session\n"
01325 "Variables: NONE\n";
01326
01327 static int action_logoff(struct mansession *s, const struct message *m)
01328 {
01329 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
01330 return -1;
01331 }
01332
01333 static char mandescr_hangup[] =
01334 "Description: Hangup a channel\n"
01335 "Variables: \n"
01336 " Channel: The channel name to be hungup\n";
01337
01338 static int action_hangup(struct mansession *s, const struct message *m)
01339 {
01340 struct ast_channel *c = NULL;
01341 const char *name = astman_get_header(m, "Channel");
01342 if (ast_strlen_zero(name)) {
01343 astman_send_error(s, m, "No channel specified");
01344 return 0;
01345 }
01346 c = ast_get_channel_by_name_locked(name);
01347 if (!c) {
01348 astman_send_error(s, m, "No such channel");
01349 return 0;
01350 }
01351 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
01352 ast_channel_unlock(c);
01353 astman_send_ack(s, m, "Channel Hungup");
01354 return 0;
01355 }
01356
01357 static char mandescr_setvar[] =
01358 "Description: Set a global or local channel variable.\n"
01359 "Variables: (Names marked with * are required)\n"
01360 " Channel: Channel to set variable for\n"
01361 " *Variable: Variable name\n"
01362 " *Value: Value\n";
01363
01364 static int action_setvar(struct mansession *s, const struct message *m)
01365 {
01366 struct ast_channel *c = NULL;
01367 const char *name = astman_get_header(m, "Channel");
01368 const char *varname = astman_get_header(m, "Variable");
01369 const char *varval = astman_get_header(m, "Value");
01370
01371 if (ast_strlen_zero(varname)) {
01372 astman_send_error(s, m, "No variable specified");
01373 return 0;
01374 }
01375
01376 if (!ast_strlen_zero(name)) {
01377 c = ast_get_channel_by_name_locked(name);
01378 if (!c) {
01379 astman_send_error(s, m, "No such channel");
01380 return 0;
01381 }
01382 }
01383
01384 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
01385
01386 if (c)
01387 ast_channel_unlock(c);
01388
01389 astman_send_ack(s, m, "Variable Set");
01390
01391 return 0;
01392 }
01393
01394 static char mandescr_getvar[] =
01395 "Description: Get the value of a global or local channel variable.\n"
01396 "Variables: (Names marked with * are required)\n"
01397 " Channel: Channel to read variable from\n"
01398 " *Variable: Variable name\n"
01399 " ActionID: Optional Action id for message matching.\n";
01400
01401 static int action_getvar(struct mansession *s, const struct message *m)
01402 {
01403 struct ast_channel *c = NULL;
01404 const char *name = astman_get_header(m, "Channel");
01405 const char *varname = astman_get_header(m, "Variable");
01406 const char *id = astman_get_header(m,"ActionID");
01407 char *varval;
01408 char workspace[1024] = "";
01409
01410 if (ast_strlen_zero(varname)) {
01411 astman_send_error(s, m, "No variable specified");
01412 return 0;
01413 }
01414
01415 if (!ast_strlen_zero(name)) {
01416 c = ast_get_channel_by_name_locked(name);
01417 if (!c) {
01418 astman_send_error(s, m, "No such channel");
01419 return 0;
01420 }
01421 }
01422
01423 if (varname[strlen(varname) - 1] == ')') {
01424 char *copy = ast_strdupa(varname);
01425
01426 ast_func_read(c, copy, workspace, sizeof(workspace));
01427 varval = workspace;
01428 } else {
01429 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
01430 }
01431
01432 if (c)
01433 ast_channel_unlock(c);
01434 astman_append(s, "Response: Success\r\n"
01435 "Variable: %s\r\nValue: %s\r\n", varname, varval);
01436 if (!ast_strlen_zero(id))
01437 astman_append(s, "ActionID: %s\r\n",id);
01438 astman_append(s, "\r\n");
01439
01440 return 0;
01441 }
01442
01443
01444
01445
01446 static int action_status(struct mansession *s, const struct message *m)
01447 {
01448 const char *id = astman_get_header(m,"ActionID");
01449 const char *name = astman_get_header(m,"Channel");
01450 char idText[256] = "";
01451 struct ast_channel *c;
01452 char bridge[256];
01453 struct timeval now = ast_tvnow();
01454 long elapsed_seconds = 0;
01455 int all = ast_strlen_zero(name);
01456
01457 if (!ast_strlen_zero(id))
01458 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01459 if (all)
01460 c = ast_channel_walk_locked(NULL);
01461 else {
01462 c = ast_get_channel_by_name_locked(name);
01463 if (!c) {
01464 astman_send_error(s, m, "No such channel");
01465 return 0;
01466 }
01467 }
01468 astman_send_ack(s, m, "Channel status will follow");
01469
01470 while (c) {
01471 if (c->_bridge)
01472 snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
01473 else
01474 bridge[0] = '\0';
01475 if (c->pbx) {
01476 if (c->cdr) {
01477 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01478 }
01479 astman_append(s,
01480 "Event: Status\r\n"
01481 "Privilege: Call\r\n"
01482 "Channel: %s\r\n"
01483 "CallerID: %s\r\n"
01484 "CallerIDNum: %s\r\n"
01485 "CallerIDName: %s\r\n"
01486 "Account: %s\r\n"
01487 "State: %s\r\n"
01488 "Context: %s\r\n"
01489 "Extension: %s\r\n"
01490 "Priority: %d\r\n"
01491 "Seconds: %ld\r\n"
01492 "%s"
01493 "Uniqueid: %s\r\n"
01494 "%s"
01495 "\r\n",
01496 c->name,
01497 S_OR(c->cid.cid_num, "<unknown>"),
01498 S_OR(c->cid.cid_num, "<unknown>"),
01499 S_OR(c->cid.cid_name, "<unknown>"),
01500 c->accountcode,
01501 ast_state2str(c->_state), c->context,
01502 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
01503 } else {
01504 astman_append(s,
01505 "Event: Status\r\n"
01506 "Privilege: Call\r\n"
01507 "Channel: %s\r\n"
01508 "CallerID: %s\r\n"
01509 "CallerIDNum: %s\r\n"
01510 "CallerIDName: %s\r\n"
01511 "Account: %s\r\n"
01512 "State: %s\r\n"
01513 "%s"
01514 "Uniqueid: %s\r\n"
01515 "%s"
01516 "\r\n",
01517 c->name,
01518 S_OR(c->cid.cid_num, "<unknown>"),
01519 S_OR(c->cid.cid_num, "<unknown>"),
01520 S_OR(c->cid.cid_name, "<unknown>"),
01521 c->accountcode,
01522 ast_state2str(c->_state), bridge, c->uniqueid, idText);
01523 }
01524 ast_channel_unlock(c);
01525 if (!all)
01526 break;
01527 c = ast_channel_walk_locked(c);
01528 }
01529 astman_append(s,
01530 "Event: StatusComplete\r\n"
01531 "%s"
01532 "\r\n",idText);
01533 return 0;
01534 }
01535
01536 static char mandescr_redirect[] =
01537 "Description: Redirect (transfer) a call.\n"
01538 "Variables: (Names marked with * are required)\n"
01539 " *Channel: Channel to redirect\n"
01540 " ExtraChannel: Second call leg to transfer (optional)\n"
01541 " *Exten: Extension to transfer to\n"
01542 " *Context: Context to transfer to\n"
01543 " *Priority: Priority to transfer to\n"
01544 " ActionID: Optional Action id for message matching.\n";
01545
01546
01547 static int action_redirect(struct mansession *s, const struct message *m)
01548 {
01549 const char *name = astman_get_header(m, "Channel");
01550 const char *name2 = astman_get_header(m, "ExtraChannel");
01551 const char *exten = astman_get_header(m, "Exten");
01552 const char *context = astman_get_header(m, "Context");
01553 const char *priority = astman_get_header(m, "Priority");
01554 struct ast_channel *chan, *chan2 = NULL;
01555 int pi = 0;
01556 int res;
01557
01558 if (ast_strlen_zero(name)) {
01559 astman_send_error(s, m, "Channel not specified");
01560 return 0;
01561 }
01562 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
01563 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
01564 astman_send_error(s, m, "Invalid priority\n");
01565 return 0;
01566 }
01567 }
01568
01569 chan = ast_get_channel_by_name_locked(name);
01570 if (!chan) {
01571 char buf[BUFSIZ];
01572 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
01573 astman_send_error(s, m, buf);
01574 return 0;
01575 }
01576 if (ast_check_hangup(chan)) {
01577 astman_send_error(s, m, "Redirect failed, channel not up.\n");
01578 ast_channel_unlock(chan);
01579 return 0;
01580 }
01581 if (!ast_strlen_zero(name2))
01582 chan2 = ast_get_channel_by_name_locked(name2);
01583 if (chan2 && ast_check_hangup(chan2)) {
01584 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
01585 ast_channel_unlock(chan);
01586 ast_channel_unlock(chan2);
01587 return 0;
01588 }
01589 res = ast_async_goto(chan, context, exten, pi);
01590 if (!res) {
01591 if (!ast_strlen_zero(name2)) {
01592 if (chan2)
01593 res = ast_async_goto(chan2, context, exten, pi);
01594 else
01595 res = -1;
01596 if (!res)
01597 astman_send_ack(s, m, "Dual Redirect successful");
01598 else
01599 astman_send_error(s, m, "Secondary redirect failed");
01600 } else
01601 astman_send_ack(s, m, "Redirect successful");
01602 } else
01603 astman_send_error(s, m, "Redirect failed");
01604 if (chan)
01605 ast_channel_unlock(chan);
01606 if (chan2)
01607 ast_channel_unlock(chan2);
01608 return 0;
01609 }
01610
01611 static char mandescr_command[] =
01612 "Description: Run a CLI command.\n"
01613 "Variables: (Names marked with * are required)\n"
01614 " *Command: Asterisk CLI command to run\n"
01615 " ActionID: Optional Action id for message matching.\n";
01616
01617
01618 static int action_command(struct mansession *s, const struct message *m)
01619 {
01620 const char *cmd = astman_get_header(m, "Command");
01621 const char *id = astman_get_header(m, "ActionID");
01622 char *buf, *final_buf;
01623 char template[] = "/tmp/ast-ami-XXXXXX";
01624 int fd = mkstemp(template), i = 0;
01625 off_t l;
01626
01627 for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
01628 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
01629 astman_send_error(s, m, "Command blacklisted");
01630 return 0;
01631 }
01632 }
01633
01634 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
01635 if (!ast_strlen_zero(id))
01636 astman_append(s, "ActionID: %s\r\n", id);
01637
01638 ast_cli_command(fd, cmd);
01639 l = lseek(fd, 0, SEEK_END);
01640
01641
01642 buf = ast_calloc(1, l + 1);
01643 final_buf = ast_calloc(1, l + 1);
01644 if (buf) {
01645 lseek(fd, 0, SEEK_SET);
01646 read(fd, buf, l);
01647 buf[l] = '\0';
01648 if (final_buf) {
01649 term_strip(final_buf, buf, l);
01650 final_buf[l] = '\0';
01651 }
01652 astman_append(s, S_OR(final_buf, buf));
01653 ast_free(buf);
01654 }
01655 close(fd);
01656 unlink(template);
01657 astman_append(s, "--END COMMAND--\r\n\r\n");
01658 if (final_buf)
01659 ast_free(final_buf);
01660 return 0;
01661 }
01662
01663 static void *fast_originate(void *data)
01664 {
01665 struct fast_originate_helper *in = data;
01666 int res;
01667 int reason = 0;
01668 struct ast_channel *chan = NULL;
01669 char requested_channel[AST_CHANNEL_NAME];
01670
01671 if (!ast_strlen_zero(in->app)) {
01672 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
01673 S_OR(in->cid_num, NULL),
01674 S_OR(in->cid_name, NULL),
01675 in->vars, in->account, &chan);
01676 } else {
01677 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
01678 S_OR(in->cid_num, NULL),
01679 S_OR(in->cid_name, NULL),
01680 in->vars, in->account, &chan);
01681 }
01682
01683 if (!chan)
01684 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
01685
01686 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
01687 "%s"
01688 "Response: %s\r\n"
01689 "Channel: %s\r\n"
01690 "Context: %s\r\n"
01691 "Exten: %s\r\n"
01692 "Reason: %d\r\n"
01693 "Uniqueid: %s\r\n"
01694 "CallerID: %s\r\n"
01695 "CallerIDNum: %s\r\n"
01696 "CallerIDName: %s\r\n",
01697 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
01698 chan ? chan->uniqueid : "<null>",
01699 S_OR(in->cid_num, "<unknown>"),
01700 S_OR(in->cid_num, "<unknown>"),
01701 S_OR(in->cid_name, "<unknown>")
01702 );
01703
01704
01705 if (chan)
01706 ast_channel_unlock(chan);
01707 free(in);
01708 return NULL;
01709 }
01710
01711 static char mandescr_originate[] =
01712 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
01713 " Application/Data\n"
01714 "Variables: (Names marked with * are required)\n"
01715 " *Channel: Channel name to call\n"
01716 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
01717 " Context: Context to use (requires 'Exten' and 'Priority')\n"
01718 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
01719 " Application: Application to use\n"
01720 " Data: Data to use (requires 'Application')\n"
01721 " Timeout: How long to wait for call to be answered (in ms)\n"
01722 " CallerID: Caller ID to be set on the outgoing channel\n"
01723 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
01724 " Account: Account code\n"
01725 " Async: Set to 'true' for fast origination\n";
01726
01727 static int action_originate(struct mansession *s, const struct message *m)
01728 {
01729 const char *name = astman_get_header(m, "Channel");
01730 const char *exten = astman_get_header(m, "Exten");
01731 const char *context = astman_get_header(m, "Context");
01732 const char *priority = astman_get_header(m, "Priority");
01733 const char *timeout = astman_get_header(m, "Timeout");
01734 const char *callerid = astman_get_header(m, "CallerID");
01735 const char *account = astman_get_header(m, "Account");
01736 const char *app = astman_get_header(m, "Application");
01737 const char *appdata = astman_get_header(m, "Data");
01738 const char *async = astman_get_header(m, "Async");
01739 const char *id = astman_get_header(m, "ActionID");
01740 struct ast_variable *vars = astman_get_variables(m);
01741 char *tech, *data;
01742 char *l = NULL, *n = NULL;
01743 int pi = 0;
01744 int res;
01745 int to = 30000;
01746 int reason = 0;
01747 char tmp[256];
01748 char tmp2[256];
01749
01750 pthread_t th;
01751 pthread_attr_t attr;
01752 if (!name) {
01753 astman_send_error(s, m, "Channel not specified");
01754 return 0;
01755 }
01756 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
01757 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
01758 astman_send_error(s, m, "Invalid priority\n");
01759 return 0;
01760 }
01761 }
01762 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
01763 astman_send_error(s, m, "Invalid timeout\n");
01764 return 0;
01765 }
01766 ast_copy_string(tmp, name, sizeof(tmp));
01767 tech = tmp;
01768 data = strchr(tmp, '/');
01769 if (!data) {
01770 astman_send_error(s, m, "Invalid channel\n");
01771 return 0;
01772 }
01773 *data++ = '\0';
01774 ast_copy_string(tmp2, callerid, sizeof(tmp2));
01775 ast_callerid_parse(tmp2, &n, &l);
01776 if (n) {
01777 if (ast_strlen_zero(n))
01778 n = NULL;
01779 }
01780 if (l) {
01781 ast_shrink_phone_number(l);
01782 if (ast_strlen_zero(l))
01783 l = NULL;
01784 }
01785 if (ast_true(async)) {
01786 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
01787 if (!fast) {
01788 res = -1;
01789 } else {
01790 if (!ast_strlen_zero(id))
01791 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
01792 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
01793 ast_copy_string(fast->data, data, sizeof(fast->data));
01794 ast_copy_string(fast->app, app, sizeof(fast->app));
01795 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
01796 if (l)
01797 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
01798 if (n)
01799 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
01800 fast->vars = vars;
01801 ast_copy_string(fast->context, context, sizeof(fast->context));
01802 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
01803 ast_copy_string(fast->account, account, sizeof(fast->account));
01804 fast->timeout = to;
01805 fast->priority = pi;
01806 pthread_attr_init(&attr);
01807 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01808 if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
01809 res = -1;
01810 } else {
01811 res = 0;
01812 }
01813 pthread_attr_destroy(&attr);
01814 }
01815 } else if (!ast_strlen_zero(app)) {
01816 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
01817 } else {
01818 if (exten && context && pi)
01819 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
01820 else {
01821 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
01822 return 0;
01823 }
01824 }
01825 if (!res)
01826 astman_send_ack(s, m, "Originate successfully queued");
01827 else
01828 astman_send_error(s, m, "Originate failed");
01829 return 0;
01830 }
01831
01832
01833
01834 static char mandescr_mailboxstatus[] =
01835 "Description: Checks a voicemail account for status.\n"
01836 "Variables: (Names marked with * are required)\n"
01837 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
01838 " ActionID: Optional ActionID for message matching.\n"
01839 "Returns number of messages.\n"
01840 " Message: Mailbox Status\n"
01841 " Mailbox: <mailboxid>\n"
01842 " Waiting: <count>\n"
01843 "\n";
01844
01845 static int action_mailboxstatus(struct mansession *s, const struct message *m)
01846 {
01847 const char *mailbox = astman_get_header(m, "Mailbox");
01848 const char *id = astman_get_header(m,"ActionID");
01849 char idText[256] = "";
01850 int ret;
01851 if (ast_strlen_zero(mailbox)) {
01852 astman_send_error(s, m, "Mailbox not specified");
01853 return 0;
01854 }
01855 if (!ast_strlen_zero(id))
01856 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01857 ret = ast_app_has_voicemail(mailbox, NULL);
01858 astman_append(s, "Response: Success\r\n"
01859 "%s"
01860 "Message: Mailbox Status\r\n"
01861 "Mailbox: %s\r\n"
01862 "Waiting: %d\r\n\r\n", idText, mailbox, ret);
01863 return 0;
01864 }
01865
01866 static char mandescr_mailboxcount[] =
01867 "Description: Checks a voicemail account for new messages.\n"
01868 "Variables: (Names marked with * are required)\n"
01869 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
01870 " ActionID: Optional ActionID for message matching.\n"
01871 "Returns number of new and old messages.\n"
01872 " Message: Mailbox Message Count\n"
01873 " Mailbox: <mailboxid>\n"
01874 " NewMessages: <count>\n"
01875 " OldMessages: <count>\n"
01876 "\n";
01877 static int action_mailboxcount(struct mansession *s, const struct message *m)
01878 {
01879 const char *mailbox = astman_get_header(m, "Mailbox");
01880 const char *id = astman_get_header(m,"ActionID");
01881 char idText[256] = "";
01882 int newmsgs = 0, oldmsgs = 0;
01883 if (ast_strlen_zero(mailbox)) {
01884 astman_send_error(s, m, "Mailbox not specified");
01885 return 0;
01886 }
01887 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
01888 if (!ast_strlen_zero(id)) {
01889 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
01890 }
01891 astman_append(s, "Response: Success\r\n"
01892 "%s"
01893 "Message: Mailbox Message Count\r\n"
01894 "Mailbox: %s\r\n"
01895 "NewMessages: %d\r\n"
01896 "OldMessages: %d\r\n"
01897 "\r\n",
01898 idText,mailbox, newmsgs, oldmsgs);
01899 return 0;
01900 }
01901
01902 static char mandescr_extensionstate[] =
01903 "Description: Report the extension state for given extension.\n"
01904 " If the extension has a hint, will use devicestate to check\n"
01905 " the status of the device connected to the extension.\n"
01906 "Variables: (Names marked with * are required)\n"
01907 " *Exten: Extension to check state on\n"
01908 " *Context: Context for extension\n"
01909 " ActionId: Optional ID for this transaction\n"
01910 "Will return an \"Extension Status\" message.\n"
01911 "The response will include the hint for the extension and the status.\n";
01912
01913 static int action_extensionstate(struct mansession *s, const struct message *m)
01914 {
01915 const char *exten = astman_get_header(m, "Exten");
01916 const char *context = astman_get_header(m, "Context");
01917 const char *id = astman_get_header(m,"ActionID");
01918 char idText[256] = "";
01919 char hint[256] = "";
01920 int status;
01921 if (ast_strlen_zero(exten)) {
01922 astman_send_error(s, m, "Extension not specified");
01923 return 0;
01924 }
01925 if (ast_strlen_zero(context))
01926 context = "default";
01927 status = ast_extension_state(NULL, context, exten);
01928 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
01929 if (!ast_strlen_zero(id)) {
01930 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01931 }
01932 astman_append(s, "Response: Success\r\n"
01933 "%s"
01934 "Message: Extension Status\r\n"
01935 "Exten: %s\r\n"
01936 "Context: %s\r\n"
01937 "Hint: %s\r\n"
01938 "Status: %d\r\n\r\n",
01939 idText,exten, context, hint, status);
01940 return 0;
01941 }
01942
01943 static char mandescr_timeout[] =
01944 "Description: Hangup a channel after a certain time.\n"
01945 "Variables: (Names marked with * are required)\n"
01946 " *Channel: Channel name to hangup\n"
01947 " *Timeout: Maximum duration of the call (sec)\n"
01948 "Acknowledges set time with 'Timeout Set' message\n";
01949
01950 static int action_timeout(struct mansession *s, const struct message *m)
01951 {
01952 struct ast_channel *c = NULL;
01953 const char *name = astman_get_header(m, "Channel");
01954 int timeout = atoi(astman_get_header(m, "Timeout"));
01955 if (ast_strlen_zero(name)) {
01956 astman_send_error(s, m, "No channel specified");
01957 return 0;
01958 }
01959 if (!timeout) {
01960 astman_send_error(s, m, "No timeout specified");
01961 return 0;
01962 }
01963 c = ast_get_channel_by_name_locked(name);
01964 if (!c) {
01965 astman_send_error(s, m, "No such channel");
01966 return 0;
01967 }
01968 ast_channel_setwhentohangup(c, timeout);
01969 ast_channel_unlock(c);
01970 astman_send_ack(s, m, "Timeout Set");
01971 return 0;
01972 }
01973
01974 static int process_events(struct mansession *s)
01975 {
01976 struct eventqent *eqe;
01977 int ret = 0;
01978 ast_mutex_lock(&s->__lock);
01979 if (s->fd > -1) {
01980 if (!s->eventq)
01981 s->eventq = master_eventq;
01982 while(s->eventq->next) {
01983 eqe = s->eventq->next;
01984 if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
01985 ((s->send_events & eqe->category) == eqe->category)) {
01986 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
01987 ret = -1;
01988 }
01989 unuse_eventqent(s->eventq);
01990 s->eventq = eqe;
01991 }
01992 }
01993 ast_mutex_unlock(&s->__lock);
01994 return ret;
01995 }
01996
01997 static char mandescr_userevent[] =
01998 "Description: Send an event to manager sessions.\n"
01999 "Variables: (Names marked with * are required)\n"
02000 " *UserEvent: EventStringToSend\n"
02001 " Header1: Content1\n"
02002 " HeaderN: ContentN\n";
02003
02004 static int action_userevent(struct mansession *s, const struct message *m)
02005 {
02006 const char *event = astman_get_header(m, "UserEvent");
02007 char body[2048] = "";
02008 int x, bodylen = 0;
02009 for (x = 0; x < m->hdrcount; x++) {
02010 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
02011 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
02012 bodylen += strlen(m->headers[x]);
02013 ast_copy_string(body + bodylen, "\r\n", 3);
02014 bodylen += 2;
02015 }
02016 }
02017
02018 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
02019 return 0;
02020 }
02021
02022 static int process_message(struct mansession *s, const struct message *m)
02023 {
02024 char action[80] = "";
02025 struct manager_action *tmp;
02026 const char *id = astman_get_header(m,"ActionID");
02027 char idText[256] = "";
02028 int ret = 0;
02029
02030 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
02031 if (option_debug)
02032 ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
02033
02034 if (ast_strlen_zero(action)) {
02035 astman_send_error(s, m, "Missing action in request");
02036 return 0;
02037 }
02038 if (!ast_strlen_zero(id)) {
02039 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02040 }
02041 if (!s->authenticated) {
02042 if (!strcasecmp(action, "Challenge")) {
02043 const char *authtype = astman_get_header(m, "AuthType");
02044
02045 if (!strcasecmp(authtype, "MD5")) {
02046 if (ast_strlen_zero(s->challenge))
02047 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
02048 astman_append(s, "Response: Success\r\n"
02049 "%s"
02050 "Challenge: %s\r\n\r\n",
02051 idText, s->challenge);
02052 return 0;
02053 } else {
02054 astman_send_error(s, m, "Must specify AuthType");
02055 return 0;
02056 }
02057 } else if (!strcasecmp(action, "Login")) {
02058 if (authenticate(s, m)) {
02059 sleep(1);
02060 astman_send_error(s, m, "Authentication failed");
02061 return -1;
02062 } else {
02063 s->authenticated = 1;
02064 if (option_verbose > 1) {
02065 if (displayconnects) {
02066 ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n",
02067 (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
02068 }
02069 }
02070 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n",
02071 (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
02072 astman_send_ack(s, m, "Authentication accepted");
02073 }
02074 } else if (!strcasecmp(action, "Logoff")) {
02075 astman_send_ack(s, m, "See ya");
02076 return -1;
02077 } else
02078 astman_send_error(s, m, "Authentication Required");
02079 } else {
02080 if (!strcasecmp(action, "Login"))
02081 astman_send_ack(s, m, "Already logged in");
02082 else {
02083 ast_rwlock_rdlock(&actionlock);
02084 for (tmp = first_action; tmp; tmp = tmp->next) {
02085 if (strcasecmp(action, tmp->action))
02086 continue;
02087 if ((s->writeperm & tmp->authority) == tmp->authority) {
02088 if (tmp->func(s, m))
02089 ret = -1;
02090 } else
02091 astman_send_error(s, m, "Permission denied");
02092 break;
02093 }
02094 ast_rwlock_unlock(&actionlock);
02095 if (!tmp)
02096 astman_send_error(s, m, "Invalid/unknown command");
02097 }
02098 }
02099 if (ret)
02100 return ret;
02101 return process_events(s);
02102 }
02103
02104 static int get_input(struct mansession *s, char *output)
02105 {
02106
02107 int res;
02108 int x;
02109 struct pollfd fds[1];
02110 for (x = 1; x < s->inlen; x++) {
02111 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
02112
02113 memcpy(output, s->inbuf, x + 1);
02114
02115 output[x+1] = '\0';
02116
02117 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
02118 s->inlen -= (x + 1);
02119 return 1;
02120 }
02121 }
02122 if (s->inlen >= sizeof(s->inbuf) - 1) {
02123 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
02124 s->inlen = 0;
02125 }
02126 fds[0].fd = s->fd;
02127 fds[0].events = POLLIN;
02128 do {
02129 ast_mutex_lock(&s->__lock);
02130 s->waiting_thread = pthread_self();
02131 ast_mutex_unlock(&s->__lock);
02132
02133 res = poll(fds, 1, -1);
02134
02135 ast_mutex_lock(&s->__lock);
02136 s->waiting_thread = AST_PTHREADT_NULL;
02137 ast_mutex_unlock(&s->__lock);
02138 if (res < 0) {
02139 if (errno == EINTR) {
02140 return 0;
02141 }
02142 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
02143 return -1;
02144 } else if (res > 0) {
02145 ast_mutex_lock(&s->__lock);
02146 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
02147 ast_mutex_unlock(&s->__lock);
02148 if (res < 1)
02149 return -1;
02150 break;
02151 }
02152 } while(1);
02153 s->inlen += res;
02154 s->inbuf[s->inlen] = '\0';
02155 return 0;
02156 }
02157
02158 static int do_message(struct mansession *s)
02159 {
02160 struct message m = { 0 };
02161 char header_buf[sizeof(s->inbuf)] = { '\0' };
02162 int res;
02163
02164 for (;;) {
02165
02166 if (s->eventq->next) {
02167 if (process_events(s))
02168 return -1;
02169 }
02170 res = get_input(s, header_buf);
02171 if (res == 0) {
02172 continue;
02173 } else if (res > 0) {
02174
02175 if (strlen(header_buf) < 2)
02176 continue;
02177 header_buf[strlen(header_buf) - 2] = '\0';
02178 if (ast_strlen_zero(header_buf))
02179 return process_message(s, &m) ? -1 : 0;
02180 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
02181 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
02182 } else {
02183 return res;
02184 }
02185 }
02186 }
02187
02188 static void *session_do(void *data)
02189 {
02190 struct mansession *s = data;
02191 int res;
02192
02193 astman_append(s, "Asterisk Call Manager/1.0\r\n");
02194 for (;;) {
02195 if ((res = do_message(s)) < 0)
02196 break;
02197 }
02198 if (s->authenticated) {
02199 if (option_verbose > 1) {
02200 if (displayconnects)
02201 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
02202 }
02203 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
02204 } else {
02205 if (option_verbose > 1) {
02206 if (displayconnects)
02207 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
02208 }
02209 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
02210 }
02211 destroy_session(s);
02212 return NULL;
02213 }
02214
02215 static void *accept_thread(void *ignore)
02216 {
02217 int as;
02218 struct sockaddr_in sin;
02219 socklen_t sinlen;
02220 struct eventqent *eqe;
02221 struct mansession *s;
02222 struct protoent *p;
02223 int arg = 1;
02224 int flags;
02225 pthread_attr_t attr;
02226 time_t now;
02227 struct pollfd pfds[1];
02228
02229 pthread_attr_init(&attr);
02230 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02231
02232 for (;;) {
02233 time(&now);
02234 AST_LIST_LOCK(&sessions);
02235 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
02236 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
02237 AST_LIST_REMOVE_CURRENT(&sessions, list);
02238 if (s->authenticated && (option_verbose > 1) && displayconnects) {
02239 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
02240 s->username, ast_inet_ntoa(s->sin.sin_addr));
02241 }
02242 free_session(s);
02243 break;
02244 }
02245 }
02246 AST_LIST_TRAVERSE_SAFE_END
02247
02248
02249 eqe = master_eventq;
02250 while (master_eventq->next && !master_eventq->usecount) {
02251 eqe = master_eventq;
02252 master_eventq = master_eventq->next;
02253 free(eqe);
02254 }
02255 AST_LIST_UNLOCK(&sessions);
02256 if (s)
02257 ast_atomic_fetchadd_int(&num_sessions, -1);
02258
02259 sinlen = sizeof(sin);
02260 pfds[0].fd = asock;
02261 pfds[0].events = POLLIN;
02262
02263
02264 if (poll(pfds, 1, 5000) < 1)
02265 continue;
02266 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
02267 if (as < 0) {
02268 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
02269 continue;
02270 }
02271 p = getprotobyname("tcp");
02272 if (p) {
02273 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
02274 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
02275 }
02276 }
02277 if (!(s = ast_calloc(1, sizeof(*s))))
02278 continue;
02279
02280 ast_atomic_fetchadd_int(&num_sessions, 1);
02281
02282 memcpy(&s->sin, &sin, sizeof(sin));
02283 s->writetimeout = 100;
02284 s->waiting_thread = AST_PTHREADT_NULL;
02285
02286 if (!block_sockets) {
02287
02288 flags = fcntl(as, F_GETFL);
02289 fcntl(as, F_SETFL, flags | O_NONBLOCK);
02290 } else {
02291 flags = fcntl(as, F_GETFL);
02292 fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
02293 }
02294 ast_mutex_init(&s->__lock);
02295 s->fd = as;
02296 s->send_events = -1;
02297 AST_LIST_LOCK(&sessions);
02298 AST_LIST_INSERT_HEAD(&sessions, s, list);
02299
02300
02301 s->eventq = master_eventq;
02302 while(s->eventq->next)
02303 s->eventq = s->eventq->next;
02304 AST_LIST_UNLOCK(&sessions);
02305 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
02306 if (ast_pthread_create_background(&s->t, &attr, session_do, s))
02307 destroy_session(s);
02308 }
02309 pthread_attr_destroy(&attr);
02310 return NULL;
02311 }
02312
02313 static int append_event(const char *str, int category)
02314 {
02315 struct eventqent *tmp, *prev = NULL;
02316 tmp = ast_malloc(sizeof(*tmp) + strlen(str));
02317
02318 if (!tmp)
02319 return -1;
02320
02321 tmp->next = NULL;
02322 tmp->category = category;
02323 strcpy(tmp->eventdata, str);
02324
02325 if (master_eventq) {
02326 prev = master_eventq;
02327 while (prev->next)
02328 prev = prev->next;
02329 prev->next = tmp;
02330 } else {
02331 master_eventq = tmp;
02332 }
02333
02334 tmp->usecount = num_sessions;
02335
02336 return 0;
02337 }
02338
02339
02340 int manager_event(int category, const char *event, const char *fmt, ...)
02341 {
02342 struct mansession *s;
02343 char auth[80];
02344 va_list ap;
02345 struct timeval now;
02346 struct ast_dynamic_str *buf;
02347
02348
02349 if (!num_sessions)
02350 return 0;
02351
02352 if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
02353 return -1;
02354
02355 ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
02356 "Event: %s\r\nPrivilege: %s\r\n",
02357 event, authority_to_str(category, auth, sizeof(auth)));
02358
02359 if (timestampevents) {
02360 now = ast_tvnow();
02361 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
02362 "Timestamp: %ld.%06lu\r\n",
02363 now.tv_sec, (unsigned long) now.tv_usec);
02364 }
02365
02366 va_start(ap, fmt);
02367 ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
02368 va_end(ap);
02369
02370 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");
02371
02372
02373 AST_LIST_LOCK(&sessions);
02374 append_event(buf->str, category);
02375 AST_LIST_TRAVERSE(&sessions, s, list) {
02376 ast_mutex_lock(&s->__lock);
02377 if (s->waiting_thread != AST_PTHREADT_NULL)
02378 pthread_kill(s->waiting_thread, SIGURG);
02379 ast_mutex_unlock(&s->__lock);
02380 }
02381 AST_LIST_UNLOCK(&sessions);
02382
02383 return 0;
02384 }
02385
02386 int ast_manager_unregister(char *action)
02387 {
02388 struct manager_action *cur, *prev;
02389
02390 ast_rwlock_wrlock(&actionlock);
02391 cur = prev = first_action;
02392 while (cur) {
02393 if (!strcasecmp(action, cur->action)) {
02394 prev->next = cur->next;
02395 free(cur);
02396 if (option_verbose > 1)
02397 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
02398 ast_rwlock_unlock(&actionlock);
02399 return 0;
02400 }
02401 prev = cur;
02402 cur = cur->next;
02403 }
02404 ast_rwlock_unlock(&actionlock);
02405 return 0;
02406 }
02407
02408 static int manager_state_cb(char *context, char *exten, int state, void *data)
02409 {
02410
02411 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
02412 return 0;
02413 }
02414
02415 static int ast_manager_register_struct(struct manager_action *act)
02416 {
02417 struct manager_action *cur, *prev = NULL;
02418 int ret;
02419
02420 ast_rwlock_wrlock(&actionlock);
02421 cur = first_action;
02422 while (cur) {
02423 ret = strcasecmp(cur->action, act->action);
02424 if (ret == 0) {
02425 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
02426 ast_rwlock_unlock(&actionlock);
02427 return -1;
02428 } else if (ret > 0) {
02429
02430 if (prev) {
02431 act->next = prev->next;
02432 prev->next = act;
02433 } else {
02434 act->next = first_action;
02435 first_action = act;
02436 }
02437 break;
02438 }
02439 prev = cur;
02440 cur = cur->next;
02441 }
02442
02443 if (!cur) {
02444 if (prev)
02445 prev->next = act;
02446 else
02447 first_action = act;
02448 act->next = NULL;
02449 }
02450
02451 if (option_verbose > 1)
02452 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
02453 ast_rwlock_unlock(&actionlock);
02454 return 0;
02455 }
02456
02457
02458
02459 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
02460 {
02461 struct manager_action *cur;
02462
02463 cur = ast_malloc(sizeof(*cur));
02464 if (!cur)
02465 return -1;
02466
02467 cur->action = action;
02468 cur->authority = auth;
02469 cur->func = func;
02470 cur->synopsis = synopsis;
02471 cur->description = description;
02472 cur->next = NULL;
02473
02474 ast_manager_register_struct(cur);
02475
02476 return 0;
02477 }
02478
02479
02480
02481 static struct mansession *find_session(unsigned long ident)
02482 {
02483 struct mansession *s;
02484
02485 AST_LIST_LOCK(&sessions);
02486 AST_LIST_TRAVERSE(&sessions, s, list) {
02487 ast_mutex_lock(&s->__lock);
02488 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
02489 s->inuse++;
02490 break;
02491 }
02492 ast_mutex_unlock(&s->__lock);
02493 }
02494 AST_LIST_UNLOCK(&sessions);
02495
02496 return s;
02497 }
02498
02499 int astman_verify_session_readpermissions(unsigned long ident, int perm)
02500 {
02501 int result = 0;
02502 struct mansession *s;
02503
02504 AST_LIST_LOCK(&sessions);
02505 AST_LIST_TRAVERSE(&sessions, s, list) {
02506 ast_mutex_lock(&s->__lock);
02507 if ((s->managerid == ident) && (s->readperm & perm)) {
02508 result = 1;
02509 ast_mutex_unlock(&s->__lock);
02510 break;
02511 }
02512 ast_mutex_unlock(&s->__lock);
02513 }
02514 AST_LIST_UNLOCK(&sessions);
02515 return result;
02516 }
02517
02518 int astman_verify_session_writepermissions(unsigned long ident, int perm)
02519 {
02520 int result = 0;
02521 struct mansession *s;
02522
02523 AST_LIST_LOCK(&sessions);
02524 AST_LIST_TRAVERSE(&sessions, s, list) {
02525 ast_mutex_lock(&s->__lock);
02526 if ((s->managerid == ident) && (s->writeperm & perm)) {
02527 result = 1;
02528 ast_mutex_unlock(&s->__lock);
02529 break;
02530 }
02531 ast_mutex_unlock(&s->__lock);
02532 }
02533 AST_LIST_UNLOCK(&sessions);
02534 return result;
02535 }
02536
02537 enum {
02538 FORMAT_RAW,
02539 FORMAT_HTML,
02540 FORMAT_XML,
02541 };
02542 static char *contenttype[] = { "plain", "html", "xml" };
02543
02544 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
02545 {
02546 struct mansession *s = NULL;
02547 unsigned long ident = 0;
02548 char workspace[512];
02549 char cookie[128];
02550 size_t len = sizeof(workspace);
02551 int blastaway = 0;
02552 char *c = workspace;
02553 char *retval = NULL;
02554 struct ast_variable *v;
02555
02556 for (v = params; v; v = v->next) {
02557 if (!strcasecmp(v->name, "mansession_id")) {
02558 sscanf(v->value, "%lx", &ident);
02559 break;
02560 }
02561 }
02562
02563 if (!(s = find_session(ident))) {
02564
02565 if (!(s = ast_calloc(1, sizeof(*s)))) {
02566 *status = 500;
02567 goto generic_callback_out;
02568 }
02569 memcpy(&s->sin, requestor, sizeof(s->sin));
02570 s->fd = -1;
02571 s->waiting_thread = AST_PTHREADT_NULL;
02572 s->send_events = 0;
02573 ast_mutex_init(&s->__lock);
02574 ast_mutex_lock(&s->__lock);
02575 s->inuse = 1;
02576 s->managerid = rand() | (unsigned long)s;
02577 AST_LIST_LOCK(&sessions);
02578 AST_LIST_INSERT_HEAD(&sessions, s, list);
02579
02580 s->eventq = master_eventq;
02581 while (s->eventq->next)
02582 s->eventq = s->eventq->next;
02583 AST_LIST_UNLOCK(&sessions);
02584 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
02585 ast_atomic_fetchadd_int(&num_sessions, 1);
02586 }
02587
02588
02589 time(&s->sessiontimeout);
02590 if (!s->authenticated && (httptimeout > 5))
02591 s->sessiontimeout += 5;
02592 else
02593 s->sessiontimeout += httptimeout;
02594 ast_mutex_unlock(&s->__lock);
02595
02596 if (s) {
02597 struct message m = { 0 };
02598 char tmp[80];
02599 unsigned int x;
02600 size_t hdrlen;
02601
02602 for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
02603 hdrlen = strlen(v->name) + strlen(v->value) + 3;
02604 m.headers[m.hdrcount] = alloca(hdrlen);
02605 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
02606 m.hdrcount = x + 1;
02607 }
02608
02609 if (process_message(s, &m)) {
02610 if (s->authenticated) {
02611 if (option_verbose > 1) {
02612 if (displayconnects)
02613 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
02614 }
02615 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
02616 } else {
02617 if (option_verbose > 1) {
02618 if (displayconnects)
02619 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
02620 }
02621 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
02622 }
02623 s->needdestroy = 1;
02624 }
02625 ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
02626 sprintf(tmp, "%08lx", s->managerid);
02627 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
02628 if (format == FORMAT_HTML)
02629 ast_build_string(&c, &len, "<title>Asterisk™ Manager Interface</title>");
02630 if (format == FORMAT_XML) {
02631 ast_build_string(&c, &len, "<ajax-response>\n");
02632 } else if (format == FORMAT_HTML) {
02633 ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
02634 ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1> Manager Tester</h1></td></tr>\r\n");
02635 }
02636 ast_mutex_lock(&s->__lock);
02637 if (s->outputstr) {
02638 char *tmp;
02639 if (format == FORMAT_XML)
02640 tmp = xml_translate(s->outputstr->str, params);
02641 else if (format == FORMAT_HTML)
02642 tmp = html_translate(s->outputstr->str);
02643 else
02644 tmp = s->outputstr->str;
02645 if (tmp) {
02646 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
02647 if (retval) {
02648 strcpy(retval, workspace);
02649 strcpy(retval + strlen(retval), tmp);
02650 c = retval + strlen(retval);
02651 len = 120;
02652 }
02653 }
02654 if (tmp != s->outputstr->str)
02655 free(tmp);
02656 free(s->outputstr);
02657 s->outputstr = NULL;
02658 }
02659 ast_mutex_unlock(&s->__lock);
02660
02661
02662 if (format == FORMAT_XML) {
02663 ast_build_string(&c, &len, "</ajax-response>\n");
02664 } else if (format == FORMAT_HTML)
02665 ast_build_string(&c, &len, "</table></body>\r\n");
02666 } else {
02667 *status = 500;
02668 *title = strdup("Server Error");
02669 }
02670 ast_mutex_lock(&s->__lock);
02671 if (s->needdestroy) {
02672 if (s->inuse == 1) {
02673 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
02674 blastaway = 1;
02675 } else {
02676 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
02677 if (s->waiting_thread != AST_PTHREADT_NULL)
02678 pthread_kill(s->waiting_thread, SIGURG);
02679 s->inuse--;
02680 }
02681 } else
02682 s->inuse--;
02683 ast_mutex_unlock(&s->__lock);
02684
02685 if (blastaway)
02686 destroy_session(s);
02687 generic_callback_out:
02688 if (*status != 200)
02689 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
02690 return retval;
02691 }
02692
02693 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
02694 {
02695 return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
02696 }
02697
02698 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
02699 {
02700 return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
02701 }
02702
02703 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
02704 {
02705 return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
02706 }
02707
02708 struct ast_http_uri rawmanuri = {
02709 .description = "Raw HTTP Manager Event Interface",
02710 .uri = "rawman",
02711 .has_subtree = 0,
02712 .callback = rawman_http_callback,
02713 };
02714
02715 struct ast_http_uri manageruri = {
02716 .description = "HTML Manager Event Interface",
02717 .uri = "manager",
02718 .has_subtree = 0,
02719 .callback = manager_http_callback,
02720 };
02721
02722 struct ast_http_uri managerxmluri = {
02723 .description = "XML Manager Event Interface",
02724 .uri = "mxml",
02725 .has_subtree = 0,
02726 .callback = mxml_http_callback,
02727 };
02728
02729 static int registered = 0;
02730 static int webregged = 0;
02731
02732 int init_manager(void)
02733 {
02734 struct ast_config *cfg = NULL;
02735 const char *val;
02736 char *cat = NULL;
02737 int oldportno = portno;
02738 static struct sockaddr_in ba;
02739 int x = 1;
02740 int flags;
02741 int webenabled = 0;
02742 int newhttptimeout = 60;
02743 struct ast_manager_user *user = NULL;
02744
02745 if (!registered) {
02746
02747 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
02748 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
02749 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
02750 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
02751 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
02752 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
02753 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
02754 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
02755 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
02756 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
02757 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
02758 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
02759 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
02760 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
02761 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
02762 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
02763 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
02764 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
02765 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
02766
02767 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
02768 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
02769 registered = 1;
02770
02771 append_event("Event: Placeholder\r\n\r\n", 0);
02772 }
02773 portno = DEFAULT_MANAGER_PORT;
02774 displayconnects = 1;
02775 cfg = ast_config_load("manager.conf");
02776 if (!cfg) {
02777 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n");
02778 return 0;
02779 }
02780 val = ast_variable_retrieve(cfg, "general", "enabled");
02781 if (val)
02782 enabled = ast_true(val);
02783
02784 val = ast_variable_retrieve(cfg, "general", "block-sockets");
02785 if (val)
02786 block_sockets = ast_true(val);
02787
02788 val = ast_variable_retrieve(cfg, "general", "webenabled");
02789 if (val)
02790 webenabled = ast_true(val);
02791
02792 if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
02793 if (sscanf(val, "%d", &portno) != 1) {
02794 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
02795 portno = DEFAULT_MANAGER_PORT;
02796 }
02797 }
02798
02799 if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
02800 displayconnects = ast_true(val);
02801
02802 if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
02803 timestampevents = ast_true(val);
02804
02805 if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
02806 newhttptimeout = atoi(val);
02807
02808 memset(&ba, 0, sizeof(ba));
02809 ba.sin_family = AF_INET;
02810 ba.sin_port = htons(portno);
02811
02812 if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
02813 if (!inet_aton(val, &ba.sin_addr)) {
02814 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
02815 memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
02816 }
02817 }
02818
02819
02820 if ((asock > -1) && ((portno != oldportno) || !enabled)) {
02821 #if 0
02822
02823 close(asock);
02824 asock = -1;
02825 #else
02826 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
02827 #endif
02828 }
02829
02830 AST_LIST_LOCK(&users);
02831
02832 while ((cat = ast_category_browse(cfg, cat))) {
02833 struct ast_variable *var = NULL;
02834
02835 if (!strcasecmp(cat, "general"))
02836 continue;
02837
02838
02839 if (!(user = ast_get_manager_by_name_locked(cat))) {
02840 if (!(user = ast_calloc(1, sizeof(*user))))
02841 break;
02842
02843 ast_copy_string(user->username, cat, sizeof(user->username));
02844
02845 AST_LIST_INSERT_TAIL(&users, user, list);
02846 }
02847
02848
02849 user->keep = 1;
02850
02851 var = ast_variable_browse(cfg, cat);
02852 while (var) {
02853 if (!strcasecmp(var->name, "secret")) {
02854 if (user->secret)
02855 free(user->secret);
02856 user->secret = ast_strdup(var->value);
02857 } else if (!strcasecmp(var->name, "deny") ) {
02858 if (user->deny)
02859 free(user->deny);
02860 user->deny = ast_strdup(var->value);
02861 } else if (!strcasecmp(var->name, "permit") ) {
02862 if (user->permit)
02863 free(user->permit);
02864 user->permit = ast_strdup(var->value);
02865 } else if (!strcasecmp(var->name, "read") ) {
02866 if (user->read)
02867 free(user->read);
02868 user->read = ast_strdup(var->value);
02869 } else if (!strcasecmp(var->name, "write") ) {
02870 if (user->write)
02871 free(user->write);
02872 user->write = ast_strdup(var->value);
02873 } else if (!strcasecmp(var->name, "displayconnects") )
02874 user->displayconnects = ast_true(var->value);
02875 else
02876 ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
02877 var = var->next;
02878 }
02879 }
02880
02881
02882 AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
02883 if (user->keep) {
02884 user->keep = 0;
02885 continue;
02886 }
02887
02888 AST_LIST_REMOVE_CURRENT(&users, list);
02889
02890 if (user->secret)
02891 free(user->secret);
02892 if (user->deny)
02893 free(user->deny);
02894 if (user->permit)
02895 free(user->permit);
02896 if (user->read)
02897 free(user->read);
02898 if (user->write)
02899 free(user->write);
02900 free(user);
02901 }
02902 AST_LIST_TRAVERSE_SAFE_END
02903
02904 AST_LIST_UNLOCK(&users);
02905
02906 ast_config_destroy(cfg);
02907
02908 if (webenabled && enabled) {
02909 if (!webregged) {
02910 ast_http_uri_link(&rawmanuri);
02911 ast_http_uri_link(&manageruri);
02912 ast_http_uri_link(&managerxmluri);
02913 webregged = 1;
02914 }
02915 } else {
02916 if (webregged) {
02917 ast_http_uri_unlink(&rawmanuri);
02918 ast_http_uri_unlink(&manageruri);
02919 ast_http_uri_unlink(&managerxmluri);
02920 webregged = 0;
02921 }
02922 }
02923
02924 if (newhttptimeout > 0)
02925 httptimeout = newhttptimeout;
02926
02927
02928 if (!enabled)
02929 return 0;
02930
02931 if (asock < 0) {
02932 asock = socket(AF_INET, SOCK_STREAM, 0);
02933 if (asock < 0) {
02934 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
02935 return -1;
02936 }
02937 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
02938 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
02939 ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
02940 close(asock);
02941 asock = -1;
02942 return -1;
02943 }
02944 if (listen(asock, 2)) {
02945 ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
02946 close(asock);
02947 asock = -1;
02948 return -1;
02949 }
02950 flags = fcntl(asock, F_GETFL);
02951 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
02952 if (option_verbose)
02953 ast_verbose("Asterisk Management interface listening on port %d\n", portno);
02954 ast_pthread_create_background(&t, NULL, accept_thread, NULL);
02955 }
02956 return 0;
02957 }
02958
02959 int reload_manager(void)
02960 {
02961 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
02962 return init_manager();
02963 }