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