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