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