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