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