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