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