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