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