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