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