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