00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044 #include "asterisk.h"
00045
00046 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 257184 $")
00047
00048 #include "asterisk/_private.h"
00049 #include "asterisk/paths.h"
00050 #include <ctype.h>
00051 #include <sys/time.h>
00052 #include <signal.h>
00053 #include <sys/mman.h>
00054
00055 #include "asterisk/channel.h"
00056 #include "asterisk/file.h"
00057 #include "asterisk/manager.h"
00058 #include "asterisk/module.h"
00059 #include "asterisk/config.h"
00060 #include "asterisk/callerid.h"
00061 #include "asterisk/lock.h"
00062 #include "asterisk/cli.h"
00063 #include "asterisk/app.h"
00064 #include "asterisk/pbx.h"
00065 #include "asterisk/md5.h"
00066 #include "asterisk/acl.h"
00067 #include "asterisk/utils.h"
00068 #include "asterisk/tcptls.h"
00069 #include "asterisk/http.h"
00070 #include "asterisk/ast_version.h"
00071 #include "asterisk/threadstorage.h"
00072 #include "asterisk/linkedlists.h"
00073 #include "asterisk/version.h"
00074 #include "asterisk/term.h"
00075 #include "asterisk/astobj2.h"
00076 #include "asterisk/features.h"
00077
00078 enum error_type {
00079 UNKNOWN_ACTION = 1,
00080 UNKNOWN_CATEGORY,
00081 UNSPECIFIED_CATEGORY,
00082 UNSPECIFIED_ARGUMENT,
00083 FAILURE_ALLOCATION,
00084 FAILURE_NEWCAT,
00085 FAILURE_DELCAT,
00086 FAILURE_EMPTYCAT,
00087 FAILURE_UPDATE,
00088 FAILURE_DELETE,
00089 FAILURE_APPEND
00090 };
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112 struct eventqent {
00113 int usecount;
00114 int category;
00115 unsigned int seq;
00116 AST_LIST_ENTRY(eventqent) eq_next;
00117 char eventdata[1];
00118 };
00119
00120 static AST_LIST_HEAD_STATIC(all_events, eventqent);
00121
00122 static int displayconnects = 1;
00123 static int allowmultiplelogin = 1;
00124 static int timestampevents;
00125 static int httptimeout = 60;
00126 static int broken_events_action = 0;
00127 static int manager_enabled = 0;
00128 static int webmanager_enabled = 0;
00129
00130 static int block_sockets;
00131 static int num_sessions;
00132
00133 static int manager_debug;
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144 #define MAX_BLACKLIST_CMD_LEN 2
00145 static struct {
00146 char *words[AST_MAX_CMD_LEN];
00147 } command_blacklist[] = {
00148 {{ "module", "load", NULL }},
00149 {{ "module", "unload", NULL }},
00150 {{ "restart", "gracefully", NULL }},
00151 };
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185 struct mansession_session {
00186 pthread_t ms_t;
00187 ast_mutex_t __lock;
00188
00189 struct sockaddr_in sin;
00190 FILE *f;
00191 int fd;
00192 int inuse;
00193 int needdestroy;
00194 pthread_t waiting_thread;
00195 uint32_t managerid;
00196 time_t sessionstart;
00197 time_t sessiontimeout;
00198 char username[80];
00199 char challenge[10];
00200 int authenticated;
00201 int readperm;
00202 int writeperm;
00203 char inbuf[1025];
00204
00205 int inlen;
00206 int send_events;
00207 struct eventqent *last_ev;
00208 int writetimeout;
00209 int pending_event;
00210 AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores;
00211 AST_LIST_ENTRY(mansession_session) list;
00212 };
00213
00214
00215
00216
00217
00218
00219 struct mansession {
00220 struct mansession_session *session;
00221 FILE *f;
00222 int fd;
00223 };
00224
00225 #define NEW_EVENT(m) (AST_LIST_NEXT(m->session->last_ev, eq_next))
00226
00227 static AST_LIST_HEAD_STATIC(sessions, mansession_session);
00228
00229
00230
00231
00232
00233
00234
00235 struct ast_manager_user {
00236 char username[80];
00237 char *secret;
00238 struct ast_ha *ha;
00239 int readperm;
00240 int writeperm;
00241 int writetimeout;
00242 int displayconnects;
00243 int keep;
00244 AST_RWLIST_ENTRY(ast_manager_user) list;
00245 };
00246
00247
00248 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
00249
00250
00251 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
00252
00253
00254 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
00255
00256
00257 void ast_manager_register_hook(struct manager_custom_hook *hook)
00258 {
00259 AST_RWLIST_WRLOCK(&manager_hooks);
00260 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
00261 AST_RWLIST_UNLOCK(&manager_hooks);
00262 return;
00263 }
00264
00265
00266 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
00267 {
00268 AST_RWLIST_WRLOCK(&manager_hooks);
00269 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
00270 AST_RWLIST_UNLOCK(&manager_hooks);
00271 return;
00272 }
00273
00274
00275
00276
00277
00278
00279
00280 #if 0
00281 static time_t __deb(time_t start, const char *msg)
00282 {
00283 time_t now = time(NULL);
00284 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
00285 if (start != 0 && now - start > 5)
00286 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
00287 return now;
00288 }
00289
00290 static void LOCK_EVENTS(void)
00291 {
00292 time_t start = __deb(0, "about to lock events");
00293 AST_LIST_LOCK(&all_events);
00294 __deb(start, "done lock events");
00295 }
00296
00297 static void UNLOCK_EVENTS(void)
00298 {
00299 __deb(0, "about to unlock events");
00300 AST_LIST_UNLOCK(&all_events);
00301 }
00302
00303 static void LOCK_SESS(void)
00304 {
00305 time_t start = __deb(0, "about to lock sessions");
00306 AST_LIST_LOCK(&sessions);
00307 __deb(start, "done lock sessions");
00308 }
00309
00310 static void UNLOCK_SESS(void)
00311 {
00312 __deb(0, "about to unlock sessions");
00313 AST_LIST_UNLOCK(&sessions);
00314 }
00315 #endif
00316
00317 int check_manager_enabled()
00318 {
00319 return manager_enabled;
00320 }
00321
00322 int check_webmanager_enabled()
00323 {
00324 return (webmanager_enabled && manager_enabled);
00325 }
00326
00327
00328
00329
00330
00331 static struct eventqent *grab_last(void)
00332 {
00333 struct eventqent *ret;
00334
00335 AST_LIST_LOCK(&all_events);
00336 ret = AST_LIST_LAST(&all_events);
00337
00338
00339
00340 if (ret)
00341 ast_atomic_fetchadd_int(&ret->usecount, 1);
00342 AST_LIST_UNLOCK(&all_events);
00343 return ret;
00344 }
00345
00346
00347
00348
00349
00350 static void purge_events(void)
00351 {
00352 struct eventqent *ev;
00353
00354 AST_LIST_LOCK(&all_events);
00355 while ( (ev = AST_LIST_FIRST(&all_events)) &&
00356 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
00357 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
00358 ast_free(ev);
00359 }
00360 AST_LIST_UNLOCK(&all_events);
00361 }
00362
00363
00364
00365
00366
00367 static struct permalias {
00368 int num;
00369 char *label;
00370 } perms[] = {
00371 { EVENT_FLAG_SYSTEM, "system" },
00372 { EVENT_FLAG_CALL, "call" },
00373 { EVENT_FLAG_LOG, "log" },
00374 { EVENT_FLAG_VERBOSE, "verbose" },
00375 { EVENT_FLAG_COMMAND, "command" },
00376 { EVENT_FLAG_AGENT, "agent" },
00377 { EVENT_FLAG_USER, "user" },
00378 { EVENT_FLAG_CONFIG, "config" },
00379 { EVENT_FLAG_DTMF, "dtmf" },
00380 { EVENT_FLAG_REPORTING, "reporting" },
00381 { EVENT_FLAG_CDR, "cdr" },
00382 { EVENT_FLAG_DIALPLAN, "dialplan" },
00383 { EVENT_FLAG_ORIGINATE, "originate" },
00384 { EVENT_FLAG_AGI, "agi" },
00385 { INT_MAX, "all" },
00386 { 0, "none" },
00387 };
00388
00389
00390 static char *authority_to_str(int authority, struct ast_str **res)
00391 {
00392 int i;
00393 char *sep = "";
00394
00395 ast_str_reset(*res);
00396 for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
00397 if (authority & perms[i].num) {
00398 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
00399 sep = ",";
00400 }
00401 }
00402
00403 if (ast_str_strlen(*res) == 0)
00404 ast_str_append(res, 0, "<none>");
00405
00406 return ast_str_buffer(*res);
00407 }
00408
00409
00410
00411
00412
00413
00414 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
00415 {
00416 const char *val = bigstr, *next;
00417
00418 do {
00419 if ((next = strchr(val, delim))) {
00420 if (!strncmp(val, smallstr, (next - val)))
00421 return 1;
00422 else
00423 continue;
00424 } else
00425 return !strcmp(smallstr, val);
00426 } while (*(val = (next + 1)));
00427
00428 return 0;
00429 }
00430
00431 static int get_perm(const char *instr)
00432 {
00433 int x = 0, ret = 0;
00434
00435 if (!instr)
00436 return 0;
00437
00438 for (x = 0; x < ARRAY_LEN(perms); x++) {
00439 if (ast_instring(instr, perms[x].label, ','))
00440 ret |= perms[x].num;
00441 }
00442
00443 return ret;
00444 }
00445
00446
00447
00448
00449
00450 static int strings_to_mask(const char *string)
00451 {
00452 const char *p;
00453
00454 if (ast_strlen_zero(string))
00455 return -1;
00456
00457 for (p = string; *p; p++)
00458 if (*p < '0' || *p > '9')
00459 break;
00460 if (!*p)
00461 return atoi(string);
00462 if (ast_false(string))
00463 return 0;
00464 if (ast_true(string)) {
00465 int x, ret = 0;
00466 for (x = 0; x < ARRAY_LEN(perms); x++)
00467 ret |= perms[x].num;
00468 return ret;
00469 }
00470 return get_perm(string);
00471 }
00472
00473 static int check_manager_session_inuse(const char *name)
00474 {
00475 struct mansession_session *session = NULL;
00476
00477 AST_LIST_LOCK(&sessions);
00478 AST_LIST_TRAVERSE(&sessions, session, list) {
00479 if (!strcasecmp(session->username, name))
00480 break;
00481 }
00482 AST_LIST_UNLOCK(&sessions);
00483
00484 return session ? 1 : 0;
00485 }
00486
00487
00488
00489
00490
00491
00492 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
00493 {
00494 struct ast_manager_user *user = NULL;
00495
00496 AST_RWLIST_TRAVERSE(&users, user, list)
00497 if (!strcasecmp(user->username, name))
00498 break;
00499 return user;
00500 }
00501
00502
00503
00504
00505
00506 static int manager_displayconnects (struct mansession_session *session)
00507 {
00508 struct ast_manager_user *user = NULL;
00509 int ret = 0;
00510
00511 AST_RWLIST_RDLOCK(&users);
00512 if ((user = get_manager_by_name_locked (session->username)))
00513 ret = user->displayconnects;
00514 AST_RWLIST_UNLOCK(&users);
00515
00516 return ret;
00517 }
00518
00519 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00520 {
00521 struct manager_action *cur;
00522 struct ast_str *authority;
00523 int num, l, which;
00524 char *ret = NULL;
00525 switch (cmd) {
00526 case CLI_INIT:
00527 e->command = "manager show command";
00528 e->usage =
00529 "Usage: manager show command <actionname> [<actionname> [<actionname> [...]]]\n"
00530 " Shows the detailed description for a specific Asterisk manager interface command.\n";
00531 return NULL;
00532 case CLI_GENERATE:
00533 l = strlen(a->word);
00534 which = 0;
00535 AST_RWLIST_RDLOCK(&actions);
00536 AST_RWLIST_TRAVERSE(&actions, cur, list) {
00537 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
00538 ret = ast_strdup(cur->action);
00539 break;
00540 }
00541 }
00542 AST_RWLIST_UNLOCK(&actions);
00543 return ret;
00544 }
00545 authority = ast_str_alloca(80);
00546 if (a->argc < 4) {
00547 return CLI_SHOWUSAGE;
00548 }
00549
00550 AST_RWLIST_RDLOCK(&actions);
00551 AST_RWLIST_TRAVERSE(&actions, cur, list) {
00552 for (num = 3; num < a->argc; num++) {
00553 if (!strcasecmp(cur->action, a->argv[num])) {
00554 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
00555 cur->action, cur->synopsis,
00556 authority_to_str(cur->authority, &authority),
00557 S_OR(cur->description, ""));
00558 }
00559 }
00560 }
00561 AST_RWLIST_UNLOCK(&actions);
00562
00563 return CLI_SUCCESS;
00564 }
00565
00566 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00567 {
00568 switch (cmd) {
00569 case CLI_INIT:
00570 e->command = "manager set debug [on|off]";
00571 e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
00572 return NULL;
00573 case CLI_GENERATE:
00574 return NULL;
00575 }
00576 if (a->argc == 3)
00577 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
00578 else if (a->argc == 4) {
00579 if (!strcasecmp(a->argv[3], "on"))
00580 manager_debug = 1;
00581 else if (!strcasecmp(a->argv[3], "off"))
00582 manager_debug = 0;
00583 else
00584 return CLI_SHOWUSAGE;
00585 }
00586 return CLI_SUCCESS;
00587 }
00588
00589 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00590 {
00591 struct ast_manager_user *user = NULL;
00592 int l, which;
00593 char *ret = NULL;
00594 struct ast_str *rauthority = ast_str_alloca(128);
00595 struct ast_str *wauthority = ast_str_alloca(128);
00596
00597 switch (cmd) {
00598 case CLI_INIT:
00599 e->command = "manager show user";
00600 e->usage =
00601 " Usage: manager show user <user>\n"
00602 " Display all information related to the manager user specified.\n";
00603 return NULL;
00604 case CLI_GENERATE:
00605 l = strlen(a->word);
00606 which = 0;
00607 if (a->pos != 3)
00608 return NULL;
00609 AST_RWLIST_RDLOCK(&users);
00610 AST_RWLIST_TRAVERSE(&users, user, list) {
00611 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
00612 ret = ast_strdup(user->username);
00613 break;
00614 }
00615 }
00616 AST_RWLIST_UNLOCK(&users);
00617 return ret;
00618 }
00619
00620 if (a->argc != 4)
00621 return CLI_SHOWUSAGE;
00622
00623 AST_RWLIST_RDLOCK(&users);
00624
00625 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
00626 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
00627 AST_RWLIST_UNLOCK(&users);
00628 return CLI_SUCCESS;
00629 }
00630
00631 ast_cli(a->fd, "\n");
00632 ast_cli(a->fd,
00633 " username: %s\n"
00634 " secret: %s\n"
00635 " acl: %s\n"
00636 " read perm: %s\n"
00637 " write perm: %s\n"
00638 "displayconnects: %s\n",
00639 (user->username ? user->username : "(N/A)"),
00640 (user->secret ? "<Set>" : "(N/A)"),
00641 (user->ha ? "yes" : "no"),
00642 authority_to_str(user->readperm, &rauthority),
00643 authority_to_str(user->writeperm, &wauthority),
00644 (user->displayconnects ? "yes" : "no"));
00645
00646 AST_RWLIST_UNLOCK(&users);
00647
00648 return CLI_SUCCESS;
00649 }
00650
00651
00652 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00653 {
00654 struct ast_manager_user *user = NULL;
00655 int count_amu = 0;
00656 switch (cmd) {
00657 case CLI_INIT:
00658 e->command = "manager show users";
00659 e->usage =
00660 "Usage: manager show users\n"
00661 " Prints a listing of all managers that are currently configured on that\n"
00662 " system.\n";
00663 return NULL;
00664 case CLI_GENERATE:
00665 return NULL;
00666 }
00667 if (a->argc != 3)
00668 return CLI_SHOWUSAGE;
00669
00670 AST_RWLIST_RDLOCK(&users);
00671
00672
00673 if (AST_RWLIST_EMPTY(&users)) {
00674 ast_cli(a->fd, "There are no manager users.\n");
00675 AST_RWLIST_UNLOCK(&users);
00676 return CLI_SUCCESS;
00677 }
00678
00679 ast_cli(a->fd, "\nusername\n--------\n");
00680
00681 AST_RWLIST_TRAVERSE(&users, user, list) {
00682 ast_cli(a->fd, "%s\n", user->username);
00683 count_amu++;
00684 }
00685
00686 AST_RWLIST_UNLOCK(&users);
00687
00688 ast_cli(a->fd, "-------------------\n");
00689 ast_cli(a->fd, "%d manager users configured.\n", count_amu);
00690
00691 return CLI_SUCCESS;
00692 }
00693
00694
00695
00696 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00697 {
00698 struct manager_action *cur;
00699 struct ast_str *authority;
00700 #define HSMC_FORMAT " %-15.15s %-15.15s %-55.55s\n"
00701 switch (cmd) {
00702 case CLI_INIT:
00703 e->command = "manager show commands";
00704 e->usage =
00705 "Usage: manager show commands\n"
00706 " Prints a listing of all the available Asterisk manager interface commands.\n";
00707 return NULL;
00708 case CLI_GENERATE:
00709 return NULL;
00710 }
00711 authority = ast_str_alloca(80);
00712 ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis");
00713 ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------");
00714
00715 AST_RWLIST_RDLOCK(&actions);
00716 AST_RWLIST_TRAVERSE(&actions, cur, list)
00717 ast_cli(a->fd, HSMC_FORMAT, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
00718 AST_RWLIST_UNLOCK(&actions);
00719
00720 return CLI_SUCCESS;
00721 }
00722
00723
00724 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00725 {
00726 struct mansession_session *session;
00727 time_t now = time(NULL);
00728 #define HSMCONN_FORMAT1 " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
00729 #define HSMCONN_FORMAT2 " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
00730 int count = 0;
00731 switch (cmd) {
00732 case CLI_INIT:
00733 e->command = "manager show connected";
00734 e->usage =
00735 "Usage: manager show connected\n"
00736 " Prints a listing of the users that are currently connected to the\n"
00737 "Asterisk manager interface.\n";
00738 return NULL;
00739 case CLI_GENERATE:
00740 return NULL;
00741 }
00742
00743 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
00744
00745 AST_LIST_LOCK(&sessions);
00746 AST_LIST_TRAVERSE(&sessions, session, list) {
00747 ast_cli(a->fd, HSMCONN_FORMAT2, session->username, ast_inet_ntoa(session->sin.sin_addr), (int)(session->sessionstart), (int)(now - session->sessionstart), session->fd, session->inuse, session->readperm, session->writeperm);
00748 count++;
00749 }
00750 AST_LIST_UNLOCK(&sessions);
00751
00752 ast_cli(a->fd, "%d users connected.\n", count);
00753
00754 return CLI_SUCCESS;
00755 }
00756
00757
00758
00759 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00760 {
00761 struct eventqent *s;
00762 switch (cmd) {
00763 case CLI_INIT:
00764 e->command = "manager show eventq";
00765 e->usage =
00766 "Usage: manager show eventq\n"
00767 " Prints a listing of all events pending in the Asterisk manger\n"
00768 "event queue.\n";
00769 return NULL;
00770 case CLI_GENERATE:
00771 return NULL;
00772 }
00773 AST_LIST_LOCK(&all_events);
00774 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
00775 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
00776 ast_cli(a->fd, "Category: %d\n", s->category);
00777 ast_cli(a->fd, "Event:\n%s", s->eventdata);
00778 }
00779 AST_LIST_UNLOCK(&all_events);
00780
00781 return CLI_SUCCESS;
00782 }
00783
00784
00785 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00786 {
00787 switch (cmd) {
00788 case CLI_INIT:
00789 e->command = "manager reload";
00790 e->usage =
00791 "Usage: manager reload\n"
00792 " Reloads the manager configuration.\n";
00793 return NULL;
00794 case CLI_GENERATE:
00795 return NULL;
00796 }
00797 if (a->argc > 2)
00798 return CLI_SHOWUSAGE;
00799 reload_manager();
00800 return CLI_SUCCESS;
00801 }
00802
00803
00804 static struct ast_cli_entry cli_manager[] = {
00805 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
00806 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
00807 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
00808 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
00809 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
00810 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
00811 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
00812 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
00813 };
00814
00815
00816
00817
00818
00819
00820
00821 static struct eventqent *unref_event(struct eventqent *e)
00822 {
00823 ast_atomic_fetchadd_int(&e->usecount, -1);
00824 return AST_LIST_NEXT(e, eq_next);
00825 }
00826
00827 static void ref_event(struct eventqent *e)
00828 {
00829 ast_atomic_fetchadd_int(&e->usecount, 1);
00830 }
00831
00832
00833
00834
00835 static void free_session(struct mansession_session *session)
00836 {
00837 struct eventqent *eqe = session->last_ev;
00838 struct ast_datastore *datastore;
00839
00840
00841 while ((datastore = AST_LIST_REMOVE_HEAD(&session->datastores, entry))) {
00842
00843 ast_datastore_free(datastore);
00844 }
00845
00846 if (session->f != NULL)
00847 fclose(session->f);
00848 ast_mutex_destroy(&session->__lock);
00849 ast_free(session);
00850 unref_event(eqe);
00851 }
00852
00853 static void destroy_session(struct mansession_session *session)
00854 {
00855 AST_LIST_LOCK(&sessions);
00856 AST_LIST_REMOVE(&sessions, session, list);
00857 ast_atomic_fetchadd_int(&num_sessions, -1);
00858 free_session(session);
00859 AST_LIST_UNLOCK(&sessions);
00860 }
00861
00862
00863
00864
00865
00866
00867
00868 #define GET_HEADER_FIRST_MATCH 0
00869 #define GET_HEADER_LAST_MATCH 1
00870 #define GET_HEADER_SKIP_EMPTY 2
00871 static const char *__astman_get_header(const struct message *m, char *var, int mode)
00872 {
00873 int x, l = strlen(var);
00874 const char *result = "";
00875
00876 for (x = 0; x < m->hdrcount; x++) {
00877 const char *h = m->headers[x];
00878 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ') {
00879 const char *value = h + l + 2;
00880
00881 if (mode & GET_HEADER_SKIP_EMPTY && ast_strlen_zero(value))
00882 continue;
00883 if (mode & GET_HEADER_LAST_MATCH)
00884 result = value;
00885 else
00886 return value;
00887 }
00888 }
00889
00890 return "";
00891 }
00892
00893
00894
00895
00896
00897
00898 const char *astman_get_header(const struct message *m, char *var)
00899 {
00900 return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
00901 }
00902
00903
00904 struct ast_variable *astman_get_variables(const struct message *m)
00905 {
00906 int varlen, x, y;
00907 struct ast_variable *head = NULL, *cur;
00908
00909 AST_DECLARE_APP_ARGS(args,
00910 AST_APP_ARG(vars)[32];
00911 );
00912
00913 varlen = strlen("Variable: ");
00914
00915 for (x = 0; x < m->hdrcount; x++) {
00916 char *parse, *var, *val;
00917
00918 if (strncasecmp("Variable: ", m->headers[x], varlen))
00919 continue;
00920 parse = ast_strdupa(m->headers[x] + varlen);
00921
00922 AST_STANDARD_APP_ARGS(args, parse);
00923 if (!args.argc)
00924 continue;
00925 for (y = 0; y < args.argc; y++) {
00926 if (!args.vars[y])
00927 continue;
00928 var = val = ast_strdupa(args.vars[y]);
00929 strsep(&val, "=");
00930 if (!val || ast_strlen_zero(var))
00931 continue;
00932 cur = ast_variable_new(var, val, "");
00933 cur->next = head;
00934 head = cur;
00935 }
00936 }
00937
00938 return head;
00939 }
00940
00941
00942
00943
00944
00945 static int send_string(struct mansession *s, char *string)
00946 {
00947 if (s->f) {
00948 return ast_careful_fwrite(s->f, s->fd, string, strlen(string), s->session->writetimeout);
00949 } else {
00950 return ast_careful_fwrite(s->session->f, s->session->fd, string, strlen(string), s->session->writetimeout);
00951 }
00952 }
00953
00954
00955
00956
00957
00958
00959
00960
00961 AST_THREADSTORAGE(astman_append_buf);
00962 AST_THREADSTORAGE(userevent_buf);
00963
00964
00965 #define ASTMAN_APPEND_BUF_INITSIZE 256
00966
00967
00968
00969
00970 void astman_append(struct mansession *s, const char *fmt, ...)
00971 {
00972 va_list ap;
00973 struct ast_str *buf;
00974
00975 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
00976 return;
00977
00978 va_start(ap, fmt);
00979 ast_str_set_va(&buf, 0, fmt, ap);
00980 va_end(ap);
00981
00982 if (s->f != NULL || s->session->f != NULL) {
00983 send_string(s, ast_str_buffer(buf));
00984 } else {
00985 ast_verbose("fd == -1 in astman_append, should not happen\n");
00986 }
00987 }
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005 #define MSG_MOREDATA ((char *)astman_send_response)
01006 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
01007 {
01008 const char *id = astman_get_header(m, "ActionID");
01009
01010 astman_append(s, "Response: %s\r\n", resp);
01011 if (!ast_strlen_zero(id))
01012 astman_append(s, "ActionID: %s\r\n", id);
01013 if (listflag)
01014 astman_append(s, "Eventlist: %s\r\n", listflag);
01015 if (msg == MSG_MOREDATA)
01016 return;
01017 else if (msg)
01018 astman_append(s, "Message: %s\r\n\r\n", msg);
01019 else
01020 astman_append(s, "\r\n");
01021 }
01022
01023 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
01024 {
01025 astman_send_response_full(s, m, resp, msg, NULL);
01026 }
01027
01028 void astman_send_error(struct mansession *s, const struct message *m, char *error)
01029 {
01030 astman_send_response_full(s, m, "Error", error, NULL);
01031 }
01032
01033 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
01034 {
01035 astman_send_response_full(s, m, "Success", msg, NULL);
01036 }
01037
01038 static void astman_start_ack(struct mansession *s, const struct message *m)
01039 {
01040 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
01041 }
01042
01043 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
01044 {
01045 astman_send_response_full(s, m, "Success", msg, listflag);
01046 }
01047
01048
01049
01050
01051
01052
01053 static int set_eventmask(struct mansession *s, const char *eventmask)
01054 {
01055 int maskint = strings_to_mask(eventmask);
01056
01057 ast_mutex_lock(&s->session->__lock);
01058 if (maskint >= 0)
01059 s->session->send_events = maskint;
01060 ast_mutex_unlock(&s->session->__lock);
01061
01062 return maskint;
01063 }
01064
01065
01066
01067
01068
01069
01070
01071
01072 static int authenticate(struct mansession *s, const struct message *m)
01073 {
01074 const char *username = astman_get_header(m, "Username");
01075 const char *password = astman_get_header(m, "Secret");
01076 int error = -1;
01077 struct ast_manager_user *user = NULL;
01078
01079 if (ast_strlen_zero(username))
01080 return -1;
01081
01082
01083 AST_RWLIST_WRLOCK(&users);
01084
01085 if (!(user = get_manager_by_name_locked(username))) {
01086 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
01087 } else if (user->ha && !ast_apply_ha(user->ha, &(s->session->sin))) {
01088 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
01089 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
01090 const char *key = astman_get_header(m, "Key");
01091 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
01092 int x;
01093 int len = 0;
01094 char md5key[256] = "";
01095 struct MD5Context md5;
01096 unsigned char digest[16];
01097
01098 MD5Init(&md5);
01099 MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
01100 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
01101 MD5Final(digest, &md5);
01102 for (x = 0; x < 16; x++)
01103 len += sprintf(md5key + len, "%2.2x", digest[x]);
01104 if (!strcmp(md5key, key))
01105 error = 0;
01106 } else {
01107 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
01108 S_OR(s->session->challenge, ""));
01109 }
01110 } else if (password && user->secret && !strcmp(password, user->secret))
01111 error = 0;
01112
01113 if (error) {
01114 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
01115 AST_RWLIST_UNLOCK(&users);
01116 return -1;
01117 }
01118
01119
01120
01121 ast_copy_string(s->session->username, username, sizeof(s->session->username));
01122 s->session->readperm = user->readperm;
01123 s->session->writeperm = user->writeperm;
01124 s->session->writetimeout = user->writetimeout;
01125 s->session->sessionstart = time(NULL);
01126 set_eventmask(s, astman_get_header(m, "Events"));
01127
01128 AST_RWLIST_UNLOCK(&users);
01129 return 0;
01130 }
01131
01132
01133 static char mandescr_ping[] =
01134 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
01135 " manager connection open.\n"
01136 "Variables: NONE\n";
01137
01138 static int action_ping(struct mansession *s, const struct message *m)
01139 {
01140 const char *actionid = astman_get_header(m, "ActionID");
01141
01142 astman_append(s, "Response: Success\r\n");
01143 if (!ast_strlen_zero(actionid)){
01144 astman_append(s, "ActionID: %s\r\n", actionid);
01145 }
01146 astman_append(s, "Ping: Pong\r\n\r\n");
01147 return 0;
01148 }
01149
01150 static char mandescr_getconfig[] =
01151 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
01152 "file by category and contents or optionally by specified category only.\n"
01153 "Variables: (Names marked with * are required)\n"
01154 " *Filename: Configuration filename (e.g. foo.conf)\n"
01155 " Category: Category in configuration file\n";
01156
01157 static int action_getconfig(struct mansession *s, const struct message *m)
01158 {
01159 struct ast_config *cfg;
01160 const char *fn = astman_get_header(m, "Filename");
01161 const char *category = astman_get_header(m, "Category");
01162 int catcount = 0;
01163 int lineno = 0;
01164 char *cur_category = NULL;
01165 struct ast_variable *v;
01166 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
01167
01168 if (ast_strlen_zero(fn)) {
01169 astman_send_error(s, m, "Filename not specified");
01170 return 0;
01171 }
01172 cfg = ast_config_load2(fn, "manager", config_flags);
01173 if (cfg == CONFIG_STATUS_FILEMISSING) {
01174 astman_send_error(s, m, "Config file not found");
01175 return 0;
01176 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
01177 astman_send_error(s, m, "Config file has invalid format");
01178 return 0;
01179 }
01180
01181 astman_start_ack(s, m);
01182 while ((cur_category = ast_category_browse(cfg, cur_category))) {
01183 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
01184 lineno = 0;
01185 astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
01186 for (v = ast_variable_browse(cfg, cur_category); v; v = v->next)
01187 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
01188 catcount++;
01189 }
01190 }
01191 if (!ast_strlen_zero(category) && catcount == 0)
01192 astman_append(s, "No categories found\r\n");
01193 ast_config_destroy(cfg);
01194 astman_append(s, "\r\n");
01195
01196 return 0;
01197 }
01198
01199 static char mandescr_listcategories[] =
01200 "Description: A 'ListCategories' action will dump the categories in\n"
01201 "a given file.\n"
01202 "Variables:\n"
01203 " Filename: Configuration filename (e.g. foo.conf)\n";
01204
01205 static int action_listcategories(struct mansession *s, const struct message *m)
01206 {
01207 struct ast_config *cfg;
01208 const char *fn = astman_get_header(m, "Filename");
01209 char *category = NULL;
01210 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
01211 int catcount = 0;
01212
01213 if (ast_strlen_zero(fn)) {
01214 astman_send_error(s, m, "Filename not specified");
01215 return 0;
01216 }
01217 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
01218 astman_send_error(s, m, "Config file not found");
01219 return 0;
01220 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
01221 astman_send_error(s, m, "Config file has invalid format");
01222 return 0;
01223 }
01224 astman_start_ack(s, m);
01225 while ((category = ast_category_browse(cfg, category))) {
01226 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
01227 catcount++;
01228 }
01229 if (catcount == 0)
01230 astman_append(s, "Error: no categories found\r\n");
01231 ast_config_destroy(cfg);
01232 astman_append(s, "\r\n");
01233
01234 return 0;
01235 }
01236
01237
01238
01239
01240
01241 static void json_escape(char *out, const char *in)
01242 {
01243 for (; *in; in++) {
01244 if (*in == '\\' || *in == '\"')
01245 *out++ = '\\';
01246 *out++ = *in;
01247 }
01248 *out = '\0';
01249 }
01250
01251 static char mandescr_getconfigjson[] =
01252 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
01253 "file by category and contents in JSON format. This only makes sense to be used\n"
01254 "using rawman over the HTTP interface.\n"
01255 "Variables:\n"
01256 " Filename: Configuration filename (e.g. foo.conf)\n";
01257
01258 static int action_getconfigjson(struct mansession *s, const struct message *m)
01259 {
01260 struct ast_config *cfg;
01261 const char *fn = astman_get_header(m, "Filename");
01262 char *category = NULL;
01263 struct ast_variable *v;
01264 int comma1 = 0;
01265 char *buf = NULL;
01266 unsigned int buf_len = 0;
01267 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
01268
01269 if (ast_strlen_zero(fn)) {
01270 astman_send_error(s, m, "Filename not specified");
01271 return 0;
01272 }
01273
01274 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
01275 astman_send_error(s, m, "Config file not found");
01276 return 0;
01277 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
01278 astman_send_error(s, m, "Config file has invalid format");
01279 return 0;
01280 }
01281
01282 buf_len = 512;
01283 buf = alloca(buf_len);
01284
01285 astman_start_ack(s, m);
01286 astman_append(s, "JSON: {");
01287 while ((category = ast_category_browse(cfg, category))) {
01288 int comma2 = 0;
01289 if (buf_len < 2 * strlen(category) + 1) {
01290 buf_len *= 2;
01291 buf = alloca(buf_len);
01292 }
01293 json_escape(buf, category);
01294 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
01295 if (!comma1)
01296 comma1 = 1;
01297 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
01298 if (comma2)
01299 astman_append(s, ",");
01300 if (buf_len < 2 * strlen(v->name) + 1) {
01301 buf_len *= 2;
01302 buf = alloca(buf_len);
01303 }
01304 json_escape(buf, v->name);
01305 astman_append(s, "\"%s", buf);
01306 if (buf_len < 2 * strlen(v->value) + 1) {
01307 buf_len *= 2;
01308 buf = alloca(buf_len);
01309 }
01310 json_escape(buf, v->value);
01311 astman_append(s, "%s\"", buf);
01312 if (!comma2)
01313 comma2 = 1;
01314 }
01315 astman_append(s, "]");
01316 }
01317 astman_append(s, "}\r\n\r\n");
01318
01319 ast_config_destroy(cfg);
01320
01321 return 0;
01322 }
01323
01324
01325 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
01326 {
01327 int x;
01328 char hdr[40];
01329 const char *action, *cat, *var, *value, *match, *line;
01330 struct ast_category *category;
01331 struct ast_variable *v;
01332 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
01333 enum error_type result = 0;
01334
01335 for (x = 0; x < 100000; x++) {
01336 unsigned int object = 0;
01337
01338 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
01339 action = astman_get_header(m, hdr);
01340 if (ast_strlen_zero(action))
01341 break;
01342
01343 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
01344 cat = astman_get_header(m, hdr);
01345 if (ast_strlen_zero(cat)) {
01346 result = UNSPECIFIED_CATEGORY;
01347 break;
01348 }
01349
01350 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
01351 var = astman_get_header(m, hdr);
01352
01353 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
01354 value = astman_get_header(m, hdr);
01355
01356 if (!ast_strlen_zero(value) && *value == '>') {
01357 object = 1;
01358 value++;
01359 }
01360
01361 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
01362 match = astman_get_header(m, hdr);
01363
01364 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
01365 line = astman_get_header(m, hdr);
01366
01367 if (!strcasecmp(action, "newcat")) {
01368 if (ast_category_get(cfg,cat)) {
01369 result = FAILURE_NEWCAT;
01370 break;
01371 }
01372 if (!(category = ast_category_new(cat, dfn, -1))) {
01373 result = FAILURE_ALLOCATION;
01374 break;
01375 }
01376 if (ast_strlen_zero(match)) {
01377 ast_category_append(cfg, category);
01378 } else
01379 ast_category_insert(cfg, category, match);
01380 } else if (!strcasecmp(action, "renamecat")) {
01381 if (ast_strlen_zero(value)) {
01382 result = UNSPECIFIED_ARGUMENT;
01383 break;
01384 }
01385 if (!(category = ast_category_get(cfg, cat))) {
01386 result = UNKNOWN_CATEGORY;
01387 break;
01388 }
01389 ast_category_rename(category, value);
01390 } else if (!strcasecmp(action, "delcat")) {
01391 if (ast_category_delete(cfg, cat)) {
01392 result = FAILURE_DELCAT;
01393 break;
01394 }
01395 } else if (!strcasecmp(action, "emptycat")) {
01396 if (ast_category_empty(cfg, cat)) {
01397 result = FAILURE_EMPTYCAT;
01398 break;
01399 }
01400 } else if (!strcasecmp(action, "update")) {
01401 if (ast_strlen_zero(var)) {
01402 result = UNSPECIFIED_ARGUMENT;
01403 break;
01404 }
01405 if (!(category = ast_category_get(cfg,cat))) {
01406 result = UNKNOWN_CATEGORY;
01407 break;
01408 }
01409 if (ast_variable_update(category, var, value, match, object)) {
01410 result = FAILURE_UPDATE;
01411 break;
01412 }
01413 } else if (!strcasecmp(action, "delete")) {
01414 if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
01415 result = UNSPECIFIED_ARGUMENT;
01416 break;
01417 }
01418 if (!(category = ast_category_get(cfg, cat))) {
01419 result = UNKNOWN_CATEGORY;
01420 break;
01421 }
01422 if (ast_variable_delete(category, var, match, line)) {
01423 result = FAILURE_DELETE;
01424 break;
01425 }
01426 } else if (!strcasecmp(action, "append")) {
01427 if (ast_strlen_zero(var)) {
01428 result = UNSPECIFIED_ARGUMENT;
01429 break;
01430 }
01431 if (!(category = ast_category_get(cfg, cat))) {
01432 result = UNKNOWN_CATEGORY;
01433 break;
01434 }
01435 if (!(v = ast_variable_new(var, value, dfn))) {
01436 result = FAILURE_ALLOCATION;
01437 break;
01438 }
01439 if (object || (match && !strcasecmp(match, "object")))
01440 v->object = 1;
01441 ast_variable_append(category, v);
01442 } else if (!strcasecmp(action, "insert")) {
01443 if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
01444 result = UNSPECIFIED_ARGUMENT;
01445 break;
01446 }
01447 if (!(category = ast_category_get(cfg, cat))) {
01448 result = UNKNOWN_CATEGORY;
01449 break;
01450 }
01451 if (!(v = ast_variable_new(var, value, dfn))) {
01452 result = FAILURE_ALLOCATION;
01453 break;
01454 }
01455 ast_variable_insert(category, v, line);
01456 }
01457 else {
01458 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
01459 result = UNKNOWN_ACTION;
01460 break;
01461 }
01462 }
01463 ast_free(str1);
01464 ast_free(str2);
01465 return result;
01466 }
01467
01468 static char mandescr_updateconfig[] =
01469 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
01470 "configuration elements in Asterisk configuration files.\n"
01471 "Variables (X's represent 6 digit number beginning with 000000):\n"
01472 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
01473 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
01474 " Reload: Whether or not a reload should take place (or name of specific module)\n"
01475 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,EmptyCat,Update,Delete,Append,Insert)\n"
01476 " Cat-XXXXXX: Category to operate on\n"
01477 " Var-XXXXXX: Variable to work on\n"
01478 " Value-XXXXXX: Value to work on\n"
01479 " Match-XXXXXX: Extra match required to match line\n"
01480 " Line-XXXXXX: Line in category to operate on (used with delete and insert actions)\n";
01481
01482 static int action_updateconfig(struct mansession *s, const struct message *m)
01483 {
01484 struct ast_config *cfg;
01485 const char *sfn = astman_get_header(m, "SrcFilename");
01486 const char *dfn = astman_get_header(m, "DstFilename");
01487 int res;
01488 const char *rld = astman_get_header(m, "Reload");
01489 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
01490 enum error_type result;
01491
01492 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
01493 astman_send_error(s, m, "Filename not specified");
01494 return 0;
01495 }
01496 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
01497 astman_send_error(s, m, "Config file not found");
01498 return 0;
01499 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
01500 astman_send_error(s, m, "Config file has invalid format");
01501 return 0;
01502 }
01503 result = handle_updates(s, m, cfg, dfn);
01504 if (!result) {
01505 ast_include_rename(cfg, sfn, dfn);
01506 res = ast_config_text_file_save(dfn, cfg, "Manager");
01507 ast_config_destroy(cfg);
01508 if (res) {
01509 astman_send_error(s, m, "Save of config failed");
01510 return 0;
01511 }
01512 astman_send_ack(s, m, NULL);
01513 if (!ast_strlen_zero(rld)) {
01514 if (ast_true(rld))
01515 rld = NULL;
01516 ast_module_reload(rld);
01517 }
01518 } else {
01519 ast_config_destroy(cfg);
01520 switch(result) {
01521 case UNKNOWN_ACTION:
01522 astman_send_error(s, m, "Unknown action command");
01523 break;
01524 case UNKNOWN_CATEGORY:
01525 astman_send_error(s, m, "Given category does not exist");
01526 break;
01527 case UNSPECIFIED_CATEGORY:
01528 astman_send_error(s, m, "Category not specified");
01529 break;
01530 case UNSPECIFIED_ARGUMENT:
01531 astman_send_error(s, m, "Problem with category, value, or line (if required)");
01532 break;
01533 case FAILURE_ALLOCATION:
01534 astman_send_error(s, m, "Memory allocation failure, this should not happen");
01535 break;
01536 case FAILURE_NEWCAT:
01537 astman_send_error(s, m, "Create category did not complete successfully");
01538 break;
01539 case FAILURE_DELCAT:
01540 astman_send_error(s, m, "Delete category did not complete successfully");
01541 break;
01542 case FAILURE_EMPTYCAT:
01543 astman_send_error(s, m, "Empty category did not complete successfully");
01544 break;
01545 case FAILURE_UPDATE:
01546 astman_send_error(s, m, "Update did not complete successfully");
01547 break;
01548 case FAILURE_DELETE:
01549 astman_send_error(s, m, "Delete did not complete successfully");
01550 break;
01551 case FAILURE_APPEND:
01552 astman_send_error(s, m, "Append did not complete successfully");
01553 break;
01554 }
01555 }
01556 return 0;
01557 }
01558
01559 static char mandescr_createconfig[] =
01560 "Description: A 'CreateConfig' action will create an empty file in the\n"
01561 "configuration directory. This action is intended to be used before an\n"
01562 "UpdateConfig action.\n"
01563 "Variables\n"
01564 " Filename: The configuration filename to create (e.g. foo.conf)\n";
01565
01566 static int action_createconfig(struct mansession *s, const struct message *m)
01567 {
01568 int fd;
01569 const char *fn = astman_get_header(m, "Filename");
01570 struct ast_str *filepath = ast_str_alloca(PATH_MAX);
01571 ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
01572 ast_str_append(&filepath, 0, "%s", fn);
01573
01574 if ((fd = open(ast_str_buffer(filepath), O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
01575 close(fd);
01576 astman_send_ack(s, m, "New configuration file created successfully");
01577 } else {
01578 astman_send_error(s, m, strerror(errno));
01579 }
01580
01581 return 0;
01582 }
01583
01584
01585 static char mandescr_waitevent[] =
01586 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
01587 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
01588 "session, events will be generated and queued.\n"
01589 "Variables: \n"
01590 " Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
01591
01592 static int action_waitevent(struct mansession *s, const struct message *m)
01593 {
01594 const char *timeouts = astman_get_header(m, "Timeout");
01595 int timeout = -1;
01596 int x;
01597 int needexit = 0;
01598 const char *id = astman_get_header(m, "ActionID");
01599 char idText[256];
01600
01601 if (!ast_strlen_zero(id))
01602 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01603 else
01604 idText[0] = '\0';
01605
01606 if (!ast_strlen_zero(timeouts)) {
01607 sscanf(timeouts, "%30i", &timeout);
01608 if (timeout < -1)
01609 timeout = -1;
01610
01611 }
01612
01613 ast_mutex_lock(&s->session->__lock);
01614 if (s->session->waiting_thread != AST_PTHREADT_NULL)
01615 pthread_kill(s->session->waiting_thread, SIGURG);
01616
01617 if (s->session->managerid) {
01618
01619
01620
01621
01622
01623 time_t now = time(NULL);
01624 int max = s->session->sessiontimeout - now - 10;
01625
01626 if (max < 0)
01627 max = 0;
01628 if (timeout < 0 || timeout > max)
01629 timeout = max;
01630 if (!s->session->send_events)
01631 s->session->send_events = -1;
01632 }
01633 ast_mutex_unlock(&s->session->__lock);
01634
01635
01636 s->session->waiting_thread = pthread_self();
01637 ast_debug(1, "Starting waiting for an event!\n");
01638
01639 for (x = 0; x < timeout || timeout < 0; x++) {
01640 ast_mutex_lock(&s->session->__lock);
01641 if (NEW_EVENT(s))
01642 needexit = 1;
01643
01644
01645
01646
01647 if (s->session->waiting_thread != pthread_self())
01648 needexit = 1;
01649 if (s->session->needdestroy)
01650 needexit = 1;
01651 ast_mutex_unlock(&s->session->__lock);
01652 if (needexit)
01653 break;
01654 if (s->session->managerid == 0) {
01655 if (ast_wait_for_input(s->session->fd, 1000))
01656 break;
01657 } else {
01658 sleep(1);
01659 }
01660 }
01661 ast_debug(1, "Finished waiting for an event!\n");
01662 ast_mutex_lock(&s->session->__lock);
01663 if (s->session->waiting_thread == pthread_self()) {
01664 struct eventqent *eqe;
01665 astman_send_response(s, m, "Success", "Waiting for Event completed.");
01666 while ( (eqe = NEW_EVENT(s)) ) {
01667 ref_event(eqe);
01668 if (((s->session->readperm & eqe->category) == eqe->category) &&
01669 ((s->session->send_events & eqe->category) == eqe->category)) {
01670 astman_append(s, "%s", eqe->eventdata);
01671 }
01672 s->session->last_ev = unref_event(s->session->last_ev);
01673 }
01674 astman_append(s,
01675 "Event: WaitEventComplete\r\n"
01676 "%s"
01677 "\r\n", idText);
01678 s->session->waiting_thread = AST_PTHREADT_NULL;
01679 } else {
01680 ast_debug(1, "Abandoning event request!\n");
01681 }
01682 ast_mutex_unlock(&s->session->__lock);
01683 return 0;
01684 }
01685
01686 static char mandescr_listcommands[] =
01687 "Description: Returns the action name and synopsis for every\n"
01688 " action that is available to the user\n"
01689 "Variables: NONE\n";
01690
01691
01692 static int action_listcommands(struct mansession *s, const struct message *m)
01693 {
01694 struct manager_action *cur;
01695 struct ast_str *temp = ast_str_alloca(BUFSIZ);
01696
01697 astman_start_ack(s, m);
01698 AST_RWLIST_TRAVERSE(&actions, cur, list) {
01699 if (s->session->writeperm & cur->authority || cur->authority == 0)
01700 astman_append(s, "%s: %s (Priv: %s)\r\n",
01701 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
01702 }
01703 astman_append(s, "\r\n");
01704
01705 return 0;
01706 }
01707
01708 static char mandescr_events[] =
01709 "Description: Enable/Disable sending of events to this manager\n"
01710 " client.\n"
01711 "Variables:\n"
01712 " EventMask: 'on' if all events should be sent,\n"
01713 " 'off' if no events should be sent,\n"
01714 " 'system,call,log' to select which flags events should have to be sent.\n";
01715
01716 static int action_events(struct mansession *s, const struct message *m)
01717 {
01718 const char *mask = astman_get_header(m, "EventMask");
01719 int res, x;
01720
01721 res = set_eventmask(s, mask);
01722 if (broken_events_action) {
01723
01724
01725
01726 if (res > 0) {
01727 for (x = 0; x < ARRAY_LEN(perms); x++) {
01728 if (!strcasecmp(perms[x].label, "all") && res == perms[x].num) {
01729 return 0;
01730 }
01731 }
01732 astman_append(s, "Response: Success\r\n"
01733 "Events: On\r\n\r\n");
01734 } else if (res == 0)
01735 astman_append(s, "Response: Success\r\n"
01736 "Events: Off\r\n\r\n");
01737 return 0;
01738 }
01739
01740 if (res > 0)
01741 astman_append(s, "Response: Success\r\n"
01742 "Events: On\r\n\r\n");
01743 else if (res == 0)
01744 astman_append(s, "Response: Success\r\n"
01745 "Events: Off\r\n\r\n");
01746 else
01747 astman_send_error(s, m, "Invalid event mask");
01748
01749 return 0;
01750 }
01751
01752 static char mandescr_logoff[] =
01753 "Description: Logoff this manager session\n"
01754 "Variables: NONE\n";
01755
01756 static int action_logoff(struct mansession *s, const struct message *m)
01757 {
01758 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
01759 return -1;
01760 }
01761
01762 static int action_login(struct mansession *s, const struct message *m)
01763 {
01764 if (authenticate(s, m)) {
01765 sleep(1);
01766 astman_send_error(s, m, "Authentication failed");
01767 return -1;
01768 }
01769 s->session->authenticated = 1;
01770 if (manager_displayconnects(s->session))
01771 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
01772 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
01773 astman_send_ack(s, m, "Authentication accepted");
01774 return 0;
01775 }
01776
01777 static int action_challenge(struct mansession *s, const struct message *m)
01778 {
01779 const char *authtype = astman_get_header(m, "AuthType");
01780
01781 if (!strcasecmp(authtype, "MD5")) {
01782 if (ast_strlen_zero(s->session->challenge))
01783 snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
01784 ast_mutex_lock(&s->session->__lock);
01785 astman_start_ack(s, m);
01786 astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
01787 ast_mutex_unlock(&s->session->__lock);
01788 } else {
01789 astman_send_error(s, m, "Must specify AuthType");
01790 }
01791 return 0;
01792 }
01793
01794 static char mandescr_hangup[] =
01795 "Description: Hangup a channel\n"
01796 "Variables: \n"
01797 " Channel: The channel name to be hungup\n";
01798
01799 static int action_hangup(struct mansession *s, const struct message *m)
01800 {
01801 struct ast_channel *c = NULL;
01802 const char *name = astman_get_header(m, "Channel");
01803 if (ast_strlen_zero(name)) {
01804 astman_send_error(s, m, "No channel specified");
01805 return 0;
01806 }
01807 c = ast_get_channel_by_name_locked(name);
01808 if (!c) {
01809 astman_send_error(s, m, "No such channel");
01810 return 0;
01811 }
01812 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
01813 ast_channel_unlock(c);
01814 astman_send_ack(s, m, "Channel Hungup");
01815 return 0;
01816 }
01817
01818 static char mandescr_setvar[] =
01819 "Description: Set a global or local channel variable.\n"
01820 "Variables: (Names marked with * are required)\n"
01821 " Channel: Channel to set variable for\n"
01822 " *Variable: Variable name\n"
01823 " *Value: Value\n";
01824
01825 static int action_setvar(struct mansession *s, const struct message *m)
01826 {
01827 struct ast_channel *c = NULL;
01828 const char *name = astman_get_header(m, "Channel");
01829 const char *varname = astman_get_header(m, "Variable");
01830 const char *varval = astman_get_header(m, "Value");
01831 int res = 0;
01832
01833 if (ast_strlen_zero(varname)) {
01834 astman_send_error(s, m, "No variable specified");
01835 return 0;
01836 }
01837
01838 if (!ast_strlen_zero(name)) {
01839 c = ast_get_channel_by_name_locked(name);
01840 if (!c) {
01841 astman_send_error(s, m, "No such channel");
01842 return 0;
01843 }
01844 }
01845 if (varname[strlen(varname)-1] == ')') {
01846 char *function = ast_strdupa(varname);
01847 res = ast_func_write(c, function, varval);
01848 } else {
01849 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
01850 }
01851
01852 if (c)
01853 ast_channel_unlock(c);
01854 if (res == 0) {
01855 astman_send_ack(s, m, "Variable Set");
01856 } else {
01857 astman_send_error(s, m, "Variable not set");
01858 }
01859 return 0;
01860 }
01861
01862 static char mandescr_getvar[] =
01863 "Description: Get the value of a global or local channel variable.\n"
01864 "Variables: (Names marked with * are required)\n"
01865 " Channel: Channel to read variable from\n"
01866 " *Variable: Variable name\n"
01867 " ActionID: Optional Action id for message matching.\n";
01868
01869 static int action_getvar(struct mansession *s, const struct message *m)
01870 {
01871 struct ast_channel *c = NULL;
01872 const char *name = astman_get_header(m, "Channel");
01873 const char *varname = astman_get_header(m, "Variable");
01874 char *varval;
01875 char workspace[1024] = "";
01876
01877 if (ast_strlen_zero(varname)) {
01878 astman_send_error(s, m, "No variable specified");
01879 return 0;
01880 }
01881
01882 if (!ast_strlen_zero(name)) {
01883 c = ast_get_channel_by_name_locked(name);
01884 if (!c) {
01885 astman_send_error(s, m, "No such channel");
01886 return 0;
01887 }
01888 }
01889
01890 if (varname[strlen(varname) - 1] == ')') {
01891 if (!c) {
01892 c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager");
01893 if (c) {
01894 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
01895 ast_channel_free(c);
01896 c = NULL;
01897 } else
01898 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
01899 } else
01900 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
01901 varval = workspace;
01902 } else {
01903 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
01904 }
01905
01906 if (c)
01907 ast_channel_unlock(c);
01908 astman_start_ack(s, m);
01909 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
01910
01911 return 0;
01912 }
01913
01914 static char mandescr_status[] =
01915 "Description: Lists channel status along with requested channel vars.\n"
01916 "Variables: (Names marked with * are required)\n"
01917 " *Channel: Name of the channel to query for status\n"
01918 " Variables: Comma ',' separated list of variables to include\n"
01919 " ActionID: Optional ID for this transaction\n"
01920 "Will return the status information of each channel along with the\n"
01921 "value for the specified channel variables.\n";
01922
01923
01924
01925
01926 static int action_status(struct mansession *s, const struct message *m)
01927 {
01928 const char *name = astman_get_header(m, "Channel");
01929 const char *cvariables = astman_get_header(m, "Variables");
01930 char *variables = ast_strdupa(S_OR(cvariables, ""));
01931 struct ast_channel *c;
01932 char bridge[256];
01933 struct timeval now = ast_tvnow();
01934 long elapsed_seconds = 0;
01935 int channels = 0;
01936 int all = ast_strlen_zero(name);
01937 const char *id = astman_get_header(m, "ActionID");
01938 char idText[256];
01939 AST_DECLARE_APP_ARGS(vars,
01940 AST_APP_ARG(name)[100];
01941 );
01942 struct ast_str *str = ast_str_create(1000);
01943
01944 if (!ast_strlen_zero(id))
01945 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01946 else
01947 idText[0] = '\0';
01948
01949 if (all)
01950 c = ast_channel_walk_locked(NULL);
01951 else {
01952 c = ast_get_channel_by_name_locked(name);
01953 if (!c) {
01954 astman_send_error(s, m, "No such channel");
01955 ast_free(str);
01956 return 0;
01957 }
01958 }
01959 astman_send_ack(s, m, "Channel status will follow");
01960
01961 if (!ast_strlen_zero(cvariables)) {
01962 AST_STANDARD_APP_ARGS(vars, variables);
01963 }
01964
01965
01966 while (c) {
01967 if (!ast_strlen_zero(cvariables)) {
01968 int i;
01969 ast_str_reset(str);
01970 for (i = 0; i < vars.argc; i++) {
01971 char valbuf[512], *ret = NULL;
01972
01973 if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
01974 if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
01975 valbuf[0] = '\0';
01976 }
01977 ret = valbuf;
01978 } else {
01979 pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
01980 }
01981
01982 ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
01983 }
01984 }
01985
01986 channels++;
01987 if (c->_bridge)
01988 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
01989 else
01990 bridge[0] = '\0';
01991 if (c->pbx) {
01992 if (c->cdr) {
01993 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01994 }
01995 astman_append(s,
01996 "Event: Status\r\n"
01997 "Privilege: Call\r\n"
01998 "Channel: %s\r\n"
01999 "CallerIDNum: %s\r\n"
02000 "CallerIDName: %s\r\n"
02001 "Accountcode: %s\r\n"
02002 "ChannelState: %d\r\n"
02003 "ChannelStateDesc: %s\r\n"
02004 "Context: %s\r\n"
02005 "Extension: %s\r\n"
02006 "Priority: %d\r\n"
02007 "Seconds: %ld\r\n"
02008 "%s"
02009 "Uniqueid: %s\r\n"
02010 "%s"
02011 "%s"
02012 "\r\n",
02013 c->name,
02014 S_OR(c->cid.cid_num, ""),
02015 S_OR(c->cid.cid_name, ""),
02016 c->accountcode,
02017 c->_state,
02018 ast_state2str(c->_state), c->context,
02019 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, ast_str_buffer(str), idText);
02020 } else {
02021 astman_append(s,
02022 "Event: Status\r\n"
02023 "Privilege: Call\r\n"
02024 "Channel: %s\r\n"
02025 "CallerIDNum: %s\r\n"
02026 "CallerIDName: %s\r\n"
02027 "Account: %s\r\n"
02028 "State: %s\r\n"
02029 "%s"
02030 "Uniqueid: %s\r\n"
02031 "%s"
02032 "%s"
02033 "\r\n",
02034 c->name,
02035 S_OR(c->cid.cid_num, "<unknown>"),
02036 S_OR(c->cid.cid_name, "<unknown>"),
02037 c->accountcode,
02038 ast_state2str(c->_state), bridge, c->uniqueid, ast_str_buffer(str), idText);
02039 }
02040 ast_channel_unlock(c);
02041 if (!all)
02042 break;
02043 c = ast_channel_walk_locked(c);
02044 }
02045 astman_append(s,
02046 "Event: StatusComplete\r\n"
02047 "%s"
02048 "Items: %d\r\n"
02049 "\r\n", idText, channels);
02050 ast_free(str);
02051 return 0;
02052 }
02053
02054 static char mandescr_sendtext[] =
02055 "Description: Sends A Text Message while in a call.\n"
02056 "Variables: (Names marked with * are required)\n"
02057 " *Channel: Channel to send message to\n"
02058 " *Message: Message to send\n"
02059 " ActionID: Optional Action id for message matching.\n";
02060
02061 static int action_sendtext(struct mansession *s, const struct message *m)
02062 {
02063 struct ast_channel *c = NULL;
02064 const char *name = astman_get_header(m, "Channel");
02065 const char *textmsg = astman_get_header(m, "Message");
02066 int res = 0;
02067
02068 if (ast_strlen_zero(name)) {
02069 astman_send_error(s, m, "No channel specified");
02070 return 0;
02071 }
02072
02073 if (ast_strlen_zero(textmsg)) {
02074 astman_send_error(s, m, "No Message specified");
02075 return 0;
02076 }
02077
02078 c = ast_get_channel_by_name_locked(name);
02079 if (!c) {
02080 astman_send_error(s, m, "No such channel");
02081 return 0;
02082 }
02083
02084 res = ast_sendtext(c, textmsg);
02085 ast_channel_unlock(c);
02086
02087 if (res > 0)
02088 astman_send_ack(s, m, "Success");
02089 else
02090 astman_send_error(s, m, "Failure");
02091
02092 return res;
02093 }
02094
02095 static char mandescr_redirect[] =
02096 "Description: Redirect (transfer) a call.\n"
02097 "Variables: (Names marked with * are required)\n"
02098 " *Channel: Channel to redirect\n"
02099 " ExtraChannel: Second call leg to transfer (optional)\n"
02100 " *Exten: Extension to transfer to\n"
02101 " *Context: Context to transfer to\n"
02102 " *Priority: Priority to transfer to\n"
02103 " ActionID: Optional Action id for message matching.\n";
02104
02105
02106 static int action_redirect(struct mansession *s, const struct message *m)
02107 {
02108 const char *name = astman_get_header(m, "Channel");
02109 const char *name2 = astman_get_header(m, "ExtraChannel");
02110 const char *exten = astman_get_header(m, "Exten");
02111 const char *context = astman_get_header(m, "Context");
02112 const char *priority = astman_get_header(m, "Priority");
02113 struct ast_channel *chan, *chan2 = NULL;
02114 int pi = 0;
02115 int res;
02116
02117 if (ast_strlen_zero(name)) {
02118 astman_send_error(s, m, "Channel not specified");
02119 return 0;
02120 }
02121 if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
02122 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
02123 astman_send_error(s, m, "Invalid priority");
02124 return 0;
02125 }
02126 }
02127
02128 chan = ast_get_channel_by_name_locked(name);
02129 if (!chan) {
02130 char buf[256];
02131 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
02132 astman_send_error(s, m, buf);
02133 return 0;
02134 }
02135 if (ast_check_hangup(chan)) {
02136 astman_send_error(s, m, "Redirect failed, channel not up.");
02137 ast_channel_unlock(chan);
02138 return 0;
02139 }
02140 if (!ast_strlen_zero(name2))
02141 chan2 = ast_get_channel_by_name_locked(name2);
02142 if (chan2 && ast_check_hangup(chan2)) {
02143 astman_send_error(s, m, "Redirect failed, extra channel not up.");
02144 ast_channel_unlock(chan);
02145 ast_channel_unlock(chan2);
02146 return 0;
02147 }
02148 if (chan->pbx) {
02149 ast_channel_lock(chan);
02150 ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT);
02151 ast_channel_unlock(chan);
02152 }
02153 res = ast_async_goto(chan, context, exten, pi);
02154 if (!res) {
02155 if (!ast_strlen_zero(name2)) {
02156 if (chan2) {
02157 if (chan2->pbx) {
02158 ast_channel_lock(chan2);
02159 ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT);
02160 ast_channel_unlock(chan2);
02161 }
02162 res = ast_async_goto(chan2, context, exten, pi);
02163 } else {
02164 res = -1;
02165 }
02166 if (!res)
02167 astman_send_ack(s, m, "Dual Redirect successful");
02168 else
02169 astman_send_error(s, m, "Secondary redirect failed");
02170 } else
02171 astman_send_ack(s, m, "Redirect successful");
02172 } else
02173 astman_send_error(s, m, "Redirect failed");
02174 if (chan)
02175 ast_channel_unlock(chan);
02176 if (chan2)
02177 ast_channel_unlock(chan2);
02178 return 0;
02179 }
02180
02181 static char mandescr_atxfer[] =
02182 "Description: Attended transfer.\n"
02183 "Variables: (Names marked with * are required)\n"
02184 " *Channel: Transferer's channel\n"
02185 " *Exten: Extension to transfer to\n"
02186 " *Context: Context to transfer to\n"
02187 " *Priority: Priority to transfer to\n"
02188 " ActionID: Optional Action id for message matching.\n";
02189
02190 static int action_atxfer(struct mansession *s, const struct message *m)
02191 {
02192 const char *name = astman_get_header(m, "Channel");
02193 const char *exten = astman_get_header(m, "Exten");
02194 const char *context = astman_get_header(m, "Context");
02195 struct ast_channel *chan = NULL;
02196 struct ast_call_feature *atxfer_feature = NULL;
02197 char *feature_code = NULL;
02198
02199 if (ast_strlen_zero(name)) {
02200 astman_send_error(s, m, "No channel specified");
02201 return 0;
02202 }
02203 if (ast_strlen_zero(exten)) {
02204 astman_send_error(s, m, "No extension specified");
02205 return 0;
02206 }
02207
02208 if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
02209 astman_send_error(s, m, "No attended transfer feature found");
02210 return 0;
02211 }
02212
02213 if (!(chan = ast_get_channel_by_name_locked(name))) {
02214 astman_send_error(s, m, "Channel specified does not exist");
02215 return 0;
02216 }
02217
02218 if (!ast_strlen_zero(context)) {
02219 pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
02220 }
02221
02222 for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
02223 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
02224 ast_queue_frame(chan, &f);
02225 }
02226
02227 for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
02228 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
02229 ast_queue_frame(chan, &f);
02230 }
02231
02232 astman_send_ack(s, m, "Atxfer successfully queued");
02233 ast_channel_unlock(chan);
02234
02235 return 0;
02236 }
02237
02238 static int check_blacklist(const char *cmd)
02239 {
02240 char *cmd_copy, *cur_cmd;
02241 char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
02242 int i;
02243
02244 cmd_copy = ast_strdupa(cmd);
02245 for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
02246 cur_cmd = ast_strip(cur_cmd);
02247 if (ast_strlen_zero(cur_cmd)) {
02248 i--;
02249 continue;
02250 }
02251
02252 cmd_words[i] = cur_cmd;
02253 }
02254
02255 for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
02256 int j, match = 1;
02257
02258 for (j = 0; command_blacklist[i].words[j]; j++) {
02259 if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
02260 match = 0;
02261 break;
02262 }
02263 }
02264
02265 if (match) {
02266 return 1;
02267 }
02268 }
02269
02270 return 0;
02271 }
02272
02273 static char mandescr_command[] =
02274 "Description: Run a CLI command.\n"
02275 "Variables: (Names marked with * are required)\n"
02276 " *Command: Asterisk CLI command to run\n"
02277 " ActionID: Optional Action id for message matching.\n";
02278
02279
02280 static int action_command(struct mansession *s, const struct message *m)
02281 {
02282 const char *cmd = astman_get_header(m, "Command");
02283 const char *id = astman_get_header(m, "ActionID");
02284 char *buf, *final_buf;
02285 char template[] = "/tmp/ast-ami-XXXXXX";
02286 int fd;
02287 off_t l;
02288
02289 if (ast_strlen_zero(cmd)) {
02290 astman_send_error(s, m, "No command provided");
02291 return 0;
02292 }
02293
02294 if (check_blacklist(cmd)) {
02295 astman_send_error(s, m, "Command blacklisted");
02296 return 0;
02297 }
02298
02299 fd = mkstemp(template);
02300
02301 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
02302 if (!ast_strlen_zero(id))
02303 astman_append(s, "ActionID: %s\r\n", id);
02304
02305 ast_cli_command(fd, cmd);
02306 l = lseek(fd, 0, SEEK_END);
02307
02308
02309 buf = ast_calloc(1, l + 1);
02310 final_buf = ast_calloc(1, l + 1);
02311 if (buf) {
02312 lseek(fd, 0, SEEK_SET);
02313 if (read(fd, buf, l) < 0) {
02314 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
02315 }
02316 buf[l] = '\0';
02317 if (final_buf) {
02318 term_strip(final_buf, buf, l);
02319 final_buf[l] = '\0';
02320 }
02321 astman_append(s, "%s", S_OR(final_buf, buf));
02322 ast_free(buf);
02323 }
02324 close(fd);
02325 unlink(template);
02326 astman_append(s, "--END COMMAND--\r\n\r\n");
02327 if (final_buf)
02328 ast_free(final_buf);
02329 return 0;
02330 }
02331
02332
02333 struct fast_originate_helper {
02334 char tech[AST_MAX_EXTENSION];
02335
02336 char data[512];
02337 int timeout;
02338 int format;
02339 char app[AST_MAX_APP];
02340 char appdata[AST_MAX_EXTENSION];
02341 char cid_name[AST_MAX_EXTENSION];
02342 char cid_num[AST_MAX_EXTENSION];
02343 char context[AST_MAX_CONTEXT];
02344 char exten[AST_MAX_EXTENSION];
02345 char idtext[AST_MAX_EXTENSION];
02346 char account[AST_MAX_ACCOUNT_CODE];
02347 int priority;
02348 struct ast_variable *vars;
02349 };
02350
02351 static void *fast_originate(void *data)
02352 {
02353 struct fast_originate_helper *in = data;
02354 int res;
02355 int reason = 0;
02356 struct ast_channel *chan = NULL;
02357 char requested_channel[AST_CHANNEL_NAME];
02358
02359 if (!ast_strlen_zero(in->app)) {
02360 res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1,
02361 S_OR(in->cid_num, NULL),
02362 S_OR(in->cid_name, NULL),
02363 in->vars, in->account, &chan);
02364 } else {
02365 res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
02366 S_OR(in->cid_num, NULL),
02367 S_OR(in->cid_name, NULL),
02368 in->vars, in->account, &chan);
02369 }
02370
02371 if (!chan)
02372 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
02373
02374 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
02375 "%s%s"
02376 "Response: %s\r\n"
02377 "Channel: %s\r\n"
02378 "Context: %s\r\n"
02379 "Exten: %s\r\n"
02380 "Reason: %d\r\n"
02381 "Uniqueid: %s\r\n"
02382 "CallerIDNum: %s\r\n"
02383 "CallerIDName: %s\r\n",
02384 in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success",
02385 chan ? chan->name : requested_channel, in->context, in->exten, reason,
02386 chan ? chan->uniqueid : "<null>",
02387 S_OR(in->cid_num, "<unknown>"),
02388 S_OR(in->cid_name, "<unknown>")
02389 );
02390
02391
02392 if (chan)
02393 ast_channel_unlock(chan);
02394 ast_free(in);
02395 return NULL;
02396 }
02397
02398 static char mandescr_originate[] =
02399 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
02400 " Application/Data\n"
02401 "Variables: (Names marked with * are required)\n"
02402 " *Channel: Channel name to call\n"
02403 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
02404 " Context: Context to use (requires 'Exten' and 'Priority')\n"
02405 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
02406 " Application: Application to use\n"
02407 " Data: Data to use (requires 'Application')\n"
02408 " Timeout: How long to wait for call to be answered (in ms. Default: 30000)\n"
02409 " CallerID: Caller ID to be set on the outgoing channel\n"
02410 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
02411 " Account: Account code\n"
02412 " Async: Set to 'true' for fast origination\n";
02413
02414 static int action_originate(struct mansession *s, const struct message *m)
02415 {
02416 const char *name = astman_get_header(m, "Channel");
02417 const char *exten = astman_get_header(m, "Exten");
02418 const char *context = astman_get_header(m, "Context");
02419 const char *priority = astman_get_header(m, "Priority");
02420 const char *timeout = astman_get_header(m, "Timeout");
02421 const char *callerid = astman_get_header(m, "CallerID");
02422 const char *account = astman_get_header(m, "Account");
02423 const char *app = astman_get_header(m, "Application");
02424 const char *appdata = astman_get_header(m, "Data");
02425 const char *async = astman_get_header(m, "Async");
02426 const char *id = astman_get_header(m, "ActionID");
02427 const char *codecs = astman_get_header(m, "Codecs");
02428 struct ast_variable *vars = astman_get_variables(m);
02429 char *tech, *data;
02430 char *l = NULL, *n = NULL;
02431 int pi = 0;
02432 int res;
02433 int to = 30000;
02434 int reason = 0;
02435 char tmp[256];
02436 char tmp2[256];
02437 int format = AST_FORMAT_SLINEAR;
02438
02439 pthread_t th;
02440 if (ast_strlen_zero(name)) {
02441 astman_send_error(s, m, "Channel not specified");
02442 return 0;
02443 }
02444 if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
02445 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
02446 astman_send_error(s, m, "Invalid priority");
02447 return 0;
02448 }
02449 }
02450 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) {
02451 astman_send_error(s, m, "Invalid timeout");
02452 return 0;
02453 }
02454 ast_copy_string(tmp, name, sizeof(tmp));
02455 tech = tmp;
02456 data = strchr(tmp, '/');
02457 if (!data) {
02458 astman_send_error(s, m, "Invalid channel");
02459 return 0;
02460 }
02461 *data++ = '\0';
02462 ast_copy_string(tmp2, callerid, sizeof(tmp2));
02463 ast_callerid_parse(tmp2, &n, &l);
02464 if (n) {
02465 if (ast_strlen_zero(n))
02466 n = NULL;
02467 }
02468 if (l) {
02469 ast_shrink_phone_number(l);
02470 if (ast_strlen_zero(l))
02471 l = NULL;
02472 }
02473 if (!ast_strlen_zero(codecs)) {
02474 format = 0;
02475 ast_parse_allow_disallow(NULL, &format, codecs, 1);
02476 }
02477 if (ast_true(async)) {
02478 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
02479 if (!fast) {
02480 res = -1;
02481 } else {
02482 if (!ast_strlen_zero(id))
02483 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s", id);
02484 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
02485 ast_copy_string(fast->data, data, sizeof(fast->data));
02486 ast_copy_string(fast->app, app, sizeof(fast->app));
02487 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
02488 if (l)
02489 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
02490 if (n)
02491 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
02492 fast->vars = vars;
02493 ast_copy_string(fast->context, context, sizeof(fast->context));
02494 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
02495 ast_copy_string(fast->account, account, sizeof(fast->account));
02496 fast->format = format;
02497 fast->timeout = to;
02498 fast->priority = pi;
02499 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
02500 ast_free(fast);
02501 res = -1;
02502 } else {
02503 res = 0;
02504 }
02505 }
02506 } else if (!ast_strlen_zero(app)) {
02507
02508 if (!(s->session->writeperm & EVENT_FLAG_SYSTEM)
02509 && (
02510 strcasestr(app, "system") == 0 ||
02511
02512 strcasestr(app, "exec") ||
02513
02514 strcasestr(app, "agi") ||
02515
02516 strstr(appdata, "SHELL") ||
02517 strstr(appdata, "EVAL")
02518 )) {
02519 astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
02520 return 0;
02521 }
02522 res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
02523 } else {
02524 if (exten && context && pi)
02525 res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
02526 else {
02527 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
02528 return 0;
02529 }
02530 }
02531 if (!res)
02532 astman_send_ack(s, m, "Originate successfully queued");
02533 else
02534 astman_send_error(s, m, "Originate failed");
02535 return 0;
02536 }
02537
02538
02539
02540 static char mandescr_mailboxstatus[] =
02541 "Description: Checks a voicemail account for status.\n"
02542 "Variables: (Names marked with * are required)\n"
02543 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02544 " ActionID: Optional ActionID for message matching.\n"
02545 "Returns number of messages.\n"
02546 " Message: Mailbox Status\n"
02547 " Mailbox: <mailboxid>\n"
02548 " Waiting: <count>\n"
02549 "\n";
02550
02551 static int action_mailboxstatus(struct mansession *s, const struct message *m)
02552 {
02553 const char *mailbox = astman_get_header(m, "Mailbox");
02554 int ret;
02555
02556 if (ast_strlen_zero(mailbox)) {
02557 astman_send_error(s, m, "Mailbox not specified");
02558 return 0;
02559 }
02560 ret = ast_app_has_voicemail(mailbox, NULL);
02561 astman_start_ack(s, m);
02562 astman_append(s, "Message: Mailbox Status\r\n"
02563 "Mailbox: %s\r\n"
02564 "Waiting: %d\r\n\r\n", mailbox, ret);
02565 return 0;
02566 }
02567
02568 static char mandescr_mailboxcount[] =
02569 "Description: Checks a voicemail account for new messages.\n"
02570 "Variables: (Names marked with * are required)\n"
02571 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02572 " ActionID: Optional ActionID for message matching.\n"
02573 "Returns number of urgent, new and old messages.\n"
02574 " Message: Mailbox Message Count\n"
02575 " Mailbox: <mailboxid>\n"
02576 " UrgentMessages: <count>\n"
02577 " NewMessages: <count>\n"
02578 " OldMessages: <count>\n"
02579 "\n";
02580 static int action_mailboxcount(struct mansession *s, const struct message *m)
02581 {
02582 const char *mailbox = astman_get_header(m, "Mailbox");
02583 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
02584
02585 if (ast_strlen_zero(mailbox)) {
02586 astman_send_error(s, m, "Mailbox not specified");
02587 return 0;
02588 }
02589 ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
02590 astman_start_ack(s, m);
02591 astman_append(s, "Message: Mailbox Message Count\r\n"
02592 "Mailbox: %s\r\n"
02593 "UrgMessages: %d\r\n"
02594 "NewMessages: %d\r\n"
02595 "OldMessages: %d\r\n"
02596 "\r\n",
02597 mailbox, urgentmsgs, newmsgs, oldmsgs);
02598 return 0;
02599 }
02600
02601 static char mandescr_extensionstate[] =
02602 "Description: Report the extension state for given extension.\n"
02603 " If the extension has a hint, will use devicestate to check\n"
02604 " the status of the device connected to the extension.\n"
02605 "Variables: (Names marked with * are required)\n"
02606 " *Exten: Extension to check state on\n"
02607 " *Context: Context for extension\n"
02608 " ActionId: Optional ID for this transaction\n"
02609 "Will return an \"Extension Status\" message.\n"
02610 "The response will include the hint for the extension and the status.\n";
02611
02612 static int action_extensionstate(struct mansession *s, const struct message *m)
02613 {
02614 const char *exten = astman_get_header(m, "Exten");
02615 const char *context = astman_get_header(m, "Context");
02616 char hint[256] = "";
02617 int status;
02618 if (ast_strlen_zero(exten)) {
02619 astman_send_error(s, m, "Extension not specified");
02620 return 0;
02621 }
02622 if (ast_strlen_zero(context))
02623 context = "default";
02624 status = ast_extension_state(NULL, context, exten);
02625 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
02626 astman_start_ack(s, m);
02627 astman_append(s, "Message: Extension Status\r\n"
02628 "Exten: %s\r\n"
02629 "Context: %s\r\n"
02630 "Hint: %s\r\n"
02631 "Status: %d\r\n\r\n",
02632 exten, context, hint, status);
02633 return 0;
02634 }
02635
02636 static char mandescr_timeout[] =
02637 "Description: Hangup a channel after a certain time.\n"
02638 "Variables: (Names marked with * are required)\n"
02639 " *Channel: Channel name to hangup\n"
02640 " *Timeout: Maximum duration of the call (sec)\n"
02641 "Acknowledges set time with 'Timeout Set' message\n";
02642
02643 static int action_timeout(struct mansession *s, const struct message *m)
02644 {
02645 struct ast_channel *c;
02646 const char *name = astman_get_header(m, "Channel");
02647 double timeout = atof(astman_get_header(m, "Timeout"));
02648 struct timeval when = { timeout, 0 };
02649
02650 if (ast_strlen_zero(name)) {
02651 astman_send_error(s, m, "No channel specified");
02652 return 0;
02653 }
02654 if (!timeout || timeout < 0) {
02655 astman_send_error(s, m, "No timeout specified");
02656 return 0;
02657 }
02658 c = ast_get_channel_by_name_locked(name);
02659 if (!c) {
02660 astman_send_error(s, m, "No such channel");
02661 return 0;
02662 }
02663
02664 when.tv_usec = (timeout - when.tv_sec) * 1000000.0;
02665 ast_channel_setwhentohangup_tv(c, when);
02666 ast_channel_unlock(c);
02667 astman_send_ack(s, m, "Timeout Set");
02668 return 0;
02669 }
02670
02671
02672
02673
02674
02675
02676 static int process_events(struct mansession *s)
02677 {
02678 int ret = 0;
02679
02680 ast_mutex_lock(&s->session->__lock);
02681 if (s->session->f != NULL) {
02682 struct eventqent *eqe;
02683
02684 while ( (eqe = NEW_EVENT(s)) ) {
02685 ref_event(eqe);
02686 if (!ret && s->session->authenticated &&
02687 (s->session->readperm & eqe->category) == eqe->category &&
02688 (s->session->send_events & eqe->category) == eqe->category) {
02689 if (send_string(s, eqe->eventdata) < 0)
02690 ret = -1;
02691 }
02692 s->session->last_ev = unref_event(s->session->last_ev);
02693 }
02694 }
02695 ast_mutex_unlock(&s->session->__lock);
02696 return ret;
02697 }
02698
02699 static char mandescr_userevent[] =
02700 "Description: Send an event to manager sessions.\n"
02701 "Variables: (Names marked with * are required)\n"
02702 " *UserEvent: EventStringToSend\n"
02703 " Header1: Content1\n"
02704 " HeaderN: ContentN\n";
02705
02706 static int action_userevent(struct mansession *s, const struct message *m)
02707 {
02708 const char *event = astman_get_header(m, "UserEvent");
02709 struct ast_str *body = ast_str_thread_get(&userevent_buf, 16);
02710 int x;
02711
02712 ast_str_reset(body);
02713
02714 for (x = 0; x < m->hdrcount; x++) {
02715 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
02716 ast_str_append(&body, 0, "%s\r\n", m->headers[x]);
02717 }
02718 }
02719
02720 astman_send_ack(s, m, "Event Sent");
02721 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, ast_str_buffer(body));
02722 return 0;
02723 }
02724
02725 static char mandescr_coresettings[] =
02726 "Description: Query for Core PBX settings.\n"
02727 "Variables: (Names marked with * are optional)\n"
02728 " *ActionID: ActionID of this transaction\n";
02729
02730
02731 static int action_coresettings(struct mansession *s, const struct message *m)
02732 {
02733 const char *actionid = astman_get_header(m, "ActionID");
02734 char idText[150];
02735
02736 if (!ast_strlen_zero(actionid))
02737 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02738 else
02739 idText[0] = '\0';
02740
02741 astman_append(s, "Response: Success\r\n"
02742 "%s"
02743 "AMIversion: %s\r\n"
02744 "AsteriskVersion: %s\r\n"
02745 "SystemName: %s\r\n"
02746 "CoreMaxCalls: %d\r\n"
02747 "CoreMaxLoadAvg: %f\r\n"
02748 "CoreRunUser: %s\r\n"
02749 "CoreRunGroup: %s\r\n"
02750 "CoreMaxFilehandles: %d\r\n"
02751 "CoreRealTimeEnabled: %s\r\n"
02752 "CoreCDRenabled: %s\r\n"
02753 "CoreHTTPenabled: %s\r\n"
02754 "\r\n",
02755 idText,
02756 AMI_VERSION,
02757 ast_get_version(),
02758 ast_config_AST_SYSTEM_NAME,
02759 option_maxcalls,
02760 option_maxload,
02761 ast_config_AST_RUN_USER,
02762 ast_config_AST_RUN_GROUP,
02763 option_maxfiles,
02764 ast_realtime_enabled() ? "Yes" : "No",
02765 check_cdr_enabled() ? "Yes" : "No",
02766 check_webmanager_enabled() ? "Yes" : "No"
02767 );
02768 return 0;
02769 }
02770
02771 static char mandescr_corestatus[] =
02772 "Description: Query for Core PBX status.\n"
02773 "Variables: (Names marked with * are optional)\n"
02774 " *ActionID: ActionID of this transaction\n";
02775
02776
02777 static int action_corestatus(struct mansession *s, const struct message *m)
02778 {
02779 const char *actionid = astman_get_header(m, "ActionID");
02780 char idText[150];
02781 char startuptime[150];
02782 char reloadtime[150];
02783 struct ast_tm tm;
02784
02785 if (!ast_strlen_zero(actionid))
02786 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02787 else
02788 idText[0] = '\0';
02789
02790 ast_localtime(&ast_startuptime, &tm, NULL);
02791 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
02792 ast_localtime(&ast_lastreloadtime, &tm, NULL);
02793 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
02794
02795 astman_append(s, "Response: Success\r\n"
02796 "%s"
02797 "CoreStartupTime: %s\r\n"
02798 "CoreReloadTime: %s\r\n"
02799 "CoreCurrentCalls: %d\r\n"
02800 "\r\n",
02801 idText,
02802 startuptime,
02803 reloadtime,
02804 ast_active_channels()
02805 );
02806 return 0;
02807 }
02808
02809 static char mandescr_reload[] =
02810 "Description: Send a reload event.\n"
02811 "Variables: (Names marked with * are optional)\n"
02812 " *ActionID: ActionID of this transaction\n"
02813 " *Module: Name of the module to reload\n";
02814
02815
02816 static int action_reload(struct mansession *s, const struct message *m)
02817 {
02818 const char *module = astman_get_header(m, "Module");
02819 int res = ast_module_reload(S_OR(module, NULL));
02820
02821 if (res == 2)
02822 astman_send_ack(s, m, "Module Reloaded");
02823 else
02824 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
02825 return 0;
02826 }
02827
02828 static char mandescr_coreshowchannels[] =
02829 "Description: List currently defined channels and some information\n"
02830 " about them.\n"
02831 "Variables:\n"
02832 " ActionID: Optional Action id for message matching.\n";
02833
02834
02835
02836 static int action_coreshowchannels(struct mansession *s, const struct message *m)
02837 {
02838 const char *actionid = astman_get_header(m, "ActionID");
02839 char idText[256];
02840 struct ast_channel *c = NULL;
02841 int numchans = 0;
02842 int duration, durh, durm, durs;
02843
02844 if (!ast_strlen_zero(actionid))
02845 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02846 else
02847 idText[0] = '\0';
02848
02849 astman_send_listack(s, m, "Channels will follow", "start");
02850
02851 while ((c = ast_channel_walk_locked(c)) != NULL) {
02852 struct ast_channel *bc = ast_bridged_channel(c);
02853 char durbuf[10] = "";
02854
02855 if (c->cdr && !ast_tvzero(c->cdr->start)) {
02856 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
02857 durh = duration / 3600;
02858 durm = (duration % 3600) / 60;
02859 durs = duration % 60;
02860 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
02861 }
02862
02863 astman_append(s,
02864 "Event: CoreShowChannel\r\n"
02865 "%s"
02866 "Channel: %s\r\n"
02867 "UniqueID: %s\r\n"
02868 "Context: %s\r\n"
02869 "Extension: %s\r\n"
02870 "Priority: %d\r\n"
02871 "ChannelState: %d\r\n"
02872 "ChannelStateDesc: %s\r\n"
02873 "Application: %s\r\n"
02874 "ApplicationData: %s\r\n"
02875 "CallerIDnum: %s\r\n"
02876 "Duration: %s\r\n"
02877 "AccountCode: %s\r\n"
02878 "BridgedChannel: %s\r\n"
02879 "BridgedUniqueID: %s\r\n"
02880 "\r\n", idText, c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state,
02881 ast_state2str(c->_state), c->appl ? c->appl : "", c->data ? S_OR(c->data, "") : "",
02882 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
02883 ast_channel_unlock(c);
02884 numchans++;
02885 }
02886
02887 astman_append(s,
02888 "Event: CoreShowChannelsComplete\r\n"
02889 "EventList: Complete\r\n"
02890 "ListItems: %d\r\n"
02891 "%s"
02892 "\r\n", numchans, idText);
02893
02894 return 0;
02895 }
02896
02897 static char mandescr_modulecheck[] =
02898 "Description: Checks if Asterisk module is loaded\n"
02899 "Variables: \n"
02900 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
02901 " Module: <name> Asterisk module name (not including extension)\n"
02902 "\n"
02903 "Will return Success/Failure\n"
02904 "For success returns, the module revision number is included.\n";
02905
02906
02907 static int manager_modulecheck(struct mansession *s, const struct message *m)
02908 {
02909 int res;
02910 const char *module = astman_get_header(m, "Module");
02911 const char *id = astman_get_header(m, "ActionID");
02912 char idText[256];
02913 #if !defined(LOW_MEMORY)
02914 const char *version;
02915 #endif
02916 char filename[PATH_MAX];
02917 char *cut;
02918
02919 ast_copy_string(filename, module, sizeof(filename));
02920 if ((cut = strchr(filename, '.'))) {
02921 *cut = '\0';
02922 } else {
02923 cut = filename + strlen(filename);
02924 }
02925 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
02926 ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
02927 res = ast_module_check(filename);
02928 if (!res) {
02929 astman_send_error(s, m, "Module not loaded");
02930 return 0;
02931 }
02932 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
02933 ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
02934 #if !defined(LOW_MEMORY)
02935 version = ast_file_version_find(filename);
02936 #endif
02937
02938 if (!ast_strlen_zero(id))
02939 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02940 else
02941 idText[0] = '\0';
02942 astman_append(s, "Response: Success\r\n%s", idText);
02943 #if !defined(LOW_MEMORY)
02944 astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
02945 #endif
02946 return 0;
02947 }
02948
02949 static char mandescr_moduleload[] =
02950 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
02951 "Variables: \n"
02952 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
02953 " Module: <name> Asterisk module name (including .so extension)\n"
02954 " or subsystem identifier:\n"
02955 " cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
02956 " LoadType: load | unload | reload\n"
02957 " The operation to be done on module\n"
02958 " If no module is specified for a reload loadtype, all modules are reloaded";
02959
02960 static int manager_moduleload(struct mansession *s, const struct message *m)
02961 {
02962 int res;
02963 const char *module = astman_get_header(m, "Module");
02964 const char *loadtype = astman_get_header(m, "LoadType");
02965
02966 if (!loadtype || strlen(loadtype) == 0)
02967 astman_send_error(s, m, "Incomplete ModuleLoad action.");
02968 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
02969 astman_send_error(s, m, "Need module name");
02970
02971 if (!strcasecmp(loadtype, "load")) {
02972 res = ast_load_resource(module);
02973 if (res)
02974 astman_send_error(s, m, "Could not load module.");
02975 else
02976 astman_send_ack(s, m, "Module loaded.");
02977 } else if (!strcasecmp(loadtype, "unload")) {
02978 res = ast_unload_resource(module, AST_FORCE_SOFT);
02979 if (res)
02980 astman_send_error(s, m, "Could not unload module.");
02981 else
02982 astman_send_ack(s, m, "Module unloaded.");
02983 } else if (!strcasecmp(loadtype, "reload")) {
02984 if (module != NULL) {
02985 res = ast_module_reload(module);
02986 if (res == 0)
02987 astman_send_error(s, m, "No such module.");
02988 else if (res == 1)
02989 astman_send_error(s, m, "Module does not support reload action.");
02990 else
02991 astman_send_ack(s, m, "Module reloaded.");
02992 } else {
02993 ast_module_reload(NULL);
02994 astman_send_ack(s, m, "All modules reloaded");
02995 }
02996 } else
02997 astman_send_error(s, m, "Incomplete ModuleLoad action.");
02998 return 0;
02999 }
03000
03001
03002
03003
03004
03005
03006
03007
03008
03009
03010
03011
03012
03013
03014 static int process_message(struct mansession *s, const struct message *m)
03015 {
03016 char action[80] = "";
03017 int ret = 0;
03018 struct manager_action *tmp;
03019 const char *user = astman_get_header(m, "Username");
03020
03021 ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
03022 ast_debug(1, "Manager received command '%s'\n", action);
03023
03024 if (ast_strlen_zero(action)) {
03025 ast_mutex_lock(&s->session->__lock);
03026 astman_send_error(s, m, "Missing action in request");
03027 ast_mutex_unlock(&s->session->__lock);
03028 return 0;
03029 }
03030
03031 if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
03032 ast_mutex_lock(&s->session->__lock);
03033 astman_send_error(s, m, "Permission denied");
03034 ast_mutex_unlock(&s->session->__lock);
03035 return 0;
03036 }
03037
03038 if (!allowmultiplelogin && !s->session->authenticated && user &&
03039 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
03040 if (check_manager_session_inuse(user)) {
03041 sleep(1);
03042 ast_mutex_lock(&s->session->__lock);
03043 astman_send_error(s, m, "Login Already In Use");
03044 ast_mutex_unlock(&s->session->__lock);
03045 return -1;
03046 }
03047 }
03048
03049 AST_RWLIST_RDLOCK(&actions);
03050 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
03051 if (strcasecmp(action, tmp->action))
03052 continue;
03053 if (s->session->writeperm & tmp->authority || tmp->authority == 0)
03054 ret = tmp->func(s, m);
03055 else
03056 astman_send_error(s, m, "Permission denied");
03057 break;
03058 }
03059 AST_RWLIST_UNLOCK(&actions);
03060
03061 if (!tmp) {
03062 char buf[512];
03063 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
03064 ast_mutex_lock(&s->session->__lock);
03065 astman_send_error(s, m, buf);
03066 ast_mutex_unlock(&s->session->__lock);
03067 }
03068 if (ret)
03069 return ret;
03070
03071
03072
03073 if (ast_strlen_zero(astman_get_header(m, "SuppressEvents"))) {
03074 return process_events(s);
03075 } else {
03076 return ret;
03077 }
03078 }
03079
03080
03081
03082
03083
03084
03085
03086
03087
03088
03089 static int get_input(struct mansession *s, char *output)
03090 {
03091 int res, x;
03092 int maxlen = sizeof(s->session->inbuf) - 1;
03093 char *src = s->session->inbuf;
03094
03095
03096
03097
03098
03099 for (x = 0; x < s->session->inlen; x++) {
03100 int cr;
03101 if (src[x] == '\r' && x+1 < s->session->inlen && src[x+1] == '\n')
03102 cr = 2;
03103 else if (src[x] == '\n')
03104 cr = 1;
03105 else
03106 continue;
03107 memmove(output, src, x);
03108 output[x] = '\0';
03109 x += cr;
03110 s->session->inlen -= x;
03111 memmove(src, src + x, s->session->inlen);
03112 return 1;
03113 }
03114 if (s->session->inlen >= maxlen) {
03115
03116 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->session->sin.sin_addr), src);
03117 s->session->inlen = 0;
03118 }
03119 res = 0;
03120 while (res == 0) {
03121
03122 ast_mutex_lock(&s->session->__lock);
03123 if (s->session->pending_event) {
03124 s->session->pending_event = 0;
03125 ast_mutex_unlock(&s->session->__lock);
03126 return 0;
03127 }
03128 s->session->waiting_thread = pthread_self();
03129 ast_mutex_unlock(&s->session->__lock);
03130
03131 res = ast_wait_for_input(s->session->fd, -1);
03132
03133 ast_mutex_lock(&s->session->__lock);
03134 s->session->waiting_thread = AST_PTHREADT_NULL;
03135 ast_mutex_unlock(&s->session->__lock);
03136 }
03137 if (res < 0) {
03138
03139
03140
03141 if (errno == EINTR || errno == EAGAIN)
03142 return 0;
03143 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
03144 return -1;
03145 }
03146 ast_mutex_lock(&s->session->__lock);
03147 res = fread(src + s->session->inlen, 1, maxlen - s->session->inlen, s->session->f);
03148 if (res < 1)
03149 res = -1;
03150 else {
03151 s->session->inlen += res;
03152 src[s->session->inlen] = '\0';
03153 res = 0;
03154 }
03155 ast_mutex_unlock(&s->session->__lock);
03156 return res;
03157 }
03158
03159 static int do_message(struct mansession *s)
03160 {
03161 struct message m = { 0 };
03162 char header_buf[sizeof(s->session->inbuf)] = { '\0' };
03163 int res;
03164
03165 for (;;) {
03166
03167 if (process_events(s))
03168 return -1;
03169 res = get_input(s, header_buf);
03170 if (res == 0) {
03171 continue;
03172 } else if (res > 0) {
03173 if (ast_strlen_zero(header_buf))
03174 return process_message(s, &m) ? -1 : 0;
03175 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
03176 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
03177 } else {
03178 return res;
03179 }
03180 }
03181 }
03182
03183
03184
03185
03186
03187
03188
03189
03190
03191 static void *session_do(void *data)
03192 {
03193 struct ast_tcptls_session_instance *ser = data;
03194 struct mansession_session *session = ast_calloc(1, sizeof(*session));
03195 struct mansession s = {.session = NULL, };
03196 int flags;
03197 int res;
03198
03199 if (session == NULL)
03200 goto done;
03201
03202 session->writetimeout = 100;
03203 session->waiting_thread = AST_PTHREADT_NULL;
03204
03205 flags = fcntl(ser->fd, F_GETFL);
03206 if (!block_sockets)
03207 flags |= O_NONBLOCK;
03208 else
03209 flags &= ~O_NONBLOCK;
03210 fcntl(ser->fd, F_SETFL, flags);
03211
03212 ast_mutex_init(&session->__lock);
03213 session->send_events = -1;
03214
03215 session->last_ev = grab_last();
03216
03217
03218 session->fd = ser->fd;
03219 session->f = ser->f;
03220 session->sin = ser->remote_address;
03221 s.session = session;
03222
03223 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
03224
03225 AST_LIST_LOCK(&sessions);
03226 AST_LIST_INSERT_HEAD(&sessions, session, list);
03227 ast_atomic_fetchadd_int(&num_sessions, 1);
03228 AST_LIST_UNLOCK(&sessions);
03229
03230 astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);
03231 for (;;) {
03232 if ((res = do_message(&s)) < 0)
03233 break;
03234 }
03235
03236 if (session->authenticated) {
03237 if (manager_displayconnects(session))
03238 ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03239 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03240 } else {
03241 if (displayconnects)
03242 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
03243 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
03244 }
03245
03246
03247
03248
03249
03250
03251
03252
03253
03254
03255
03256
03257 usleep(1);
03258
03259 destroy_session(session);
03260
03261 done:
03262 ao2_ref(ser, -1);
03263 ser = NULL;
03264 return NULL;
03265 }
03266
03267
03268 static void purge_sessions(int n_max)
03269 {
03270 struct mansession_session *session;
03271 time_t now = time(NULL);
03272
03273 AST_LIST_LOCK(&sessions);
03274 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, session, list) {
03275 if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
03276 AST_LIST_REMOVE_CURRENT(list);
03277 ast_atomic_fetchadd_int(&num_sessions, -1);
03278 if (session->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(session)) {
03279 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
03280 session->username, ast_inet_ntoa(session->sin.sin_addr));
03281 }
03282 free_session(session);
03283 if (--n_max <= 0)
03284 break;
03285 }
03286 }
03287 AST_LIST_TRAVERSE_SAFE_END;
03288 AST_LIST_UNLOCK(&sessions);
03289 }
03290
03291
03292
03293
03294
03295 static int append_event(const char *str, int category)
03296 {
03297 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
03298 static int seq;
03299
03300 if (!tmp)
03301 return -1;
03302
03303
03304 tmp->usecount = 0;
03305 tmp->category = category;
03306 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
03307 AST_LIST_NEXT(tmp, eq_next) = NULL;
03308 strcpy(tmp->eventdata, str);
03309
03310 AST_LIST_LOCK(&all_events);
03311 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
03312 AST_LIST_UNLOCK(&all_events);
03313
03314 return 0;
03315 }
03316
03317
03318 AST_THREADSTORAGE(manager_event_buf);
03319 #define MANAGER_EVENT_BUF_INITSIZE 256
03320
03321
03322 int __manager_event(int category, const char *event,
03323 const char *file, int line, const char *func, const char *fmt, ...)
03324 {
03325 struct mansession_session *session;
03326 struct manager_custom_hook *hook;
03327 struct ast_str *auth = ast_str_alloca(80);
03328 const char *cat_str;
03329 va_list ap;
03330 struct timeval now;
03331 struct ast_str *buf;
03332
03333
03334 if (!num_sessions && AST_RWLIST_EMPTY(&manager_hooks))
03335 return 0;
03336
03337 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
03338 return -1;
03339
03340 cat_str = authority_to_str(category, &auth);
03341 ast_str_set(&buf, 0,
03342 "Event: %s\r\nPrivilege: %s\r\n",
03343 event, cat_str);
03344
03345 if (timestampevents) {
03346 now = ast_tvnow();
03347 ast_str_append(&buf, 0,
03348 "Timestamp: %ld.%06lu\r\n",
03349 (long)now.tv_sec, (unsigned long) now.tv_usec);
03350 }
03351 if (manager_debug) {
03352 static int seq;
03353 ast_str_append(&buf, 0,
03354 "SequenceNumber: %d\r\n",
03355 ast_atomic_fetchadd_int(&seq, 1));
03356 ast_str_append(&buf, 0,
03357 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
03358 }
03359
03360 va_start(ap, fmt);
03361 ast_str_append_va(&buf, 0, fmt, ap);
03362 va_end(ap);
03363
03364 ast_str_append(&buf, 0, "\r\n");
03365
03366 append_event(ast_str_buffer(buf), category);
03367
03368 if (num_sessions) {
03369
03370 AST_LIST_LOCK(&sessions);
03371 AST_LIST_TRAVERSE(&sessions, session, list) {
03372 ast_mutex_lock(&session->__lock);
03373 if (session->waiting_thread != AST_PTHREADT_NULL)
03374 pthread_kill(session->waiting_thread, SIGURG);
03375 else
03376
03377
03378
03379
03380
03381 session->pending_event = 1;
03382 ast_mutex_unlock(&session->__lock);
03383 }
03384 AST_LIST_UNLOCK(&sessions);
03385 }
03386
03387 if (!AST_RWLIST_EMPTY(&manager_hooks)) {
03388 AST_RWLIST_RDLOCK(&manager_hooks);
03389 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
03390 hook->helper(category, event, ast_str_buffer(buf));
03391 }
03392 AST_RWLIST_UNLOCK(&manager_hooks);
03393 }
03394
03395 return 0;
03396 }
03397
03398
03399
03400
03401 int ast_manager_unregister(char *action)
03402 {
03403 struct manager_action *cur;
03404 struct timespec tv = { 5, };
03405
03406 if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
03407 ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
03408 return -1;
03409 }
03410 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
03411 if (!strcasecmp(action, cur->action)) {
03412 AST_RWLIST_REMOVE_CURRENT(list);
03413 ast_free(cur);
03414 ast_verb(2, "Manager unregistered action %s\n", action);
03415 break;
03416 }
03417 }
03418 AST_RWLIST_TRAVERSE_SAFE_END;
03419 AST_RWLIST_UNLOCK(&actions);
03420
03421 return 0;
03422 }
03423
03424 static int manager_state_cb(char *context, char *exten, int state, void *data)
03425 {
03426
03427 char hint[512];
03428 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
03429
03430 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
03431 return 0;
03432 }
03433
03434 static int ast_manager_register_struct(struct manager_action *act)
03435 {
03436 struct manager_action *cur, *prev = NULL;
03437 struct timespec tv = { 5, };
03438
03439 if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
03440 ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
03441 return -1;
03442 }
03443 AST_RWLIST_TRAVERSE(&actions, cur, list) {
03444 int ret = strcasecmp(cur->action, act->action);
03445 if (ret == 0) {
03446 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
03447 AST_RWLIST_UNLOCK(&actions);
03448 return -1;
03449 }
03450 if (ret > 0) {
03451 prev = cur;
03452 break;
03453 }
03454 }
03455
03456 if (prev)
03457 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
03458 else
03459 AST_RWLIST_INSERT_HEAD(&actions, act, list);
03460
03461 ast_verb(2, "Manager registered action %s\n", act->action);
03462
03463 AST_RWLIST_UNLOCK(&actions);
03464
03465 return 0;
03466 }
03467
03468
03469
03470 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
03471 {
03472 struct manager_action *cur = NULL;
03473
03474 if (!(cur = ast_calloc(1, sizeof(*cur))))
03475 return -1;
03476
03477 cur->action = action;
03478 cur->authority = auth;
03479 cur->func = func;
03480 cur->synopsis = synopsis;
03481 cur->description = description;
03482
03483 if (ast_manager_register_struct(cur)) {
03484 ast_free(cur);
03485 return -1;
03486 }
03487
03488 return 0;
03489 }
03490
03491
03492
03493
03494
03495
03496
03497
03498
03499
03500
03501
03502
03503
03504
03505 enum output_format {
03506 FORMAT_RAW,
03507 FORMAT_HTML,
03508 FORMAT_XML,
03509 };
03510
03511 static char *contenttype[] = {
03512 [FORMAT_RAW] = "plain",
03513 [FORMAT_HTML] = "html",
03514 [FORMAT_XML] = "xml",
03515 };
03516
03517
03518
03519
03520
03521
03522 static struct mansession_session *find_session(uint32_t ident, int incinuse)
03523 {
03524 struct mansession_session *session;
03525
03526 if (ident == 0)
03527 return NULL;
03528
03529 AST_LIST_LOCK(&sessions);
03530 AST_LIST_TRAVERSE(&sessions, session, list) {
03531 ast_mutex_lock(&session->__lock);
03532 if (session->managerid == ident && !session->needdestroy) {
03533 ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
03534 break;
03535 }
03536 ast_mutex_unlock(&session->__lock);
03537 }
03538 AST_LIST_UNLOCK(&sessions);
03539
03540 return session;
03541 }
03542
03543 int astman_is_authed(uint32_t ident)
03544 {
03545 int authed;
03546 struct mansession_session *session;
03547
03548 if (!(session = find_session(ident, 0)))
03549 return 0;
03550
03551 authed = (session->authenticated != 0);
03552
03553 ast_mutex_unlock(&session->__lock);
03554
03555 return authed;
03556 }
03557
03558 int astman_verify_session_readpermissions(uint32_t ident, int perm)
03559 {
03560 int result = 0;
03561 struct mansession_session *session;
03562
03563 AST_LIST_LOCK(&sessions);
03564 AST_LIST_TRAVERSE(&sessions, session, list) {
03565 ast_mutex_lock(&session->__lock);
03566 if ((session->managerid == ident) && (session->readperm & perm)) {
03567 result = 1;
03568 ast_mutex_unlock(&session->__lock);
03569 break;
03570 }
03571 ast_mutex_unlock(&session->__lock);
03572 }
03573 AST_LIST_UNLOCK(&sessions);
03574 return result;
03575 }
03576
03577 int astman_verify_session_writepermissions(uint32_t ident, int perm)
03578 {
03579 int result = 0;
03580 struct mansession_session *session;
03581
03582 AST_LIST_LOCK(&sessions);
03583 AST_LIST_TRAVERSE(&sessions, session, list) {
03584 ast_mutex_lock(&session->__lock);
03585 if ((session->managerid == ident) && (session->writeperm & perm)) {
03586 result = 1;
03587 ast_mutex_unlock(&session->__lock);
03588 break;
03589 }
03590 ast_mutex_unlock(&session->__lock);
03591 }
03592 AST_LIST_UNLOCK(&sessions);
03593 return result;
03594 }
03595
03596
03597
03598
03599
03600
03601 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
03602 {
03603
03604 char buf[256];
03605 char *dst = buf;
03606 int space = sizeof(buf);
03607
03608 for ( ; *src || dst != buf ; src++) {
03609 if (*src == '\0' || space < 10) {
03610 *dst++ = '\0';
03611 ast_str_append(out, 0, "%s", buf);
03612 dst = buf;
03613 space = sizeof(buf);
03614 if (*src == '\0')
03615 break;
03616 }
03617
03618 if ( (mode & 2) && !isalnum(*src)) {
03619 *dst++ = '_';
03620 space--;
03621 continue;
03622 }
03623 switch (*src) {
03624 case '<':
03625 strcpy(dst, "<");
03626 dst += 4;
03627 space -= 4;
03628 break;
03629 case '>':
03630 strcpy(dst, ">");
03631 dst += 4;
03632 space -= 4;
03633 break;
03634 case '\"':
03635 strcpy(dst, """);
03636 dst += 6;
03637 space -= 6;
03638 break;
03639 case '\'':
03640 strcpy(dst, "'");
03641 dst += 6;
03642 space -= 6;
03643 break;
03644 case '&':
03645 strcpy(dst, "&");
03646 dst += 5;
03647 space -= 5;
03648 break;
03649
03650 default:
03651 *dst++ = mode ? tolower(*src) : *src;
03652 space--;
03653 }
03654 }
03655 }
03656
03657 struct variable_count {
03658 char *varname;
03659 int count;
03660 };
03661
03662 static int compress_char(char c)
03663 {
03664 c &= 0x7f;
03665 if (c < 32)
03666 return 0;
03667 else if (c >= 'a' && c <= 'z')
03668 return c - 64;
03669 else if (c > 'z')
03670 return '_';
03671 else
03672 return c - 32;
03673 }
03674
03675 static int variable_count_hash_fn(const void *vvc, const int flags)
03676 {
03677 const struct variable_count *vc = vvc;
03678 int res = 0, i;
03679 for (i = 0; i < 5; i++) {
03680 if (vc->varname[i] == '\0')
03681 break;
03682 res += compress_char(vc->varname[i]) << (i * 6);
03683 }
03684 return res;
03685 }
03686
03687 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
03688 {
03689
03690
03691
03692
03693 struct variable_count *vc = obj;
03694 char *str = vstr;
03695 return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
03696 }
03697
03698
03699
03700
03701
03702
03703
03704
03705
03706
03707
03708
03709
03710
03711
03712
03713
03714
03715
03716
03717
03718
03719
03720
03721
03722
03723
03724
03725
03726 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
03727 {
03728 struct ast_variable *v;
03729 const char *dest = NULL;
03730 char *var, *val;
03731 const char *objtype = NULL;
03732 int in_data = 0;
03733 int inobj = 0;
03734 int xml = (format == FORMAT_XML);
03735 struct variable_count *vc = NULL;
03736 struct ao2_container *vco = NULL;
03737
03738 for (v = vars; v; v = v->next) {
03739 if (!dest && !strcasecmp(v->name, "ajaxdest"))
03740 dest = v->value;
03741 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
03742 objtype = v->value;
03743 }
03744 if (!dest)
03745 dest = "unknown";
03746 if (!objtype)
03747 objtype = "generic";
03748
03749
03750 while (in && *in) {
03751 val = strsep(&in, "\r\n");
03752 if (in && *in == '\n')
03753 in++;
03754 ast_trim_blanks(val);
03755 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
03756 if (ast_strlen_zero(val)) {
03757 if (in_data) {
03758 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
03759 in_data = 0;
03760 }
03761 if (inobj) {
03762 ast_str_append(out, 0, xml ? " /></response>\n" :
03763 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
03764 inobj = 0;
03765 ao2_ref(vco, -1);
03766 vco = NULL;
03767 }
03768 continue;
03769 }
03770
03771
03772 if (in_data) {
03773 var = NULL;
03774 } else {
03775 var = strsep(&val, ":");
03776 if (val) {
03777 val = ast_skip_blanks(val);
03778 ast_trim_blanks(var);
03779 } else {
03780 val = var;
03781 var = "Opaque-data";
03782 }
03783 }
03784
03785 if (!inobj) {
03786 if (xml)
03787 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
03788 else
03789 ast_str_append(out, 0, "<body>\n");
03790 vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
03791 inobj = 1;
03792 }
03793
03794 if (!in_data) {
03795 ast_str_append(out, 0, xml ? " " : "<tr><td>");
03796 if ((vc = ao2_find(vco, var, 0)))
03797 vc->count++;
03798 else {
03799
03800 vc = ao2_alloc(sizeof(*vc), NULL);
03801 vc->varname = var;
03802 vc->count = 1;
03803 ao2_link(vco, vc);
03804 }
03805 xml_copy_escape(out, var, xml ? 1 | 2 : 0);
03806 if (vc->count > 1)
03807 ast_str_append(out, 0, "-%d", vc->count);
03808 ao2_ref(vc, -1);
03809 ast_str_append(out, 0, xml ? "='" : "</td><td>");
03810 if (!strcmp(var, "Opaque-data"))
03811 in_data = 1;
03812 }
03813 xml_copy_escape(out, val, 0);
03814 if (!in_data)
03815 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
03816 else
03817 ast_str_append(out, 0, xml ? "\n" : "<br>\n");
03818 }
03819 if (inobj) {
03820 ast_str_append(out, 0, xml ? " /></response>\n" :
03821 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
03822 ao2_ref(vco, -1);
03823 }
03824 }
03825
03826 static struct ast_str *generic_http_callback(enum output_format format,
03827 struct sockaddr_in *remote_address, const char *uri, enum ast_http_method method,
03828 struct ast_variable *params, int *status,
03829 char **title, int *contentlength)
03830 {
03831 struct mansession s = {.session = NULL, };
03832 struct mansession_session *session = NULL;
03833 uint32_t ident = 0;
03834 int blastaway = 0;
03835 struct ast_variable *v;
03836 char template[] = "/tmp/ast-http-XXXXXX";
03837 struct ast_str *out = NULL;
03838 struct message m = { 0 };
03839 unsigned int x;
03840 size_t hdrlen;
03841
03842 for (v = params; v; v = v->next) {
03843 if (!strcasecmp(v->name, "mansession_id")) {
03844 sscanf(v->value, "%30x", &ident);
03845 break;
03846 }
03847 }
03848
03849 if (!(session = find_session(ident, 1))) {
03850
03851
03852
03853 if (!(session = ast_calloc(1, sizeof(*session)))) {
03854 *status = 500;
03855 goto generic_callback_out;
03856 }
03857 session->sin = *remote_address;
03858 session->fd = -1;
03859 session->waiting_thread = AST_PTHREADT_NULL;
03860 session->send_events = 0;
03861 ast_mutex_init(&session->__lock);
03862 ast_mutex_lock(&session->__lock);
03863 session->inuse = 1;
03864
03865
03866
03867
03868
03869 while ((session->managerid = ast_random() ^ (unsigned long) session) == 0);
03870 session->last_ev = grab_last();
03871 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
03872 AST_LIST_LOCK(&sessions);
03873 AST_LIST_INSERT_HEAD(&sessions, session, list);
03874 ast_atomic_fetchadd_int(&num_sessions, 1);
03875 AST_LIST_UNLOCK(&sessions);
03876 }
03877
03878 s.session = session;
03879
03880 ast_mutex_unlock(&session->__lock);
03881
03882 if (!(out = ast_str_create(1024))) {
03883 *status = 500;
03884 goto generic_callback_out;
03885 }
03886
03887 s.fd = mkstemp(template);
03888 unlink(template);
03889 s.f = fdopen(s.fd, "w+");
03890
03891 for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
03892 hdrlen = strlen(v->name) + strlen(v->value) + 3;
03893 m.headers[m.hdrcount] = alloca(hdrlen);
03894 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
03895 ast_verb(4, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
03896 m.hdrcount = x + 1;
03897 }
03898
03899 if (process_message(&s, &m)) {
03900 if (session->authenticated) {
03901 if (manager_displayconnects(session)) {
03902 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03903 }
03904 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03905 } else {
03906 if (displayconnects) {
03907 ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
03908 }
03909 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
03910 }
03911 session->needdestroy = 1;
03912 }
03913
03914 ast_str_append(&out, 0,
03915 "Content-type: text/%s\r\n"
03916 "Cache-Control: no-cache;\r\n"
03917 "Set-Cookie: mansession_id=\"%08x\"; Version=\"1\"; Max-Age=%d\r\n"
03918 "Pragma: SuppressEvents\r\n"
03919 "\r\n",
03920 contenttype[format],
03921 session->managerid, httptimeout);
03922
03923 if (format == FORMAT_XML) {
03924 ast_str_append(&out, 0, "<ajax-response>\n");
03925 } else if (format == FORMAT_HTML) {
03926
03927
03928
03929
03930
03931
03932 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
03933 #define TEST_STRING \
03934 "<form action=\"manager\">\n\
03935 Action: <select name=\"action\">\n\
03936 <option value=\"\">-----></option>\n\
03937 <option value=\"login\">login</option>\n\
03938 <option value=\"command\">Command</option>\n\
03939 <option value=\"waitevent\">waitevent</option>\n\
03940 <option value=\"listcommands\">listcommands</option>\n\
03941 </select>\n\
03942 or <input name=\"action\"><br/>\n\
03943 CLI Command <input name=\"command\"><br>\n\
03944 user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\
03945 <input type=\"submit\">\n</form>\n"
03946
03947 ast_str_append(&out, 0, "<title>Asterisk™ Manager Interface</title>");
03948 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
03949 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
03950 ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
03951 }
03952
03953 if (s.f != NULL) {
03954 char *buf;
03955 size_t l;
03956
03957
03958 fprintf(s.f, "%c", 0);
03959
03960 if ((l = ftell(s.f))) {
03961 if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s.fd, 0))) {
03962 if (format == FORMAT_XML || format == FORMAT_HTML)
03963 xml_translate(&out, buf, params, format);
03964 else
03965 ast_str_append(&out, 0, "%s", buf);
03966 munmap(buf, l);
03967 }
03968 } else if (format == FORMAT_XML || format == FORMAT_HTML) {
03969 xml_translate(&out, "", params, format);
03970 }
03971 fclose(s.f);
03972 s.f = NULL;
03973 s.fd = -1;
03974 }
03975
03976 if (format == FORMAT_XML) {
03977 ast_str_append(&out, 0, "</ajax-response>\n");
03978 } else if (format == FORMAT_HTML)
03979 ast_str_append(&out, 0, "</table></body>\r\n");
03980
03981 ast_mutex_lock(&session->__lock);
03982
03983 session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5);
03984
03985 if (session->needdestroy) {
03986 if (session->inuse == 1) {
03987 ast_debug(1, "Need destroy, doing it now!\n");
03988 blastaway = 1;
03989 } else {
03990 ast_debug(1, "Need destroy, but can't do it yet!\n");
03991 if (session->waiting_thread != AST_PTHREADT_NULL)
03992 pthread_kill(session->waiting_thread, SIGURG);
03993 session->inuse--;
03994 }
03995 } else
03996 session->inuse--;
03997 ast_mutex_unlock(&session->__lock);
03998
03999 if (blastaway)
04000 destroy_session(session);
04001 generic_callback_out:
04002 if (*status != 200)
04003 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
04004 return out;
04005 }
04006
04007 static struct ast_str *manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength)
04008 {
04009 return generic_http_callback(FORMAT_HTML, &ser->remote_address, uri, method, params, status, title, contentlength);
04010 }
04011
04012 static struct ast_str *mxml_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength)
04013 {
04014 return generic_http_callback(FORMAT_XML, &ser->remote_address, uri, method, params, status, title, contentlength);
04015 }
04016
04017 static struct ast_str *rawman_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength)
04018 {
04019 return generic_http_callback(FORMAT_RAW, &ser->remote_address, uri, method, params, status, title, contentlength);
04020 }
04021
04022 struct ast_http_uri rawmanuri = {
04023 .description = "Raw HTTP Manager Event Interface",
04024 .uri = "rawman",
04025 .callback = rawman_http_callback,
04026 .supports_get = 1,
04027 .data = NULL,
04028 .key = __FILE__,
04029 };
04030
04031 struct ast_http_uri manageruri = {
04032 .description = "HTML Manager Event Interface",
04033 .uri = "manager",
04034 .callback = manager_http_callback,
04035 .supports_get = 1,
04036 .data = NULL,
04037 .key = __FILE__,
04038 };
04039
04040 struct ast_http_uri managerxmluri = {
04041 .description = "XML Manager Event Interface",
04042 .uri = "mxml",
04043 .callback = mxml_http_callback,
04044 .supports_get = 1,
04045 .data = NULL,
04046 .key = __FILE__,
04047 };
04048
04049 static int registered = 0;
04050 static int webregged = 0;
04051
04052
04053
04054
04055 static void purge_old_stuff(void *data)
04056 {
04057 purge_sessions(1);
04058 purge_events();
04059 }
04060
04061 struct ast_tls_config ami_tls_cfg;
04062 static struct ast_tcptls_session_args ami_desc = {
04063 .accept_fd = -1,
04064 .master = AST_PTHREADT_NULL,
04065 .tls_cfg = NULL,
04066 .poll_timeout = 5000,
04067 .periodic_fn = purge_old_stuff,
04068 .name = "AMI server",
04069 .accept_fn = ast_tcptls_server_root,
04070 .worker_fn = session_do,
04071 };
04072
04073 static struct ast_tcptls_session_args amis_desc = {
04074 .accept_fd = -1,
04075 .master = AST_PTHREADT_NULL,
04076 .tls_cfg = &ami_tls_cfg,
04077 .poll_timeout = -1,
04078 .name = "AMI TLS server",
04079 .accept_fn = ast_tcptls_server_root,
04080 .worker_fn = session_do,
04081 };
04082
04083 static int __init_manager(int reload)
04084 {
04085 struct ast_config *ucfg = NULL, *cfg = NULL;
04086 const char *val;
04087 char *cat = NULL;
04088 int newhttptimeout = 60;
04089 int have_sslbindaddr = 0;
04090 struct hostent *hp;
04091 struct ast_hostent ahp;
04092 struct ast_manager_user *user = NULL;
04093 struct ast_variable *var;
04094 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
04095
04096 manager_enabled = 0;
04097
04098 if (!registered) {
04099
04100 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
04101 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
04102 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
04103 ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
04104 ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
04105 ast_manager_register2("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
04106 ast_manager_register2("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status, "Lists channel status", mandescr_status);
04107 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar);
04108 ast_manager_register2("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar, "Gets a Channel Variable", mandescr_getvar);
04109 ast_manager_register2("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
04110 ast_manager_register2("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson, "Retrieve configuration (JSON format)", mandescr_getconfigjson);
04111 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
04112 ast_manager_register2("CreateConfig", EVENT_FLAG_CONFIG, action_createconfig, "Creates an empty file in the configuration directory", mandescr_createconfig);
04113 ast_manager_register2("ListCategories", EVENT_FLAG_CONFIG, action_listcategories, "List categories in configuration file", mandescr_listcategories);
04114 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
04115 ast_manager_register2("Atxfer", EVENT_FLAG_CALL, action_atxfer, "Attended transfer", mandescr_atxfer);
04116 ast_manager_register2("Originate", EVENT_FLAG_ORIGINATE, action_originate, "Originate Call", mandescr_originate);
04117 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
04118 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
04119 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
04120 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
04121 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
04122 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
04123 ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
04124 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
04125 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
04126 ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
04127 ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
04128 ast_manager_register2("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload, "Send a reload event", mandescr_reload);
04129 ast_manager_register2("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels, "List currently active channels", mandescr_coreshowchannels);
04130 ast_manager_register2("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload, "Module management", mandescr_moduleload);
04131 ast_manager_register2("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck, "Check if module is loaded", mandescr_modulecheck);
04132
04133 ast_cli_register_multiple(cli_manager, ARRAY_LEN(cli_manager));
04134 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
04135 registered = 1;
04136
04137 append_event("Event: Placeholder\r\n\r\n", 0);
04138 }
04139 if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
04140 return 0;
04141
04142 displayconnects = 1;
04143 broken_events_action = 0;
04144 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
04145 ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf, or configuration is invalid. Asterisk management interface (AMI) disabled.\n");
04146 return 0;
04147 }
04148
04149
04150 memset(&ami_desc.local_address, 0, sizeof(struct sockaddr_in));
04151 memset(&amis_desc.local_address, 0, sizeof(amis_desc.local_address));
04152 amis_desc.local_address.sin_port = htons(5039);
04153 ami_desc.local_address.sin_port = htons(DEFAULT_MANAGER_PORT);
04154
04155 ami_tls_cfg.enabled = 0;
04156 if (ami_tls_cfg.certfile)
04157 ast_free(ami_tls_cfg.certfile);
04158 ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
04159 if (ami_tls_cfg.cipher)
04160 ast_free(ami_tls_cfg.cipher);
04161 ami_tls_cfg.cipher = ast_strdup("");
04162
04163 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
04164 val = var->value;
04165 if (!strcasecmp(var->name, "sslenable"))
04166 ami_tls_cfg.enabled = ast_true(val);
04167 else if (!strcasecmp(var->name, "sslbindport"))
04168 amis_desc.local_address.sin_port = htons(atoi(val));
04169 else if (!strcasecmp(var->name, "sslbindaddr")) {
04170 if ((hp = ast_gethostbyname(val, &ahp))) {
04171 memcpy(&amis_desc.local_address.sin_addr, hp->h_addr, sizeof(amis_desc.local_address.sin_addr));
04172 have_sslbindaddr = 1;
04173 } else {
04174 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val);
04175 }
04176 } else if (!strcasecmp(var->name, "sslcert")) {
04177 ast_free(ami_tls_cfg.certfile);
04178 ami_tls_cfg.certfile = ast_strdup(val);
04179 } else if (!strcasecmp(var->name, "sslcipher")) {
04180 ast_free(ami_tls_cfg.cipher);
04181 ami_tls_cfg.cipher = ast_strdup(val);
04182 } else if (!strcasecmp(var->name, "enabled")) {
04183 manager_enabled = ast_true(val);
04184 } else if (!strcasecmp(var->name, "block-sockets")) {
04185 block_sockets = ast_true(val);
04186 } else if (!strcasecmp(var->name, "webenabled")) {
04187 webmanager_enabled = ast_true(val);
04188 } else if (!strcasecmp(var->name, "port")) {
04189 ami_desc.local_address.sin_port = htons(atoi(val));
04190 } else if (!strcasecmp(var->name, "bindaddr")) {
04191 if (!inet_aton(val, &ami_desc.local_address.sin_addr)) {
04192 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
04193 memset(&ami_desc.local_address.sin_addr, 0, sizeof(ami_desc.local_address.sin_addr));
04194 }
04195 } else if (!strcasecmp(var->name, "brokeneventsaction")) {
04196 broken_events_action = ast_true(val);
04197 } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
04198 allowmultiplelogin = ast_true(val);
04199 } else if (!strcasecmp(var->name, "displayconnects")) {
04200 displayconnects = ast_true(val);
04201 } else if (!strcasecmp(var->name, "timestampevents")) {
04202 timestampevents = ast_true(val);
04203 } else if (!strcasecmp(var->name, "debug")) {
04204 manager_debug = ast_true(val);
04205 } else if (!strcasecmp(var->name, "httptimeout")) {
04206 newhttptimeout = atoi(val);
04207 } else {
04208 ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
04209 var->name, val);
04210 }
04211 }
04212
04213 if (manager_enabled)
04214 ami_desc.local_address.sin_family = AF_INET;
04215 if (!have_sslbindaddr)
04216 amis_desc.local_address.sin_addr = ami_desc.local_address.sin_addr;
04217 if (ami_tls_cfg.enabled)
04218 amis_desc.local_address.sin_family = AF_INET;
04219
04220
04221 AST_RWLIST_WRLOCK(&users);
04222
04223
04224 ucfg = ast_config_load2("users.conf", "manager", config_flags);
04225 if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED) && ucfg != CONFIG_STATUS_FILEINVALID) {
04226 const char *hasmanager;
04227 int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
04228
04229 while ((cat = ast_category_browse(ucfg, cat))) {
04230 if (!strcasecmp(cat, "general"))
04231 continue;
04232
04233 hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
04234 if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
04235 const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
04236 const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
04237 const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
04238 const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
04239 const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
04240
04241
04242
04243
04244 if (!(user = get_manager_by_name_locked(cat))) {
04245 if (!(user = ast_calloc(1, sizeof(*user))))
04246 break;
04247
04248
04249 ast_copy_string(user->username, cat, sizeof(user->username));
04250
04251 AST_LIST_INSERT_TAIL(&users, user, list);
04252 user->ha = NULL;
04253 user->keep = 1;
04254 user->readperm = -1;
04255 user->writeperm = -1;
04256
04257 user->displayconnects = displayconnects;
04258 user->writetimeout = 100;
04259 }
04260
04261 if (!user_secret)
04262 user_secret = ast_variable_retrieve(ucfg, "general", "secret");
04263 if (!user_read)
04264 user_read = ast_variable_retrieve(ucfg, "general", "read");
04265 if (!user_write)
04266 user_write = ast_variable_retrieve(ucfg, "general", "write");
04267 if (!user_displayconnects)
04268 user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
04269 if (!user_writetimeout)
04270 user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
04271
04272 if (!ast_strlen_zero(user_secret)) {
04273 if (user->secret)
04274 ast_free(user->secret);
04275 user->secret = ast_strdup(user_secret);
04276 }
04277
04278 if (user_read)
04279 user->readperm = get_perm(user_read);
04280 if (user_write)
04281 user->writeperm = get_perm(user_write);
04282 if (user_displayconnects)
04283 user->displayconnects = ast_true(user_displayconnects);
04284
04285 if (user_writetimeout) {
04286 int value = atoi(user_writetimeout);
04287 if (value < 100)
04288 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at users.conf line %d\n", var->value, var->lineno);
04289 else
04290 user->writetimeout = value;
04291 }
04292 }
04293 }
04294 ast_config_destroy(ucfg);
04295 }
04296
04297
04298
04299 while ((cat = ast_category_browse(cfg, cat))) {
04300 struct ast_ha *oldha;
04301
04302 if (!strcasecmp(cat, "general"))
04303 continue;
04304
04305
04306 if (!(user = get_manager_by_name_locked(cat))) {
04307 if (!(user = ast_calloc(1, sizeof(*user))))
04308 break;
04309
04310 ast_copy_string(user->username, cat, sizeof(user->username));
04311
04312 user->ha = NULL;
04313 user->readperm = 0;
04314 user->writeperm = 0;
04315
04316 user->displayconnects = displayconnects;
04317 user->writetimeout = 100;
04318
04319
04320 AST_RWLIST_INSERT_TAIL(&users, user, list);
04321 }
04322
04323
04324 user->keep = 1;
04325 oldha = user->ha;
04326 user->ha = NULL;
04327
04328 var = ast_variable_browse(cfg, cat);
04329 for (; var; var = var->next) {
04330 if (!strcasecmp(var->name, "secret")) {
04331 if (user->secret)
04332 ast_free(user->secret);
04333 user->secret = ast_strdup(var->value);
04334 } else if (!strcasecmp(var->name, "deny") ||
04335 !strcasecmp(var->name, "permit")) {
04336 user->ha = ast_append_ha(var->name, var->value, user->ha, NULL);
04337 } else if (!strcasecmp(var->name, "read") ) {
04338 user->readperm = get_perm(var->value);
04339 } else if (!strcasecmp(var->name, "write") ) {
04340 user->writeperm = get_perm(var->value);
04341 } else if (!strcasecmp(var->name, "displayconnects") ) {
04342 user->displayconnects = ast_true(var->value);
04343 } else if (!strcasecmp(var->name, "writetimeout")) {
04344 int value = atoi(var->value);
04345 if (value < 100)
04346 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
04347 else
04348 user->writetimeout = value;
04349 } else
04350 ast_debug(1, "%s is an unknown option.\n", var->name);
04351 }
04352 ast_free_ha(oldha);
04353 }
04354 ast_config_destroy(cfg);
04355
04356
04357 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
04358 if (user->keep) {
04359 user->keep = 0;
04360 continue;
04361 }
04362
04363 AST_RWLIST_REMOVE_CURRENT(list);
04364
04365 if (user->secret)
04366 ast_free(user->secret);
04367 ast_free_ha(user->ha);
04368 ast_free(user);
04369 }
04370 AST_RWLIST_TRAVERSE_SAFE_END;
04371
04372 AST_RWLIST_UNLOCK(&users);
04373
04374 if (webmanager_enabled && manager_enabled) {
04375 if (!webregged) {
04376 ast_http_uri_link(&rawmanuri);
04377 ast_http_uri_link(&manageruri);
04378 ast_http_uri_link(&managerxmluri);
04379 webregged = 1;
04380 }
04381 } else {
04382 if (webregged) {
04383 ast_http_uri_unlink(&rawmanuri);
04384 ast_http_uri_unlink(&manageruri);
04385 ast_http_uri_unlink(&managerxmluri);
04386 webregged = 0;
04387 }
04388 }
04389
04390 if (newhttptimeout > 0)
04391 httptimeout = newhttptimeout;
04392
04393 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled");
04394
04395 ast_tcptls_server_start(&ami_desc);
04396 if (ast_ssl_setup(amis_desc.tls_cfg))
04397 ast_tcptls_server_start(&amis_desc);
04398 return 0;
04399 }
04400
04401 int init_manager(void)
04402 {
04403 return __init_manager(0);
04404 }
04405
04406 int reload_manager(void)
04407 {
04408 return __init_manager(1);
04409 }
04410
04411 int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
04412 {
04413 AST_LIST_INSERT_HEAD(&s->session->datastores, datastore, entry);
04414
04415 return 0;
04416 }
04417
04418 int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
04419 {
04420 return AST_LIST_REMOVE(&s->session->datastores, datastore, entry) ? 0 : -1;
04421 }
04422
04423 struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
04424 {
04425 struct ast_datastore *datastore = NULL;
04426
04427 if (info == NULL)
04428 return NULL;
04429
04430 AST_LIST_TRAVERSE_SAFE_BEGIN(&s->session->datastores, datastore, entry) {
04431 if (datastore->info != info) {
04432 continue;
04433 }
04434
04435 if (uid == NULL) {
04436
04437 break;
04438 }
04439
04440 if ((datastore->uid != NULL) && !strcasecmp(uid, datastore->uid)) {
04441
04442 break;
04443 }
04444 }
04445 AST_LIST_TRAVERSE_SAFE_END;
04446
04447 return datastore;
04448 }