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