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