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