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: 243988 $")
00047
00048 #include "asterisk/_private.h"
00049 #include "asterisk/paths.h"
00050 #include <ctype.h>
00051 #include <sys/time.h>
00052 #include <signal.h>
00053 #include <sys/mman.h>
00054
00055 #include "asterisk/channel.h"
00056 #include "asterisk/file.h"
00057 #include "asterisk/manager.h"
00058 #include "asterisk/module.h"
00059 #include "asterisk/config.h"
00060 #include "asterisk/callerid.h"
00061 #include "asterisk/lock.h"
00062 #include "asterisk/cli.h"
00063 #include "asterisk/app.h"
00064 #include "asterisk/pbx.h"
00065 #include "asterisk/md5.h"
00066 #include "asterisk/acl.h"
00067 #include "asterisk/utils.h"
00068 #include "asterisk/tcptls.h"
00069 #include "asterisk/http.h"
00070 #include "asterisk/ast_version.h"
00071 #include "asterisk/threadstorage.h"
00072 #include "asterisk/linkedlists.h"
00073 #include "asterisk/version.h"
00074 #include "asterisk/term.h"
00075 #include "asterisk/astobj2.h"
00076 #include "asterisk/features.h"
00077
00078 enum error_type {
00079 UNKNOWN_ACTION = 1,
00080 UNKNOWN_CATEGORY,
00081 UNSPECIFIED_CATEGORY,
00082 UNSPECIFIED_ARGUMENT,
00083 FAILURE_ALLOCATION,
00084 FAILURE_NEWCAT,
00085 FAILURE_DELCAT,
00086 FAILURE_EMPTYCAT,
00087 FAILURE_UPDATE,
00088 FAILURE_DELETE,
00089 FAILURE_APPEND
00090 };
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112 struct eventqent {
00113 int usecount;
00114 int category;
00115 unsigned int seq;
00116 AST_LIST_ENTRY(eventqent) eq_next;
00117 char eventdata[1];
00118 };
00119
00120 static AST_LIST_HEAD_STATIC(all_events, eventqent);
00121
00122 static int displayconnects = 1;
00123 static int allowmultiplelogin = 1;
00124 static int timestampevents;
00125 static int httptimeout = 60;
00126 static int manager_enabled = 0;
00127 static int webmanager_enabled = 0;
00128
00129 static int block_sockets;
00130 static int num_sessions;
00131
00132 static int manager_debug;
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143 #define MAX_BLACKLIST_CMD_LEN 2
00144 static struct {
00145 char *words[AST_MAX_CMD_LEN];
00146 } command_blacklist[] = {
00147 {{ "module", "load", NULL }},
00148 {{ "module", "unload", NULL }},
00149 {{ "restart", "gracefully", NULL }},
00150 };
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184 struct mansession_session {
00185 pthread_t ms_t;
00186 ast_mutex_t __lock;
00187
00188 struct sockaddr_in sin;
00189 FILE *f;
00190 int fd;
00191 int inuse;
00192 int needdestroy;
00193 pthread_t waiting_thread;
00194 uint32_t managerid;
00195 time_t sessionstart;
00196 time_t sessiontimeout;
00197 char username[80];
00198 char challenge[10];
00199 int authenticated;
00200 int readperm;
00201 int writeperm;
00202 char inbuf[1025];
00203
00204 int inlen;
00205 int send_events;
00206 struct eventqent *last_ev;
00207 int writetimeout;
00208 int pending_event;
00209 AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores;
00210 AST_LIST_ENTRY(mansession_session) list;
00211 };
00212
00213
00214
00215
00216
00217
00218 struct mansession {
00219 struct mansession_session *session;
00220 FILE *f;
00221 int fd;
00222 };
00223
00224 #define NEW_EVENT(m) (AST_LIST_NEXT(m->session->last_ev, eq_next))
00225
00226 static AST_LIST_HEAD_STATIC(sessions, mansession_session);
00227
00228
00229
00230
00231
00232
00233
00234 struct ast_manager_user {
00235 char username[80];
00236 char *secret;
00237 struct ast_ha *ha;
00238 int readperm;
00239 int writeperm;
00240 int writetimeout;
00241 int displayconnects;
00242 int keep;
00243 AST_RWLIST_ENTRY(ast_manager_user) list;
00244 };
00245
00246
00247 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
00248
00249
00250 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
00251
00252
00253 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
00254
00255
00256 void ast_manager_register_hook(struct manager_custom_hook *hook)
00257 {
00258 AST_RWLIST_WRLOCK(&manager_hooks);
00259 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
00260 AST_RWLIST_UNLOCK(&manager_hooks);
00261 return;
00262 }
00263
00264
00265 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
00266 {
00267 AST_RWLIST_WRLOCK(&manager_hooks);
00268 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
00269 AST_RWLIST_UNLOCK(&manager_hooks);
00270 return;
00271 }
00272
00273
00274
00275
00276
00277
00278
00279 #if 0
00280 static time_t __deb(time_t start, const char *msg)
00281 {
00282 time_t now = time(NULL);
00283 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
00284 if (start != 0 && now - start > 5)
00285 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
00286 return now;
00287 }
00288
00289 static void LOCK_EVENTS(void)
00290 {
00291 time_t start = __deb(0, "about to lock events");
00292 AST_LIST_LOCK(&all_events);
00293 __deb(start, "done lock events");
00294 }
00295
00296 static void UNLOCK_EVENTS(void)
00297 {
00298 __deb(0, "about to unlock events");
00299 AST_LIST_UNLOCK(&all_events);
00300 }
00301
00302 static void LOCK_SESS(void)
00303 {
00304 time_t start = __deb(0, "about to lock sessions");
00305 AST_LIST_LOCK(&sessions);
00306 __deb(start, "done lock sessions");
00307 }
00308
00309 static void UNLOCK_SESS(void)
00310 {
00311 __deb(0, "about to unlock sessions");
00312 AST_LIST_UNLOCK(&sessions);
00313 }
00314 #endif
00315
00316 int check_manager_enabled()
00317 {
00318 return manager_enabled;
00319 }
00320
00321 int check_webmanager_enabled()
00322 {
00323 return (webmanager_enabled && manager_enabled);
00324 }
00325
00326
00327
00328
00329
00330 static struct eventqent *grab_last(void)
00331 {
00332 struct eventqent *ret;
00333
00334 AST_LIST_LOCK(&all_events);
00335 ret = AST_LIST_LAST(&all_events);
00336
00337
00338
00339 if (ret)
00340 ast_atomic_fetchadd_int(&ret->usecount, 1);
00341 AST_LIST_UNLOCK(&all_events);
00342 return ret;
00343 }
00344
00345
00346
00347
00348
00349 static void purge_events(void)
00350 {
00351 struct eventqent *ev;
00352
00353 AST_LIST_LOCK(&all_events);
00354 while ( (ev = AST_LIST_FIRST(&all_events)) &&
00355 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
00356 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
00357 ast_free(ev);
00358 }
00359 AST_LIST_UNLOCK(&all_events);
00360 }
00361
00362
00363
00364
00365
00366 static struct permalias {
00367 int num;
00368 char *label;
00369 } perms[] = {
00370 { EVENT_FLAG_SYSTEM, "system" },
00371 { EVENT_FLAG_CALL, "call" },
00372 { EVENT_FLAG_LOG, "log" },
00373 { EVENT_FLAG_VERBOSE, "verbose" },
00374 { EVENT_FLAG_COMMAND, "command" },
00375 { EVENT_FLAG_AGENT, "agent" },
00376 { EVENT_FLAG_USER, "user" },
00377 { EVENT_FLAG_CONFIG, "config" },
00378 { EVENT_FLAG_DTMF, "dtmf" },
00379 { EVENT_FLAG_REPORTING, "reporting" },
00380 { EVENT_FLAG_CDR, "cdr" },
00381 { EVENT_FLAG_DIALPLAN, "dialplan" },
00382 { EVENT_FLAG_ORIGINATE, "originate" },
00383 { EVENT_FLAG_AGI, "agi" },
00384 { INT_MAX, "all" },
00385 { 0, "none" },
00386 };
00387
00388
00389 static char *authority_to_str(int authority, struct ast_str **res)
00390 {
00391 int i;
00392 char *sep = "";
00393
00394 (*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 int res = 0;
01792
01793 if (ast_strlen_zero(varname)) {
01794 astman_send_error(s, m, "No variable specified");
01795 return 0;
01796 }
01797
01798 if (!ast_strlen_zero(name)) {
01799 c = ast_get_channel_by_name_locked(name);
01800 if (!c) {
01801 astman_send_error(s, m, "No such channel");
01802 return 0;
01803 }
01804 }
01805 if (varname[strlen(varname)-1] == ')') {
01806 char *function = ast_strdupa(varname);
01807 res = ast_func_write(c, function, varval);
01808 } else {
01809 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
01810 }
01811
01812 if (c)
01813 ast_channel_unlock(c);
01814 if (res == 0) {
01815 astman_send_ack(s, m, "Variable Set");
01816 } else {
01817 astman_send_error(s, m, "Variable not set");
01818 }
01819 return 0;
01820 }
01821
01822 static char mandescr_getvar[] =
01823 "Description: Get the value of a global or local channel variable.\n"
01824 "Variables: (Names marked with * are required)\n"
01825 " Channel: Channel to read variable from\n"
01826 " *Variable: Variable name\n"
01827 " ActionID: Optional Action id for message matching.\n";
01828
01829 static int action_getvar(struct mansession *s, const struct message *m)
01830 {
01831 struct ast_channel *c = NULL;
01832 const char *name = astman_get_header(m, "Channel");
01833 const char *varname = astman_get_header(m, "Variable");
01834 char *varval;
01835 char workspace[1024] = "";
01836
01837 if (ast_strlen_zero(varname)) {
01838 astman_send_error(s, m, "No variable specified");
01839 return 0;
01840 }
01841
01842 if (!ast_strlen_zero(name)) {
01843 c = ast_get_channel_by_name_locked(name);
01844 if (!c) {
01845 astman_send_error(s, m, "No such channel");
01846 return 0;
01847 }
01848 }
01849
01850 if (varname[strlen(varname) - 1] == ')') {
01851 if (!c) {
01852 c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager");
01853 if (c) {
01854 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
01855 ast_channel_free(c);
01856 c = NULL;
01857 } else
01858 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
01859 } else
01860 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
01861 varval = workspace;
01862 } else {
01863 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
01864 }
01865
01866 if (c)
01867 ast_channel_unlock(c);
01868 astman_start_ack(s, m);
01869 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
01870
01871 return 0;
01872 }
01873
01874 static char mandescr_status[] =
01875 "Description: Lists channel status along with requested channel vars.\n"
01876 "Variables: (Names marked with * are required)\n"
01877 " *Channel: Name of the channel to query for status\n"
01878 " Variables: Comma ',' separated list of variables to include\n"
01879 " ActionID: Optional ID for this transaction\n"
01880 "Will return the status information of each channel along with the\n"
01881 "value for the specified channel variables.\n";
01882
01883
01884
01885
01886 static int action_status(struct mansession *s, const struct message *m)
01887 {
01888 const char *name = astman_get_header(m, "Channel");
01889 const char *cvariables = astman_get_header(m, "Variables");
01890 char *variables = ast_strdupa(S_OR(cvariables, ""));
01891 struct ast_channel *c;
01892 char bridge[256];
01893 struct timeval now = ast_tvnow();
01894 long elapsed_seconds = 0;
01895 int channels = 0;
01896 int all = ast_strlen_zero(name);
01897 const char *id = astman_get_header(m, "ActionID");
01898 char idText[256];
01899 AST_DECLARE_APP_ARGS(vars,
01900 AST_APP_ARG(name)[100];
01901 );
01902 struct ast_str *str = ast_str_create(1000);
01903
01904 if (!ast_strlen_zero(id))
01905 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01906 else
01907 idText[0] = '\0';
01908
01909 if (all)
01910 c = ast_channel_walk_locked(NULL);
01911 else {
01912 c = ast_get_channel_by_name_locked(name);
01913 if (!c) {
01914 astman_send_error(s, m, "No such channel");
01915 ast_free(str);
01916 return 0;
01917 }
01918 }
01919 astman_send_ack(s, m, "Channel status will follow");
01920
01921 if (!ast_strlen_zero(cvariables)) {
01922 AST_STANDARD_APP_ARGS(vars, variables);
01923 }
01924
01925
01926 while (c) {
01927 if (!ast_strlen_zero(cvariables)) {
01928 int i;
01929 ast_str_reset(str);
01930 for (i = 0; i < vars.argc; i++) {
01931 char valbuf[512], *ret = NULL;
01932
01933 if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
01934 if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
01935 valbuf[0] = '\0';
01936 }
01937 ret = valbuf;
01938 } else {
01939 pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
01940 }
01941
01942 ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
01943 }
01944 }
01945
01946 channels++;
01947 if (c->_bridge)
01948 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
01949 else
01950 bridge[0] = '\0';
01951 if (c->pbx) {
01952 if (c->cdr) {
01953 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01954 }
01955 astman_append(s,
01956 "Event: Status\r\n"
01957 "Privilege: Call\r\n"
01958 "Channel: %s\r\n"
01959 "CallerIDNum: %s\r\n"
01960 "CallerIDName: %s\r\n"
01961 "Accountcode: %s\r\n"
01962 "ChannelState: %d\r\n"
01963 "ChannelStateDesc: %s\r\n"
01964 "Context: %s\r\n"
01965 "Extension: %s\r\n"
01966 "Priority: %d\r\n"
01967 "Seconds: %ld\r\n"
01968 "%s"
01969 "Uniqueid: %s\r\n"
01970 "%s"
01971 "%s"
01972 "\r\n",
01973 c->name,
01974 S_OR(c->cid.cid_num, ""),
01975 S_OR(c->cid.cid_name, ""),
01976 c->accountcode,
01977 c->_state,
01978 ast_state2str(c->_state), c->context,
01979 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, str->str, idText);
01980 } else {
01981 astman_append(s,
01982 "Event: Status\r\n"
01983 "Privilege: Call\r\n"
01984 "Channel: %s\r\n"
01985 "CallerIDNum: %s\r\n"
01986 "CallerIDName: %s\r\n"
01987 "Account: %s\r\n"
01988 "State: %s\r\n"
01989 "%s"
01990 "Uniqueid: %s\r\n"
01991 "%s"
01992 "%s"
01993 "\r\n",
01994 c->name,
01995 S_OR(c->cid.cid_num, "<unknown>"),
01996 S_OR(c->cid.cid_name, "<unknown>"),
01997 c->accountcode,
01998 ast_state2str(c->_state), bridge, c->uniqueid, str->str, idText);
01999 }
02000 ast_channel_unlock(c);
02001 if (!all)
02002 break;
02003 c = ast_channel_walk_locked(c);
02004 }
02005 astman_append(s,
02006 "Event: StatusComplete\r\n"
02007 "%s"
02008 "Items: %d\r\n"
02009 "\r\n", idText, channels);
02010 ast_free(str);
02011 return 0;
02012 }
02013
02014 static char mandescr_sendtext[] =
02015 "Description: Sends A Text Message while in a call.\n"
02016 "Variables: (Names marked with * are required)\n"
02017 " *Channel: Channel to send message to\n"
02018 " *Message: Message to send\n"
02019 " ActionID: Optional Action id for message matching.\n";
02020
02021 static int action_sendtext(struct mansession *s, const struct message *m)
02022 {
02023 struct ast_channel *c = NULL;
02024 const char *name = astman_get_header(m, "Channel");
02025 const char *textmsg = astman_get_header(m, "Message");
02026 int res = 0;
02027
02028 if (ast_strlen_zero(name)) {
02029 astman_send_error(s, m, "No channel specified");
02030 return 0;
02031 }
02032
02033 if (ast_strlen_zero(textmsg)) {
02034 astman_send_error(s, m, "No Message specified");
02035 return 0;
02036 }
02037
02038 c = ast_get_channel_by_name_locked(name);
02039 if (!c) {
02040 astman_send_error(s, m, "No such channel");
02041 return 0;
02042 }
02043
02044 res = ast_sendtext(c, textmsg);
02045 ast_channel_unlock(c);
02046
02047 if (res > 0)
02048 astman_send_ack(s, m, "Success");
02049 else
02050 astman_send_error(s, m, "Failure");
02051
02052 return res;
02053 }
02054
02055 static char mandescr_redirect[] =
02056 "Description: Redirect (transfer) a call.\n"
02057 "Variables: (Names marked with * are required)\n"
02058 " *Channel: Channel to redirect\n"
02059 " ExtraChannel: Second call leg to transfer (optional)\n"
02060 " *Exten: Extension to transfer to\n"
02061 " *Context: Context to transfer to\n"
02062 " *Priority: Priority to transfer to\n"
02063 " ActionID: Optional Action id for message matching.\n";
02064
02065
02066 static int action_redirect(struct mansession *s, const struct message *m)
02067 {
02068 const char *name = astman_get_header(m, "Channel");
02069 const char *name2 = astman_get_header(m, "ExtraChannel");
02070 const char *exten = astman_get_header(m, "Exten");
02071 const char *context = astman_get_header(m, "Context");
02072 const char *priority = astman_get_header(m, "Priority");
02073 struct ast_channel *chan, *chan2 = NULL;
02074 int pi = 0;
02075 int res;
02076
02077 if (ast_strlen_zero(name)) {
02078 astman_send_error(s, m, "Channel not specified");
02079 return 0;
02080 }
02081 if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
02082 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
02083 astman_send_error(s, m, "Invalid priority");
02084 return 0;
02085 }
02086 }
02087
02088 chan = ast_get_channel_by_name_locked(name);
02089 if (!chan) {
02090 char buf[256];
02091 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
02092 astman_send_error(s, m, buf);
02093 return 0;
02094 }
02095 if (ast_check_hangup(chan)) {
02096 astman_send_error(s, m, "Redirect failed, channel not up.");
02097 ast_channel_unlock(chan);
02098 return 0;
02099 }
02100 if (!ast_strlen_zero(name2))
02101 chan2 = ast_get_channel_by_name_locked(name2);
02102 if (chan2 && ast_check_hangup(chan2)) {
02103 astman_send_error(s, m, "Redirect failed, extra channel not up.");
02104 ast_channel_unlock(chan);
02105 ast_channel_unlock(chan2);
02106 return 0;
02107 }
02108 if (chan->pbx) {
02109 ast_channel_lock(chan);
02110 ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT);
02111 ast_channel_unlock(chan);
02112 }
02113 res = ast_async_goto(chan, context, exten, pi);
02114 if (!res) {
02115 if (!ast_strlen_zero(name2)) {
02116 if (chan2) {
02117 if (chan2->pbx) {
02118 ast_channel_lock(chan2);
02119 ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT);
02120 ast_channel_unlock(chan2);
02121 }
02122 res = ast_async_goto(chan2, context, exten, pi);
02123 } else {
02124 res = -1;
02125 }
02126 if (!res)
02127 astman_send_ack(s, m, "Dual Redirect successful");
02128 else
02129 astman_send_error(s, m, "Secondary redirect failed");
02130 } else
02131 astman_send_ack(s, m, "Redirect successful");
02132 } else
02133 astman_send_error(s, m, "Redirect failed");
02134 if (chan)
02135 ast_channel_unlock(chan);
02136 if (chan2)
02137 ast_channel_unlock(chan2);
02138 return 0;
02139 }
02140
02141 static char mandescr_atxfer[] =
02142 "Description: Attended transfer.\n"
02143 "Variables: (Names marked with * are required)\n"
02144 " *Channel: Transferer's channel\n"
02145 " *Exten: Extension to transfer to\n"
02146 " *Context: Context to transfer to\n"
02147 " *Priority: Priority to transfer to\n"
02148 " ActionID: Optional Action id for message matching.\n";
02149
02150 static int action_atxfer(struct mansession *s, const struct message *m)
02151 {
02152 const char *name = astman_get_header(m, "Channel");
02153 const char *exten = astman_get_header(m, "Exten");
02154 const char *context = astman_get_header(m, "Context");
02155 struct ast_channel *chan = NULL;
02156 struct ast_call_feature *atxfer_feature = NULL;
02157 char *feature_code = NULL;
02158
02159 if (ast_strlen_zero(name)) {
02160 astman_send_error(s, m, "No channel specified");
02161 return 0;
02162 }
02163 if (ast_strlen_zero(exten)) {
02164 astman_send_error(s, m, "No extension specified");
02165 return 0;
02166 }
02167
02168 if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
02169 astman_send_error(s, m, "No attended transfer feature found");
02170 return 0;
02171 }
02172
02173 if (!(chan = ast_get_channel_by_name_locked(name))) {
02174 astman_send_error(s, m, "Channel specified does not exist");
02175 return 0;
02176 }
02177
02178 if (!ast_strlen_zero(context)) {
02179 pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
02180 }
02181
02182 for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
02183 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
02184 ast_queue_frame(chan, &f);
02185 }
02186
02187 for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
02188 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
02189 ast_queue_frame(chan, &f);
02190 }
02191
02192 astman_send_ack(s, m, "Atxfer successfully queued");
02193 ast_channel_unlock(chan);
02194
02195 return 0;
02196 }
02197
02198 static int check_blacklist(const char *cmd)
02199 {
02200 char *cmd_copy, *cur_cmd;
02201 char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
02202 int i;
02203
02204 cmd_copy = ast_strdupa(cmd);
02205 for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
02206 cur_cmd = ast_strip(cur_cmd);
02207 if (ast_strlen_zero(cur_cmd)) {
02208 i--;
02209 continue;
02210 }
02211
02212 cmd_words[i] = cur_cmd;
02213 }
02214
02215 for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
02216 int j, match = 1;
02217
02218 for (j = 0; command_blacklist[i].words[j]; j++) {
02219 if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
02220 match = 0;
02221 break;
02222 }
02223 }
02224
02225 if (match) {
02226 return 1;
02227 }
02228 }
02229
02230 return 0;
02231 }
02232
02233 static char mandescr_command[] =
02234 "Description: Run a CLI command.\n"
02235 "Variables: (Names marked with * are required)\n"
02236 " *Command: Asterisk CLI command to run\n"
02237 " ActionID: Optional Action id for message matching.\n";
02238
02239
02240 static int action_command(struct mansession *s, const struct message *m)
02241 {
02242 const char *cmd = astman_get_header(m, "Command");
02243 const char *id = astman_get_header(m, "ActionID");
02244 char *buf, *final_buf;
02245 char template[] = "/tmp/ast-ami-XXXXXX";
02246 int fd;
02247 off_t l;
02248
02249 if (ast_strlen_zero(cmd)) {
02250 astman_send_error(s, m, "No command provided");
02251 return 0;
02252 }
02253
02254 if (check_blacklist(cmd)) {
02255 astman_send_error(s, m, "Command blacklisted");
02256 return 0;
02257 }
02258
02259 fd = mkstemp(template);
02260
02261 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
02262 if (!ast_strlen_zero(id))
02263 astman_append(s, "ActionID: %s\r\n", id);
02264
02265 ast_cli_command(fd, cmd);
02266 l = lseek(fd, 0, SEEK_END);
02267
02268
02269 buf = ast_calloc(1, l + 1);
02270 final_buf = ast_calloc(1, l + 1);
02271 if (buf) {
02272 lseek(fd, 0, SEEK_SET);
02273 if (read(fd, buf, l) < 0) {
02274 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
02275 }
02276 buf[l] = '\0';
02277 if (final_buf) {
02278 term_strip(final_buf, buf, l);
02279 final_buf[l] = '\0';
02280 }
02281 astman_append(s, "%s", S_OR(final_buf, buf));
02282 ast_free(buf);
02283 }
02284 close(fd);
02285 unlink(template);
02286 astman_append(s, "--END COMMAND--\r\n\r\n");
02287 if (final_buf)
02288 ast_free(final_buf);
02289 return 0;
02290 }
02291
02292
02293 struct fast_originate_helper {
02294 char tech[AST_MAX_EXTENSION];
02295
02296 char data[512];
02297 int timeout;
02298 int format;
02299 char app[AST_MAX_APP];
02300 char appdata[AST_MAX_EXTENSION];
02301 char cid_name[AST_MAX_EXTENSION];
02302 char cid_num[AST_MAX_EXTENSION];
02303 char context[AST_MAX_CONTEXT];
02304 char exten[AST_MAX_EXTENSION];
02305 char idtext[AST_MAX_EXTENSION];
02306 char account[AST_MAX_ACCOUNT_CODE];
02307 int priority;
02308 struct ast_variable *vars;
02309 };
02310
02311 static void *fast_originate(void *data)
02312 {
02313 struct fast_originate_helper *in = data;
02314 int res;
02315 int reason = 0;
02316 struct ast_channel *chan = NULL;
02317 char requested_channel[AST_CHANNEL_NAME];
02318
02319 if (!ast_strlen_zero(in->app)) {
02320 res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1,
02321 S_OR(in->cid_num, NULL),
02322 S_OR(in->cid_name, NULL),
02323 in->vars, in->account, &chan);
02324 } else {
02325 res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
02326 S_OR(in->cid_num, NULL),
02327 S_OR(in->cid_name, NULL),
02328 in->vars, in->account, &chan);
02329 }
02330
02331 if (!chan)
02332 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
02333
02334 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
02335 "%s%s"
02336 "Response: %s\r\n"
02337 "Channel: %s\r\n"
02338 "Context: %s\r\n"
02339 "Exten: %s\r\n"
02340 "Reason: %d\r\n"
02341 "Uniqueid: %s\r\n"
02342 "CallerIDNum: %s\r\n"
02343 "CallerIDName: %s\r\n",
02344 in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success",
02345 chan ? chan->name : requested_channel, in->context, in->exten, reason,
02346 chan ? chan->uniqueid : "<null>",
02347 S_OR(in->cid_num, "<unknown>"),
02348 S_OR(in->cid_name, "<unknown>")
02349 );
02350
02351
02352 if (chan)
02353 ast_channel_unlock(chan);
02354 ast_free(in);
02355 return NULL;
02356 }
02357
02358 static char mandescr_originate[] =
02359 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
02360 " Application/Data\n"
02361 "Variables: (Names marked with * are required)\n"
02362 " *Channel: Channel name to call\n"
02363 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
02364 " Context: Context to use (requires 'Exten' and 'Priority')\n"
02365 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
02366 " Application: Application to use\n"
02367 " Data: Data to use (requires 'Application')\n"
02368 " Timeout: How long to wait for call to be answered (in ms. Default: 30000)\n"
02369 " CallerID: Caller ID to be set on the outgoing channel\n"
02370 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
02371 " Account: Account code\n"
02372 " Async: Set to 'true' for fast origination\n";
02373
02374 static int action_originate(struct mansession *s, const struct message *m)
02375 {
02376 const char *name = astman_get_header(m, "Channel");
02377 const char *exten = astman_get_header(m, "Exten");
02378 const char *context = astman_get_header(m, "Context");
02379 const char *priority = astman_get_header(m, "Priority");
02380 const char *timeout = astman_get_header(m, "Timeout");
02381 const char *callerid = astman_get_header(m, "CallerID");
02382 const char *account = astman_get_header(m, "Account");
02383 const char *app = astman_get_header(m, "Application");
02384 const char *appdata = astman_get_header(m, "Data");
02385 const char *async = astman_get_header(m, "Async");
02386 const char *id = astman_get_header(m, "ActionID");
02387 const char *codecs = astman_get_header(m, "Codecs");
02388 struct ast_variable *vars = astman_get_variables(m);
02389 char *tech, *data;
02390 char *l = NULL, *n = NULL;
02391 int pi = 0;
02392 int res;
02393 int to = 30000;
02394 int reason = 0;
02395 char tmp[256];
02396 char tmp2[256];
02397 int format = AST_FORMAT_SLINEAR;
02398
02399 pthread_t th;
02400 if (ast_strlen_zero(name)) {
02401 astman_send_error(s, m, "Channel not specified");
02402 return 0;
02403 }
02404 if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
02405 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
02406 astman_send_error(s, m, "Invalid priority");
02407 return 0;
02408 }
02409 }
02410 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) {
02411 astman_send_error(s, m, "Invalid timeout");
02412 return 0;
02413 }
02414 ast_copy_string(tmp, name, sizeof(tmp));
02415 tech = tmp;
02416 data = strchr(tmp, '/');
02417 if (!data) {
02418 astman_send_error(s, m, "Invalid channel");
02419 return 0;
02420 }
02421 *data++ = '\0';
02422 ast_copy_string(tmp2, callerid, sizeof(tmp2));
02423 ast_callerid_parse(tmp2, &n, &l);
02424 if (n) {
02425 if (ast_strlen_zero(n))
02426 n = NULL;
02427 }
02428 if (l) {
02429 ast_shrink_phone_number(l);
02430 if (ast_strlen_zero(l))
02431 l = NULL;
02432 }
02433 if (!ast_strlen_zero(codecs)) {
02434 format = 0;
02435 ast_parse_allow_disallow(NULL, &format, codecs, 1);
02436 }
02437 if (ast_true(async)) {
02438 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
02439 if (!fast) {
02440 res = -1;
02441 } else {
02442 if (!ast_strlen_zero(id))
02443 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s", id);
02444 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
02445 ast_copy_string(fast->data, data, sizeof(fast->data));
02446 ast_copy_string(fast->app, app, sizeof(fast->app));
02447 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
02448 if (l)
02449 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
02450 if (n)
02451 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
02452 fast->vars = vars;
02453 ast_copy_string(fast->context, context, sizeof(fast->context));
02454 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
02455 ast_copy_string(fast->account, account, sizeof(fast->account));
02456 fast->format = format;
02457 fast->timeout = to;
02458 fast->priority = pi;
02459 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
02460 ast_free(fast);
02461 res = -1;
02462 } else {
02463 res = 0;
02464 }
02465 }
02466 } else if (!ast_strlen_zero(app)) {
02467
02468 if (!(s->session->writeperm & EVENT_FLAG_SYSTEM)
02469 && (
02470 strcasestr(app, "system") == 0 ||
02471
02472 strcasestr(app, "exec") ||
02473
02474 strcasestr(app, "agi") ||
02475
02476 strstr(appdata, "SHELL") ||
02477 strstr(appdata, "EVAL")
02478 )) {
02479 astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
02480 return 0;
02481 }
02482 res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
02483 } else {
02484 if (exten && context && pi)
02485 res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
02486 else {
02487 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
02488 return 0;
02489 }
02490 }
02491 if (!res)
02492 astman_send_ack(s, m, "Originate successfully queued");
02493 else
02494 astman_send_error(s, m, "Originate failed");
02495 return 0;
02496 }
02497
02498
02499
02500 static char mandescr_mailboxstatus[] =
02501 "Description: Checks a voicemail account for status.\n"
02502 "Variables: (Names marked with * are required)\n"
02503 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02504 " ActionID: Optional ActionID for message matching.\n"
02505 "Returns number of messages.\n"
02506 " Message: Mailbox Status\n"
02507 " Mailbox: <mailboxid>\n"
02508 " Waiting: <count>\n"
02509 "\n";
02510
02511 static int action_mailboxstatus(struct mansession *s, const struct message *m)
02512 {
02513 const char *mailbox = astman_get_header(m, "Mailbox");
02514 int ret;
02515
02516 if (ast_strlen_zero(mailbox)) {
02517 astman_send_error(s, m, "Mailbox not specified");
02518 return 0;
02519 }
02520 ret = ast_app_has_voicemail(mailbox, NULL);
02521 astman_start_ack(s, m);
02522 astman_append(s, "Message: Mailbox Status\r\n"
02523 "Mailbox: %s\r\n"
02524 "Waiting: %d\r\n\r\n", mailbox, ret);
02525 return 0;
02526 }
02527
02528 static char mandescr_mailboxcount[] =
02529 "Description: Checks a voicemail account for new messages.\n"
02530 "Variables: (Names marked with * are required)\n"
02531 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02532 " ActionID: Optional ActionID for message matching.\n"
02533 "Returns number of urgent, new and old messages.\n"
02534 " Message: Mailbox Message Count\n"
02535 " Mailbox: <mailboxid>\n"
02536 " UrgentMessages: <count>\n"
02537 " NewMessages: <count>\n"
02538 " OldMessages: <count>\n"
02539 "\n";
02540 static int action_mailboxcount(struct mansession *s, const struct message *m)
02541 {
02542 const char *mailbox = astman_get_header(m, "Mailbox");
02543 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
02544
02545 if (ast_strlen_zero(mailbox)) {
02546 astman_send_error(s, m, "Mailbox not specified");
02547 return 0;
02548 }
02549 ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
02550 astman_start_ack(s, m);
02551 astman_append(s, "Message: Mailbox Message Count\r\n"
02552 "Mailbox: %s\r\n"
02553 "UrgMessages: %d\r\n"
02554 "NewMessages: %d\r\n"
02555 "OldMessages: %d\r\n"
02556 "\r\n",
02557 mailbox, urgentmsgs, newmsgs, oldmsgs);
02558 return 0;
02559 }
02560
02561 static char mandescr_extensionstate[] =
02562 "Description: Report the extension state for given extension.\n"
02563 " If the extension has a hint, will use devicestate to check\n"
02564 " the status of the device connected to the extension.\n"
02565 "Variables: (Names marked with * are required)\n"
02566 " *Exten: Extension to check state on\n"
02567 " *Context: Context for extension\n"
02568 " ActionId: Optional ID for this transaction\n"
02569 "Will return an \"Extension Status\" message.\n"
02570 "The response will include the hint for the extension and the status.\n";
02571
02572 static int action_extensionstate(struct mansession *s, const struct message *m)
02573 {
02574 const char *exten = astman_get_header(m, "Exten");
02575 const char *context = astman_get_header(m, "Context");
02576 char hint[256] = "";
02577 int status;
02578 if (ast_strlen_zero(exten)) {
02579 astman_send_error(s, m, "Extension not specified");
02580 return 0;
02581 }
02582 if (ast_strlen_zero(context))
02583 context = "default";
02584 status = ast_extension_state(NULL, context, exten);
02585 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
02586 astman_start_ack(s, m);
02587 astman_append(s, "Message: Extension Status\r\n"
02588 "Exten: %s\r\n"
02589 "Context: %s\r\n"
02590 "Hint: %s\r\n"
02591 "Status: %d\r\n\r\n",
02592 exten, context, hint, status);
02593 return 0;
02594 }
02595
02596 static char mandescr_timeout[] =
02597 "Description: Hangup a channel after a certain time.\n"
02598 "Variables: (Names marked with * are required)\n"
02599 " *Channel: Channel name to hangup\n"
02600 " *Timeout: Maximum duration of the call (sec)\n"
02601 "Acknowledges set time with 'Timeout Set' message\n";
02602
02603 static int action_timeout(struct mansession *s, const struct message *m)
02604 {
02605 struct ast_channel *c;
02606 const char *name = astman_get_header(m, "Channel");
02607 double timeout = atof(astman_get_header(m, "Timeout"));
02608 struct timeval when = { timeout, 0 };
02609
02610 if (ast_strlen_zero(name)) {
02611 astman_send_error(s, m, "No channel specified");
02612 return 0;
02613 }
02614 if (!timeout || timeout < 0) {
02615 astman_send_error(s, m, "No timeout specified");
02616 return 0;
02617 }
02618 c = ast_get_channel_by_name_locked(name);
02619 if (!c) {
02620 astman_send_error(s, m, "No such channel");
02621 return 0;
02622 }
02623
02624 when.tv_usec = (timeout - when.tv_sec) * 1000000.0;
02625 ast_channel_setwhentohangup_tv(c, when);
02626 ast_channel_unlock(c);
02627 astman_send_ack(s, m, "Timeout Set");
02628 return 0;
02629 }
02630
02631
02632
02633
02634
02635
02636 static int process_events(struct mansession *s)
02637 {
02638 int ret = 0;
02639
02640 ast_mutex_lock(&s->session->__lock);
02641 if (s->session->f != NULL) {
02642 struct eventqent *eqe;
02643
02644 while ( (eqe = NEW_EVENT(s)) ) {
02645 ref_event(eqe);
02646 if (!ret && s->session->authenticated &&
02647 (s->session->readperm & eqe->category) == eqe->category &&
02648 (s->session->send_events & eqe->category) == eqe->category) {
02649 if (send_string(s, eqe->eventdata) < 0)
02650 ret = -1;
02651 }
02652 s->session->last_ev = unref_event(s->session->last_ev);
02653 }
02654 }
02655 ast_mutex_unlock(&s->session->__lock);
02656 return ret;
02657 }
02658
02659 static char mandescr_userevent[] =
02660 "Description: Send an event to manager sessions.\n"
02661 "Variables: (Names marked with * are required)\n"
02662 " *UserEvent: EventStringToSend\n"
02663 " Header1: Content1\n"
02664 " HeaderN: ContentN\n";
02665
02666 static int action_userevent(struct mansession *s, const struct message *m)
02667 {
02668 const char *event = astman_get_header(m, "UserEvent");
02669 struct ast_str *body = ast_str_thread_get(&userevent_buf, 16);
02670 int x;
02671
02672 ast_str_reset(body);
02673
02674 for (x = 0; x < m->hdrcount; x++) {
02675 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
02676 ast_str_append(&body, 0, "%s\r\n", m->headers[x]);
02677 }
02678 }
02679
02680 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body->str);
02681 astman_send_ack(s, m, "Event Sent");
02682 return 0;
02683 }
02684
02685 static char mandescr_coresettings[] =
02686 "Description: Query for Core PBX settings.\n"
02687 "Variables: (Names marked with * are optional)\n"
02688 " *ActionID: ActionID of this transaction\n";
02689
02690
02691 static int action_coresettings(struct mansession *s, const struct message *m)
02692 {
02693 const char *actionid = astman_get_header(m, "ActionID");
02694 char idText[150];
02695
02696 if (!ast_strlen_zero(actionid))
02697 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02698 else
02699 idText[0] = '\0';
02700
02701 astman_append(s, "Response: Success\r\n"
02702 "%s"
02703 "AMIversion: %s\r\n"
02704 "AsteriskVersion: %s\r\n"
02705 "SystemName: %s\r\n"
02706 "CoreMaxCalls: %d\r\n"
02707 "CoreMaxLoadAvg: %f\r\n"
02708 "CoreRunUser: %s\r\n"
02709 "CoreRunGroup: %s\r\n"
02710 "CoreMaxFilehandles: %d\r\n"
02711 "CoreRealTimeEnabled: %s\r\n"
02712 "CoreCDRenabled: %s\r\n"
02713 "CoreHTTPenabled: %s\r\n"
02714 "\r\n",
02715 idText,
02716 AMI_VERSION,
02717 ast_get_version(),
02718 ast_config_AST_SYSTEM_NAME,
02719 option_maxcalls,
02720 option_maxload,
02721 ast_config_AST_RUN_USER,
02722 ast_config_AST_RUN_GROUP,
02723 option_maxfiles,
02724 ast_realtime_enabled() ? "Yes" : "No",
02725 check_cdr_enabled() ? "Yes" : "No",
02726 check_webmanager_enabled() ? "Yes" : "No"
02727 );
02728 return 0;
02729 }
02730
02731 static char mandescr_corestatus[] =
02732 "Description: Query for Core PBX status.\n"
02733 "Variables: (Names marked with * are optional)\n"
02734 " *ActionID: ActionID of this transaction\n";
02735
02736
02737 static int action_corestatus(struct mansession *s, const struct message *m)
02738 {
02739 const char *actionid = astman_get_header(m, "ActionID");
02740 char idText[150];
02741 char startuptime[150];
02742 char reloadtime[150];
02743 struct ast_tm tm;
02744
02745 if (!ast_strlen_zero(actionid))
02746 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02747 else
02748 idText[0] = '\0';
02749
02750 ast_localtime(&ast_startuptime, &tm, NULL);
02751 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
02752 ast_localtime(&ast_lastreloadtime, &tm, NULL);
02753 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
02754
02755 astman_append(s, "Response: Success\r\n"
02756 "%s"
02757 "CoreStartupTime: %s\r\n"
02758 "CoreReloadTime: %s\r\n"
02759 "CoreCurrentCalls: %d\r\n"
02760 "\r\n",
02761 idText,
02762 startuptime,
02763 reloadtime,
02764 ast_active_channels()
02765 );
02766 return 0;
02767 }
02768
02769 static char mandescr_reload[] =
02770 "Description: Send a reload event.\n"
02771 "Variables: (Names marked with * are optional)\n"
02772 " *ActionID: ActionID of this transaction\n"
02773 " *Module: Name of the module to reload\n";
02774
02775
02776 static int action_reload(struct mansession *s, const struct message *m)
02777 {
02778 const char *module = astman_get_header(m, "Module");
02779 int res = ast_module_reload(S_OR(module, NULL));
02780
02781 if (res == 2)
02782 astman_send_ack(s, m, "Module Reloaded");
02783 else
02784 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
02785 return 0;
02786 }
02787
02788 static char mandescr_coreshowchannels[] =
02789 "Description: List currently defined channels and some information\n"
02790 " about them.\n"
02791 "Variables:\n"
02792 " ActionID: Optional Action id for message matching.\n";
02793
02794
02795
02796 static int action_coreshowchannels(struct mansession *s, const struct message *m)
02797 {
02798 const char *actionid = astman_get_header(m, "ActionID");
02799 char actionidtext[256];
02800 struct ast_channel *c = NULL;
02801 int numchans = 0;
02802 int duration, durh, durm, durs;
02803
02804 if (!ast_strlen_zero(actionid))
02805 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
02806 else
02807 actionidtext[0] = '\0';
02808
02809 astman_send_listack(s, m, "Channels will follow", "start");
02810
02811 while ((c = ast_channel_walk_locked(c)) != NULL) {
02812 struct ast_channel *bc = ast_bridged_channel(c);
02813 char durbuf[10] = "";
02814
02815 if (c->cdr && !ast_tvzero(c->cdr->start)) {
02816 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
02817 durh = duration / 3600;
02818 durm = (duration % 3600) / 60;
02819 durs = duration % 60;
02820 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
02821 }
02822
02823 astman_append(s,
02824 "Event: CoreShowChannel\r\n"
02825 "Channel: %s\r\n"
02826 "UniqueID: %s\r\n"
02827 "Context: %s\r\n"
02828 "Extension: %s\r\n"
02829 "Priority: %d\r\n"
02830 "ChannelState: %d\r\n"
02831 "ChannelStateDesc: %s\r\n"
02832 "Application: %s\r\n"
02833 "ApplicationData: %s\r\n"
02834 "CallerIDnum: %s\r\n"
02835 "Duration: %s\r\n"
02836 "AccountCode: %s\r\n"
02837 "BridgedChannel: %s\r\n"
02838 "BridgedUniqueID: %s\r\n"
02839 "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
02840 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
02841 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
02842 ast_channel_unlock(c);
02843 numchans++;
02844 }
02845
02846 astman_append(s,
02847 "Event: CoreShowChannelsComplete\r\n"
02848 "EventList: Complete\r\n"
02849 "ListItems: %d\r\n"
02850 "%s"
02851 "\r\n", numchans, actionidtext);
02852
02853 return 0;
02854 }
02855
02856 static char mandescr_modulecheck[] =
02857 "Description: Checks if Asterisk module is loaded\n"
02858 "Variables: \n"
02859 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
02860 " Module: <name> Asterisk module name (not including extension)\n"
02861 "\n"
02862 "Will return Success/Failure\n"
02863 "For success returns, the module revision number is included.\n";
02864
02865
02866 static int manager_modulecheck(struct mansession *s, const struct message *m)
02867 {
02868 int res;
02869 const char *module = astman_get_header(m, "Module");
02870 const char *id = astman_get_header(m, "ActionID");
02871 char idText[256];
02872 #if !defined(LOW_MEMORY)
02873 const char *version;
02874 #endif
02875 char filename[PATH_MAX];
02876 char *cut;
02877
02878 ast_copy_string(filename, module, sizeof(filename));
02879 if ((cut = strchr(filename, '.'))) {
02880 *cut = '\0';
02881 } else {
02882 cut = filename + strlen(filename);
02883 }
02884 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
02885 ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
02886 res = ast_module_check(filename);
02887 if (!res) {
02888 astman_send_error(s, m, "Module not loaded");
02889 return 0;
02890 }
02891 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
02892 ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
02893 #if !defined(LOW_MEMORY)
02894 version = ast_file_version_find(filename);
02895 #endif
02896
02897 if (!ast_strlen_zero(id))
02898 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02899 else
02900 idText[0] = '\0';
02901 astman_append(s, "Response: Success\r\n%s", idText);
02902 #if !defined(LOW_MEMORY)
02903 astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
02904 #endif
02905 return 0;
02906 }
02907
02908 static char mandescr_moduleload[] =
02909 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
02910 "Variables: \n"
02911 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
02912 " Module: <name> Asterisk module name (including .so extension)\n"
02913 " or subsystem identifier:\n"
02914 " cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
02915 " LoadType: load | unload | reload\n"
02916 " The operation to be done on module\n"
02917 " If no module is specified for a reload loadtype, all modules are reloaded";
02918
02919 static int manager_moduleload(struct mansession *s, const struct message *m)
02920 {
02921 int res;
02922 const char *module = astman_get_header(m, "Module");
02923 const char *loadtype = astman_get_header(m, "LoadType");
02924
02925 if (!loadtype || strlen(loadtype) == 0)
02926 astman_send_error(s, m, "Incomplete ModuleLoad action.");
02927 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
02928 astman_send_error(s, m, "Need module name");
02929
02930 if (!strcasecmp(loadtype, "load")) {
02931 res = ast_load_resource(module);
02932 if (res)
02933 astman_send_error(s, m, "Could not load module.");
02934 else
02935 astman_send_ack(s, m, "Module loaded.");
02936 } else if (!strcasecmp(loadtype, "unload")) {
02937 res = ast_unload_resource(module, AST_FORCE_SOFT);
02938 if (res)
02939 astman_send_error(s, m, "Could not unload module.");
02940 else
02941 astman_send_ack(s, m, "Module unloaded.");
02942 } else if (!strcasecmp(loadtype, "reload")) {
02943 if (module != NULL) {
02944 res = ast_module_reload(module);
02945 if (res == 0)
02946 astman_send_error(s, m, "No such module.");
02947 else if (res == 1)
02948 astman_send_error(s, m, "Module does not support reload action.");
02949 else
02950 astman_send_ack(s, m, "Module reloaded.");
02951 } else {
02952 ast_module_reload(NULL);
02953 astman_send_ack(s, m, "All modules reloaded");
02954 }
02955 } else
02956 astman_send_error(s, m, "Incomplete ModuleLoad action.");
02957 return 0;
02958 }
02959
02960
02961
02962
02963
02964
02965
02966
02967
02968
02969
02970
02971
02972
02973 static int process_message(struct mansession *s, const struct message *m)
02974 {
02975 char action[80] = "";
02976 int ret = 0;
02977 struct manager_action *tmp;
02978 const char *user = astman_get_header(m, "Username");
02979
02980 ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
02981 ast_debug(1, "Manager received command '%s'\n", action);
02982
02983 if (ast_strlen_zero(action)) {
02984 ast_mutex_lock(&s->session->__lock);
02985 astman_send_error(s, m, "Missing action in request");
02986 ast_mutex_unlock(&s->session->__lock);
02987 return 0;
02988 }
02989
02990 if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
02991 ast_mutex_lock(&s->session->__lock);
02992 astman_send_error(s, m, "Permission denied");
02993 ast_mutex_unlock(&s->session->__lock);
02994 return 0;
02995 }
02996
02997 if (!allowmultiplelogin && !s->session->authenticated && user &&
02998 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
02999 if (check_manager_session_inuse(user)) {
03000 sleep(1);
03001 ast_mutex_lock(&s->session->__lock);
03002 astman_send_error(s, m, "Login Already In Use");
03003 ast_mutex_unlock(&s->session->__lock);
03004 return -1;
03005 }
03006 }
03007
03008 AST_RWLIST_RDLOCK(&actions);
03009 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
03010 if (strcasecmp(action, tmp->action))
03011 continue;
03012 if (s->session->writeperm & tmp->authority || tmp->authority == 0)
03013 ret = tmp->func(s, m);
03014 else
03015 astman_send_error(s, m, "Permission denied");
03016 break;
03017 }
03018 AST_RWLIST_UNLOCK(&actions);
03019
03020 if (!tmp) {
03021 char buf[512];
03022 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
03023 ast_mutex_lock(&s->session->__lock);
03024 astman_send_error(s, m, buf);
03025 ast_mutex_unlock(&s->session->__lock);
03026 }
03027 if (ret)
03028 return ret;
03029
03030 return process_events(s);
03031 }
03032
03033
03034
03035
03036
03037
03038
03039
03040
03041
03042 static int get_input(struct mansession *s, char *output)
03043 {
03044 int res, x;
03045 int maxlen = sizeof(s->session->inbuf) - 1;
03046 char *src = s->session->inbuf;
03047
03048
03049
03050
03051
03052 for (x = 0; x < s->session->inlen; x++) {
03053 int cr;
03054 if (src[x] == '\r' && x+1 < s->session->inlen && src[x+1] == '\n')
03055 cr = 2;
03056 else if (src[x] == '\n')
03057 cr = 1;
03058 else
03059 continue;
03060 memmove(output, src, x);
03061 output[x] = '\0';
03062 x += cr;
03063 s->session->inlen -= x;
03064 memmove(src, src + x, s->session->inlen);
03065 return 1;
03066 }
03067 if (s->session->inlen >= maxlen) {
03068
03069 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->session->sin.sin_addr), src);
03070 s->session->inlen = 0;
03071 }
03072 res = 0;
03073 while (res == 0) {
03074
03075 ast_mutex_lock(&s->session->__lock);
03076 if (s->session->pending_event) {
03077 s->session->pending_event = 0;
03078 ast_mutex_unlock(&s->session->__lock);
03079 return 0;
03080 }
03081 s->session->waiting_thread = pthread_self();
03082 ast_mutex_unlock(&s->session->__lock);
03083
03084 res = ast_wait_for_input(s->session->fd, -1);
03085
03086 ast_mutex_lock(&s->session->__lock);
03087 s->session->waiting_thread = AST_PTHREADT_NULL;
03088 ast_mutex_unlock(&s->session->__lock);
03089 }
03090 if (res < 0) {
03091
03092
03093
03094 if (errno == EINTR || errno == EAGAIN)
03095 return 0;
03096 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
03097 return -1;
03098 }
03099 ast_mutex_lock(&s->session->__lock);
03100 res = fread(src + s->session->inlen, 1, maxlen - s->session->inlen, s->session->f);
03101 if (res < 1)
03102 res = -1;
03103 else {
03104 s->session->inlen += res;
03105 src[s->session->inlen] = '\0';
03106 res = 0;
03107 }
03108 ast_mutex_unlock(&s->session->__lock);
03109 return res;
03110 }
03111
03112 static int do_message(struct mansession *s)
03113 {
03114 struct message m = { 0 };
03115 char header_buf[sizeof(s->session->inbuf)] = { '\0' };
03116 int res;
03117
03118 for (;;) {
03119
03120 if (process_events(s))
03121 return -1;
03122 res = get_input(s, header_buf);
03123 if (res == 0) {
03124 continue;
03125 } else if (res > 0) {
03126 if (ast_strlen_zero(header_buf))
03127 return process_message(s, &m) ? -1 : 0;
03128 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
03129 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
03130 } else {
03131 return res;
03132 }
03133 }
03134 }
03135
03136
03137
03138
03139
03140
03141
03142
03143
03144 static void *session_do(void *data)
03145 {
03146 struct ast_tcptls_session_instance *ser = data;
03147 struct mansession_session *session = ast_calloc(1, sizeof(*session));
03148 struct mansession s = {.session = NULL, };
03149 int flags;
03150 int res;
03151
03152 if (session == NULL)
03153 goto done;
03154
03155 session->writetimeout = 100;
03156 session->waiting_thread = AST_PTHREADT_NULL;
03157
03158 flags = fcntl(ser->fd, F_GETFL);
03159 if (!block_sockets)
03160 flags |= O_NONBLOCK;
03161 else
03162 flags &= ~O_NONBLOCK;
03163 fcntl(ser->fd, F_SETFL, flags);
03164
03165 ast_mutex_init(&session->__lock);
03166 session->send_events = -1;
03167
03168 session->last_ev = grab_last();
03169
03170
03171 session->fd = ser->fd;
03172 session->f = ser->f;
03173 session->sin = ser->remote_address;
03174 s.session = session;
03175
03176 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
03177
03178 AST_LIST_LOCK(&sessions);
03179 AST_LIST_INSERT_HEAD(&sessions, session, list);
03180 ast_atomic_fetchadd_int(&num_sessions, 1);
03181 AST_LIST_UNLOCK(&sessions);
03182
03183 astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);
03184 for (;;) {
03185 if ((res = do_message(&s)) < 0)
03186 break;
03187 }
03188
03189 if (session->authenticated) {
03190 if (manager_displayconnects(session))
03191 ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03192 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03193 } else {
03194 if (displayconnects)
03195 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
03196 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
03197 }
03198
03199
03200
03201
03202
03203
03204
03205
03206
03207
03208
03209
03210 usleep(1);
03211
03212 destroy_session(session);
03213
03214 done:
03215 ao2_ref(ser, -1);
03216 ser = NULL;
03217 return NULL;
03218 }
03219
03220
03221 static void purge_sessions(int n_max)
03222 {
03223 struct mansession_session *session;
03224 time_t now = time(NULL);
03225
03226 AST_LIST_LOCK(&sessions);
03227 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, session, list) {
03228 if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
03229 AST_LIST_REMOVE_CURRENT(list);
03230 ast_atomic_fetchadd_int(&num_sessions, -1);
03231 if (session->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(session)) {
03232 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
03233 session->username, ast_inet_ntoa(session->sin.sin_addr));
03234 }
03235 free_session(session);
03236 if (--n_max <= 0)
03237 break;
03238 }
03239 }
03240 AST_LIST_TRAVERSE_SAFE_END;
03241 AST_LIST_UNLOCK(&sessions);
03242 }
03243
03244
03245
03246
03247
03248 static int append_event(const char *str, int category)
03249 {
03250 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
03251 static int seq;
03252
03253 if (!tmp)
03254 return -1;
03255
03256
03257 tmp->usecount = 0;
03258 tmp->category = category;
03259 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
03260 AST_LIST_NEXT(tmp, eq_next) = NULL;
03261 strcpy(tmp->eventdata, str);
03262
03263 AST_LIST_LOCK(&all_events);
03264 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
03265 AST_LIST_UNLOCK(&all_events);
03266
03267 return 0;
03268 }
03269
03270
03271 AST_THREADSTORAGE(manager_event_buf);
03272 #define MANAGER_EVENT_BUF_INITSIZE 256
03273
03274
03275 int __manager_event(int category, const char *event,
03276 const char *file, int line, const char *func, const char *fmt, ...)
03277 {
03278 struct mansession_session *session;
03279 struct manager_custom_hook *hook;
03280 struct ast_str *auth = ast_str_alloca(80);
03281 const char *cat_str;
03282 va_list ap;
03283 struct timeval now;
03284 struct ast_str *buf;
03285
03286
03287 if (!num_sessions && AST_RWLIST_EMPTY(&manager_hooks))
03288 return 0;
03289
03290 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
03291 return -1;
03292
03293 cat_str = authority_to_str(category, &auth);
03294 ast_str_set(&buf, 0,
03295 "Event: %s\r\nPrivilege: %s\r\n",
03296 event, cat_str);
03297
03298 if (timestampevents) {
03299 now = ast_tvnow();
03300 ast_str_append(&buf, 0,
03301 "Timestamp: %ld.%06lu\r\n",
03302 (long)now.tv_sec, (unsigned long) now.tv_usec);
03303 }
03304 if (manager_debug) {
03305 static int seq;
03306 ast_str_append(&buf, 0,
03307 "SequenceNumber: %d\r\n",
03308 ast_atomic_fetchadd_int(&seq, 1));
03309 ast_str_append(&buf, 0,
03310 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
03311 }
03312
03313 va_start(ap, fmt);
03314 ast_str_append_va(&buf, 0, fmt, ap);
03315 va_end(ap);
03316
03317 ast_str_append(&buf, 0, "\r\n");
03318
03319 append_event(buf->str, category);
03320
03321
03322 if (num_sessions) {
03323 AST_LIST_LOCK(&sessions);
03324 AST_LIST_TRAVERSE(&sessions, session, list) {
03325 ast_mutex_lock(&session->__lock);
03326 if (session->waiting_thread != AST_PTHREADT_NULL)
03327 pthread_kill(session->waiting_thread, SIGURG);
03328 else
03329
03330
03331
03332
03333
03334 session->pending_event = 1;
03335 ast_mutex_unlock(&session->__lock);
03336 }
03337 AST_LIST_UNLOCK(&sessions);
03338 }
03339
03340 if (!AST_RWLIST_EMPTY(&manager_hooks)) {
03341 AST_RWLIST_RDLOCK(&manager_hooks);
03342 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
03343 hook->helper(category, event, buf->str);
03344 }
03345 AST_RWLIST_UNLOCK(&manager_hooks);
03346 }
03347
03348 return 0;
03349 }
03350
03351
03352
03353
03354 int ast_manager_unregister(char *action)
03355 {
03356 struct manager_action *cur;
03357 struct timespec tv = { 5, };
03358
03359 if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
03360 ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
03361 return -1;
03362 }
03363 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
03364 if (!strcasecmp(action, cur->action)) {
03365 AST_RWLIST_REMOVE_CURRENT(list);
03366 ast_free(cur);
03367 ast_verb(2, "Manager unregistered action %s\n", action);
03368 break;
03369 }
03370 }
03371 AST_RWLIST_TRAVERSE_SAFE_END;
03372 AST_RWLIST_UNLOCK(&actions);
03373
03374 return 0;
03375 }
03376
03377 static int manager_state_cb(char *context, char *exten, int state, void *data)
03378 {
03379
03380 char hint[512];
03381 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
03382
03383 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
03384 return 0;
03385 }
03386
03387 static int ast_manager_register_struct(struct manager_action *act)
03388 {
03389 struct manager_action *cur, *prev = NULL;
03390 struct timespec tv = { 5, };
03391
03392 if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
03393 ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
03394 return -1;
03395 }
03396 AST_RWLIST_TRAVERSE(&actions, cur, list) {
03397 int ret = strcasecmp(cur->action, act->action);
03398 if (ret == 0) {
03399 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
03400 AST_RWLIST_UNLOCK(&actions);
03401 return -1;
03402 }
03403 if (ret > 0) {
03404 prev = cur;
03405 break;
03406 }
03407 }
03408
03409 if (prev)
03410 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
03411 else
03412 AST_RWLIST_INSERT_HEAD(&actions, act, list);
03413
03414 ast_verb(2, "Manager registered action %s\n", act->action);
03415
03416 AST_RWLIST_UNLOCK(&actions);
03417
03418 return 0;
03419 }
03420
03421
03422
03423 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
03424 {
03425 struct manager_action *cur = NULL;
03426
03427 if (!(cur = ast_calloc(1, sizeof(*cur))))
03428 return -1;
03429
03430 cur->action = action;
03431 cur->authority = auth;
03432 cur->func = func;
03433 cur->synopsis = synopsis;
03434 cur->description = description;
03435
03436 if (ast_manager_register_struct(cur)) {
03437 ast_free(cur);
03438 return -1;
03439 }
03440
03441 return 0;
03442 }
03443
03444
03445
03446
03447
03448
03449
03450
03451
03452
03453
03454
03455
03456
03457
03458 enum output_format {
03459 FORMAT_RAW,
03460 FORMAT_HTML,
03461 FORMAT_XML,
03462 };
03463
03464 static char *contenttype[] = {
03465 [FORMAT_RAW] = "plain",
03466 [FORMAT_HTML] = "html",
03467 [FORMAT_XML] = "xml",
03468 };
03469
03470
03471
03472
03473
03474
03475 static struct mansession_session *find_session(uint32_t ident, int incinuse)
03476 {
03477 struct mansession_session *session;
03478
03479 if (ident == 0)
03480 return NULL;
03481
03482 AST_LIST_LOCK(&sessions);
03483 AST_LIST_TRAVERSE(&sessions, session, list) {
03484 ast_mutex_lock(&session->__lock);
03485 if (session->managerid == ident && !session->needdestroy) {
03486 ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
03487 break;
03488 }
03489 ast_mutex_unlock(&session->__lock);
03490 }
03491 AST_LIST_UNLOCK(&sessions);
03492
03493 return session;
03494 }
03495
03496 int astman_is_authed(uint32_t ident)
03497 {
03498 int authed;
03499 struct mansession_session *session;
03500
03501 if (!(session = find_session(ident, 0)))
03502 return 0;
03503
03504 authed = (session->authenticated != 0);
03505
03506 ast_mutex_unlock(&session->__lock);
03507
03508 return authed;
03509 }
03510
03511 int astman_verify_session_readpermissions(uint32_t ident, int perm)
03512 {
03513 int result = 0;
03514 struct mansession_session *session;
03515
03516 AST_LIST_LOCK(&sessions);
03517 AST_LIST_TRAVERSE(&sessions, session, list) {
03518 ast_mutex_lock(&session->__lock);
03519 if ((session->managerid == ident) && (session->readperm & perm)) {
03520 result = 1;
03521 ast_mutex_unlock(&session->__lock);
03522 break;
03523 }
03524 ast_mutex_unlock(&session->__lock);
03525 }
03526 AST_LIST_UNLOCK(&sessions);
03527 return result;
03528 }
03529
03530 int astman_verify_session_writepermissions(uint32_t ident, int perm)
03531 {
03532 int result = 0;
03533 struct mansession_session *session;
03534
03535 AST_LIST_LOCK(&sessions);
03536 AST_LIST_TRAVERSE(&sessions, session, list) {
03537 ast_mutex_lock(&session->__lock);
03538 if ((session->managerid == ident) && (session->writeperm & perm)) {
03539 result = 1;
03540 ast_mutex_unlock(&session->__lock);
03541 break;
03542 }
03543 ast_mutex_unlock(&session->__lock);
03544 }
03545 AST_LIST_UNLOCK(&sessions);
03546 return result;
03547 }
03548
03549
03550
03551
03552
03553
03554 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
03555 {
03556
03557 char buf[256];
03558 char *dst = buf;
03559 int space = sizeof(buf);
03560
03561 for ( ; *src || dst != buf ; src++) {
03562 if (*src == '\0' || space < 10) {
03563 *dst++ = '\0';
03564 ast_str_append(out, 0, "%s", buf);
03565 dst = buf;
03566 space = sizeof(buf);
03567 if (*src == '\0')
03568 break;
03569 }
03570
03571 if ( (mode & 2) && !isalnum(*src)) {
03572 *dst++ = '_';
03573 space--;
03574 continue;
03575 }
03576 switch (*src) {
03577 case '<':
03578 strcpy(dst, "<");
03579 dst += 4;
03580 space -= 4;
03581 break;
03582 case '>':
03583 strcpy(dst, ">");
03584 dst += 4;
03585 space -= 4;
03586 break;
03587 case '\"':
03588 strcpy(dst, """);
03589 dst += 6;
03590 space -= 6;
03591 break;
03592 case '\'':
03593 strcpy(dst, "'");
03594 dst += 6;
03595 space -= 6;
03596 break;
03597 case '&':
03598 strcpy(dst, "&");
03599 dst += 5;
03600 space -= 5;
03601 break;
03602
03603 default:
03604 *dst++ = mode ? tolower(*src) : *src;
03605 space--;
03606 }
03607 }
03608 }
03609
03610 struct variable_count {
03611 char *varname;
03612 int count;
03613 };
03614
03615 static int compress_char(char c)
03616 {
03617 c &= 0x7f;
03618 if (c < 32)
03619 return 0;
03620 else if (c >= 'a' && c <= 'z')
03621 return c - 64;
03622 else if (c > 'z')
03623 return '_';
03624 else
03625 return c - 32;
03626 }
03627
03628 static int variable_count_hash_fn(const void *vvc, const int flags)
03629 {
03630 const struct variable_count *vc = vvc;
03631 int res = 0, i;
03632 for (i = 0; i < 5; i++) {
03633 if (vc->varname[i] == '\0')
03634 break;
03635 res += compress_char(vc->varname[i]) << (i * 6);
03636 }
03637 return res;
03638 }
03639
03640 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
03641 {
03642
03643
03644
03645
03646 struct variable_count *vc = obj;
03647 char *str = vstr;
03648 return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
03649 }
03650
03651
03652
03653
03654
03655
03656
03657
03658
03659
03660
03661
03662
03663
03664
03665
03666
03667
03668
03669
03670
03671
03672
03673
03674
03675
03676
03677
03678
03679 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
03680 {
03681 struct ast_variable *v;
03682 const char *dest = NULL;
03683 char *var, *val;
03684 const char *objtype = NULL;
03685 int in_data = 0;
03686 int inobj = 0;
03687 int xml = (format == FORMAT_XML);
03688 struct variable_count *vc = NULL;
03689 struct ao2_container *vco = NULL;
03690
03691 for (v = vars; v; v = v->next) {
03692 if (!dest && !strcasecmp(v->name, "ajaxdest"))
03693 dest = v->value;
03694 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
03695 objtype = v->value;
03696 }
03697 if (!dest)
03698 dest = "unknown";
03699 if (!objtype)
03700 objtype = "generic";
03701
03702
03703 while (in && *in) {
03704 val = strsep(&in, "\r\n");
03705 if (in && *in == '\n')
03706 in++;
03707 ast_trim_blanks(val);
03708 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
03709 if (ast_strlen_zero(val)) {
03710 if (in_data) {
03711 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
03712 in_data = 0;
03713 }
03714 if (inobj) {
03715 ast_str_append(out, 0, xml ? " /></response>\n" :
03716 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
03717 inobj = 0;
03718 ao2_ref(vco, -1);
03719 vco = NULL;
03720 }
03721 continue;
03722 }
03723
03724
03725 if (in_data) {
03726 var = NULL;
03727 } else {
03728 var = strsep(&val, ":");
03729 if (val) {
03730 val = ast_skip_blanks(val);
03731 ast_trim_blanks(var);
03732 } else {
03733 val = var;
03734 var = "Opaque-data";
03735 }
03736 }
03737
03738 if (!inobj) {
03739 if (xml)
03740 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
03741 else
03742 ast_str_append(out, 0, "<body>\n");
03743 vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
03744 inobj = 1;
03745 }
03746
03747 if (!in_data) {
03748 ast_str_append(out, 0, xml ? " " : "<tr><td>");
03749 if ((vc = ao2_find(vco, var, 0)))
03750 vc->count++;
03751 else {
03752
03753 vc = ao2_alloc(sizeof(*vc), NULL);
03754 vc->varname = var;
03755 vc->count = 1;
03756 ao2_link(vco, vc);
03757 }
03758 xml_copy_escape(out, var, xml ? 1 | 2 : 0);
03759 if (vc->count > 1)
03760 ast_str_append(out, 0, "-%d", vc->count);
03761 ao2_ref(vc, -1);
03762 ast_str_append(out, 0, xml ? "='" : "</td><td>");
03763 if (!strcmp(var, "Opaque-data"))
03764 in_data = 1;
03765 }
03766 xml_copy_escape(out, val, 0);
03767 if (!in_data)
03768 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
03769 else
03770 ast_str_append(out, 0, xml ? "\n" : "<br>\n");
03771 }
03772 if (inobj) {
03773 ast_str_append(out, 0, xml ? " /></response>\n" :
03774 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
03775 ao2_ref(vco, -1);
03776 }
03777 }
03778
03779 static struct ast_str *generic_http_callback(enum output_format format,
03780 struct sockaddr_in *remote_address, const char *uri, enum ast_http_method method,
03781 struct ast_variable *params, int *status,
03782 char **title, int *contentlength)
03783 {
03784 struct mansession s = {.session = NULL, };
03785 struct mansession_session *session = NULL;
03786 uint32_t ident = 0;
03787 int blastaway = 0;
03788 struct ast_variable *v;
03789 char template[] = "/tmp/ast-http-XXXXXX";
03790 struct ast_str *out = NULL;
03791 struct message m = { 0 };
03792 unsigned int x;
03793 size_t hdrlen;
03794
03795 for (v = params; v; v = v->next) {
03796 if (!strcasecmp(v->name, "mansession_id")) {
03797 sscanf(v->value, "%30x", &ident);
03798 break;
03799 }
03800 }
03801
03802 if (!(session = find_session(ident, 1))) {
03803
03804
03805
03806 if (!(session = ast_calloc(1, sizeof(*session)))) {
03807 *status = 500;
03808 goto generic_callback_out;
03809 }
03810 session->sin = *remote_address;
03811 session->fd = -1;
03812 session->waiting_thread = AST_PTHREADT_NULL;
03813 session->send_events = 0;
03814 ast_mutex_init(&session->__lock);
03815 ast_mutex_lock(&session->__lock);
03816 session->inuse = 1;
03817
03818
03819
03820
03821
03822 while ((session->managerid = ast_random() ^ (unsigned long) session) == 0);
03823 session->last_ev = grab_last();
03824 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
03825 AST_LIST_LOCK(&sessions);
03826 AST_LIST_INSERT_HEAD(&sessions, session, list);
03827 ast_atomic_fetchadd_int(&num_sessions, 1);
03828 AST_LIST_UNLOCK(&sessions);
03829 }
03830
03831 s.session = session;
03832
03833 ast_mutex_unlock(&session->__lock);
03834
03835 if (!(out = ast_str_create(1024))) {
03836 *status = 500;
03837 goto generic_callback_out;
03838 }
03839
03840 s.fd = mkstemp(template);
03841 unlink(template);
03842 s.f = fdopen(s.fd, "w+");
03843
03844 for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
03845 hdrlen = strlen(v->name) + strlen(v->value) + 3;
03846 m.headers[m.hdrcount] = alloca(hdrlen);
03847 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
03848 ast_verb(4, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
03849 m.hdrcount = x + 1;
03850 }
03851
03852 if (process_message(&s, &m)) {
03853 if (session->authenticated) {
03854 if (manager_displayconnects(session)) {
03855 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03856 }
03857 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03858 } else {
03859 if (displayconnects) {
03860 ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
03861 }
03862 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
03863 }
03864 session->needdestroy = 1;
03865 }
03866
03867 ast_str_append(&out, 0,
03868 "Content-type: text/%s\r\n"
03869 "Cache-Control: no-cache;\r\n"
03870 "Set-Cookie: mansession_id=\"%08x\"; Version=\"1\"; Max-Age=%d\r\n"
03871 "\r\n",
03872 contenttype[format],
03873 session->managerid, httptimeout);
03874
03875 if (format == FORMAT_XML) {
03876 ast_str_append(&out, 0, "<ajax-response>\n");
03877 } else if (format == FORMAT_HTML) {
03878
03879
03880
03881
03882
03883
03884 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
03885 #define TEST_STRING \
03886 "<form action=\"manager\">\n\
03887 Action: <select name=\"action\">\n\
03888 <option value=\"\">-----></option>\n\
03889 <option value=\"login\">login</option>\n\
03890 <option value=\"command\">Command</option>\n\
03891 <option value=\"waitevent\">waitevent</option>\n\
03892 <option value=\"listcommands\">listcommands</option>\n\
03893 </select>\n\
03894 or <input name=\"action\"><br/>\n\
03895 CLI Command <input name=\"command\"><br>\n\
03896 user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\
03897 <input type=\"submit\">\n</form>\n"
03898
03899 ast_str_append(&out, 0, "<title>Asterisk™ Manager Interface</title>");
03900 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
03901 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
03902 ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
03903 }
03904
03905 if (s.f != NULL) {
03906 char *buf;
03907 size_t l;
03908
03909
03910 fprintf(s.f, "%c", 0);
03911
03912 if ((l = ftell(s.f))) {
03913 if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, s.fd, 0))) {
03914 ast_log(LOG_WARNING, "mmap failed. Manager output was not processed\n");
03915 } else {
03916 if (format == FORMAT_XML || format == FORMAT_HTML)
03917 xml_translate(&out, buf, params, format);
03918 else
03919 ast_str_append(&out, 0, "%s", buf);
03920 munmap(buf, l);
03921 }
03922 } else if (format == FORMAT_XML || format == FORMAT_HTML) {
03923 xml_translate(&out, "", params, format);
03924 }
03925 fclose(s.f);
03926 s.f = NULL;
03927 s.fd = -1;
03928 }
03929
03930 if (format == FORMAT_XML) {
03931 ast_str_append(&out, 0, "</ajax-response>\n");
03932 } else if (format == FORMAT_HTML)
03933 ast_str_append(&out, 0, "</table></body>\r\n");
03934
03935 ast_mutex_lock(&session->__lock);
03936
03937 session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5);
03938
03939 if (session->needdestroy) {
03940 if (session->inuse == 1) {
03941 ast_debug(1, "Need destroy, doing it now!\n");
03942 blastaway = 1;
03943 } else {
03944 ast_debug(1, "Need destroy, but can't do it yet!\n");
03945 if (session->waiting_thread != AST_PTHREADT_NULL)
03946 pthread_kill(session->waiting_thread, SIGURG);
03947 session->inuse--;
03948 }
03949 } else
03950 session->inuse--;
03951 ast_mutex_unlock(&session->__lock);
03952
03953 if (blastaway)
03954 destroy_session(session);
03955 generic_callback_out:
03956 if (*status != 200)
03957 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
03958 return out;
03959 }
03960
03961 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)
03962 {
03963 return generic_http_callback(FORMAT_HTML, &ser->remote_address, uri, method, params, status, title, contentlength);
03964 }
03965
03966 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)
03967 {
03968 return generic_http_callback(FORMAT_XML, &ser->remote_address, uri, method, params, status, title, contentlength);
03969 }
03970
03971 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)
03972 {
03973 return generic_http_callback(FORMAT_RAW, &ser->remote_address, uri, method, params, status, title, contentlength);
03974 }
03975
03976 struct ast_http_uri rawmanuri = {
03977 .description = "Raw HTTP Manager Event Interface",
03978 .uri = "rawman",
03979 .callback = rawman_http_callback,
03980 .supports_get = 1,
03981 .data = NULL,
03982 .key = __FILE__,
03983 };
03984
03985 struct ast_http_uri manageruri = {
03986 .description = "HTML Manager Event Interface",
03987 .uri = "manager",
03988 .callback = manager_http_callback,
03989 .supports_get = 1,
03990 .data = NULL,
03991 .key = __FILE__,
03992 };
03993
03994 struct ast_http_uri managerxmluri = {
03995 .description = "XML Manager Event Interface",
03996 .uri = "mxml",
03997 .callback = mxml_http_callback,
03998 .supports_get = 1,
03999 .data = NULL,
04000 .key = __FILE__,
04001 };
04002
04003 static int registered = 0;
04004 static int webregged = 0;
04005
04006
04007
04008
04009 static void purge_old_stuff(void *data)
04010 {
04011 purge_sessions(1);
04012 purge_events();
04013 }
04014
04015 struct ast_tls_config ami_tls_cfg;
04016 static struct ast_tcptls_session_args ami_desc = {
04017 .accept_fd = -1,
04018 .master = AST_PTHREADT_NULL,
04019 .tls_cfg = NULL,
04020 .poll_timeout = 5000,
04021 .periodic_fn = purge_old_stuff,
04022 .name = "AMI server",
04023 .accept_fn = ast_tcptls_server_root,
04024 .worker_fn = session_do,
04025 };
04026
04027 static struct ast_tcptls_session_args amis_desc = {
04028 .accept_fd = -1,
04029 .master = AST_PTHREADT_NULL,
04030 .tls_cfg = &ami_tls_cfg,
04031 .poll_timeout = -1,
04032 .name = "AMI TLS server",
04033 .accept_fn = ast_tcptls_server_root,
04034 .worker_fn = session_do,
04035 };
04036
04037 static int __init_manager(int reload)
04038 {
04039 struct ast_config *ucfg = NULL, *cfg = NULL;
04040 const char *val;
04041 char *cat = NULL;
04042 int newhttptimeout = 60;
04043 int have_sslbindaddr = 0;
04044 struct hostent *hp;
04045 struct ast_hostent ahp;
04046 struct ast_manager_user *user = NULL;
04047 struct ast_variable *var;
04048 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
04049
04050 manager_enabled = 0;
04051
04052 if (!registered) {
04053
04054 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
04055 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
04056 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
04057 ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
04058 ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
04059 ast_manager_register2("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
04060 ast_manager_register2("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status, "Lists channel status", mandescr_status);
04061 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar);
04062 ast_manager_register2("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar, "Gets a Channel Variable", mandescr_getvar);
04063 ast_manager_register2("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
04064 ast_manager_register2("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson, "Retrieve configuration (JSON format)", mandescr_getconfigjson);
04065 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
04066 ast_manager_register2("CreateConfig", EVENT_FLAG_CONFIG, action_createconfig, "Creates an empty file in the configuration directory", mandescr_createconfig);
04067 ast_manager_register2("ListCategories", EVENT_FLAG_CONFIG, action_listcategories, "List categories in configuration file", mandescr_listcategories);
04068 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
04069 ast_manager_register2("Atxfer", EVENT_FLAG_CALL, action_atxfer, "Attended transfer", mandescr_atxfer);
04070 ast_manager_register2("Originate", EVENT_FLAG_ORIGINATE, action_originate, "Originate Call", mandescr_originate);
04071 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
04072 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
04073 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
04074 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
04075 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
04076 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
04077 ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
04078 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
04079 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
04080 ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
04081 ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
04082 ast_manager_register2("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload, "Send a reload event", mandescr_reload);
04083 ast_manager_register2("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels, "List currently active channels", mandescr_coreshowchannels);
04084 ast_manager_register2("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload, "Module management", mandescr_moduleload);
04085 ast_manager_register2("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck, "Check if module is loaded", mandescr_modulecheck);
04086
04087 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
04088 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
04089 registered = 1;
04090
04091 append_event("Event: Placeholder\r\n\r\n", 0);
04092 }
04093 if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
04094 return 0;
04095
04096 displayconnects = 1;
04097 if (!cfg) {
04098 ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf. Asterisk management interface (AMI) disabled.\n");
04099 return 0;
04100 }
04101
04102
04103 memset(&ami_desc.local_address, 0, sizeof(struct sockaddr_in));
04104 memset(&amis_desc.local_address, 0, sizeof(amis_desc.local_address));
04105 amis_desc.local_address.sin_port = htons(5039);
04106 ami_desc.local_address.sin_port = htons(DEFAULT_MANAGER_PORT);
04107
04108 ami_tls_cfg.enabled = 0;
04109 if (ami_tls_cfg.certfile)
04110 ast_free(ami_tls_cfg.certfile);
04111 ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
04112 if (ami_tls_cfg.cipher)
04113 ast_free(ami_tls_cfg.cipher);
04114 ami_tls_cfg.cipher = ast_strdup("");
04115
04116 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
04117 val = var->value;
04118 if (!strcasecmp(var->name, "sslenable"))
04119 ami_tls_cfg.enabled = ast_true(val);
04120 else if (!strcasecmp(var->name, "sslbindport"))
04121 amis_desc.local_address.sin_port = htons(atoi(val));
04122 else if (!strcasecmp(var->name, "sslbindaddr")) {
04123 if ((hp = ast_gethostbyname(val, &ahp))) {
04124 memcpy(&amis_desc.local_address.sin_addr, hp->h_addr, sizeof(amis_desc.local_address.sin_addr));
04125 have_sslbindaddr = 1;
04126 } else {
04127 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val);
04128 }
04129 } else if (!strcasecmp(var->name, "sslcert")) {
04130 ast_free(ami_tls_cfg.certfile);
04131 ami_tls_cfg.certfile = ast_strdup(val);
04132 } else if (!strcasecmp(var->name, "sslcipher")) {
04133 ast_free(ami_tls_cfg.cipher);
04134 ami_tls_cfg.cipher = ast_strdup(val);
04135 } else if (!strcasecmp(var->name, "enabled")) {
04136 manager_enabled = ast_true(val);
04137 } else if (!strcasecmp(var->name, "block-sockets")) {
04138 block_sockets = ast_true(val);
04139 } else if (!strcasecmp(var->name, "webenabled")) {
04140 webmanager_enabled = ast_true(val);
04141 } else if (!strcasecmp(var->name, "port")) {
04142 ami_desc.local_address.sin_port = htons(atoi(val));
04143 } else if (!strcasecmp(var->name, "bindaddr")) {
04144 if (!inet_aton(val, &ami_desc.local_address.sin_addr)) {
04145 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
04146 memset(&ami_desc.local_address.sin_addr, 0, sizeof(ami_desc.local_address.sin_addr));
04147 }
04148 } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
04149 allowmultiplelogin = ast_true(val);
04150 } else if (!strcasecmp(var->name, "displayconnects")) {
04151 displayconnects = ast_true(val);
04152 } else if (!strcasecmp(var->name, "timestampevents")) {
04153 timestampevents = ast_true(val);
04154 } else if (!strcasecmp(var->name, "debug")) {
04155 manager_debug = ast_true(val);
04156 } else if (!strcasecmp(var->name, "httptimeout")) {
04157 newhttptimeout = atoi(val);
04158 } else {
04159 ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
04160 var->name, val);
04161 }
04162 }
04163
04164 if (manager_enabled)
04165 ami_desc.local_address.sin_family = AF_INET;
04166 if (!have_sslbindaddr)
04167 amis_desc.local_address.sin_addr = ami_desc.local_address.sin_addr;
04168 if (ami_tls_cfg.enabled)
04169 amis_desc.local_address.sin_family = AF_INET;
04170
04171
04172 AST_RWLIST_WRLOCK(&users);
04173
04174
04175 ucfg = ast_config_load2("users.conf", "manager", config_flags);
04176 if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED)) {
04177 const char *hasmanager;
04178 int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
04179
04180 while ((cat = ast_category_browse(ucfg, cat))) {
04181 if (!strcasecmp(cat, "general"))
04182 continue;
04183
04184 hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
04185 if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
04186 const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
04187 const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
04188 const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
04189 const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
04190 const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
04191
04192
04193
04194
04195 if (!(user = get_manager_by_name_locked(cat))) {
04196 if (!(user = ast_calloc(1, sizeof(*user))))
04197 break;
04198
04199
04200 ast_copy_string(user->username, cat, sizeof(user->username));
04201
04202 AST_LIST_INSERT_TAIL(&users, user, list);
04203 user->ha = NULL;
04204 user->keep = 1;
04205 user->readperm = -1;
04206 user->writeperm = -1;
04207
04208 user->displayconnects = displayconnects;
04209 user->writetimeout = 100;
04210 }
04211
04212 if (!user_secret)
04213 user_secret = ast_variable_retrieve(ucfg, "general", "secret");
04214 if (!user_read)
04215 user_read = ast_variable_retrieve(ucfg, "general", "read");
04216 if (!user_write)
04217 user_write = ast_variable_retrieve(ucfg, "general", "write");
04218 if (!user_displayconnects)
04219 user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
04220 if (!user_writetimeout)
04221 user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
04222
04223 if (!ast_strlen_zero(user_secret)) {
04224 if (user->secret)
04225 ast_free(user->secret);
04226 user->secret = ast_strdup(user_secret);
04227 }
04228
04229 if (user_read)
04230 user->readperm = get_perm(user_read);
04231 if (user_write)
04232 user->writeperm = get_perm(user_write);
04233 if (user_displayconnects)
04234 user->displayconnects = ast_true(user_displayconnects);
04235
04236 if (user_writetimeout) {
04237 int value = atoi(user_writetimeout);
04238 if (value < 100)
04239 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at users.conf line %d\n", var->value, var->lineno);
04240 else
04241 user->writetimeout = value;
04242 }
04243 }
04244 }
04245 ast_config_destroy(ucfg);
04246 }
04247
04248
04249
04250 while ((cat = ast_category_browse(cfg, cat))) {
04251 struct ast_ha *oldha;
04252
04253 if (!strcasecmp(cat, "general"))
04254 continue;
04255
04256
04257 if (!(user = get_manager_by_name_locked(cat))) {
04258 if (!(user = ast_calloc(1, sizeof(*user))))
04259 break;
04260
04261 ast_copy_string(user->username, cat, sizeof(user->username));
04262
04263 user->ha = NULL;
04264 user->readperm = 0;
04265 user->writeperm = 0;
04266
04267 user->displayconnects = displayconnects;
04268 user->writetimeout = 100;
04269
04270
04271 AST_RWLIST_INSERT_TAIL(&users, user, list);
04272 }
04273
04274
04275 user->keep = 1;
04276 oldha = user->ha;
04277 user->ha = NULL;
04278
04279 var = ast_variable_browse(cfg, cat);
04280 for (; var; var = var->next) {
04281 if (!strcasecmp(var->name, "secret")) {
04282 if (user->secret)
04283 ast_free(user->secret);
04284 user->secret = ast_strdup(var->value);
04285 } else if (!strcasecmp(var->name, "deny") ||
04286 !strcasecmp(var->name, "permit")) {
04287 user->ha = ast_append_ha(var->name, var->value, user->ha, NULL);
04288 } else if (!strcasecmp(var->name, "read") ) {
04289 user->readperm = get_perm(var->value);
04290 } else if (!strcasecmp(var->name, "write") ) {
04291 user->writeperm = get_perm(var->value);
04292 } else if (!strcasecmp(var->name, "displayconnects") ) {
04293 user->displayconnects = ast_true(var->value);
04294 } else if (!strcasecmp(var->name, "writetimeout")) {
04295 int value = atoi(var->value);
04296 if (value < 100)
04297 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
04298 else
04299 user->writetimeout = value;
04300 } else
04301 ast_debug(1, "%s is an unknown option.\n", var->name);
04302 }
04303 ast_free_ha(oldha);
04304 }
04305 ast_config_destroy(cfg);
04306
04307
04308 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
04309 if (user->keep) {
04310 user->keep = 0;
04311 continue;
04312 }
04313
04314 AST_RWLIST_REMOVE_CURRENT(list);
04315
04316 if (user->secret)
04317 ast_free(user->secret);
04318 ast_free_ha(user->ha);
04319 ast_free(user);
04320 }
04321 AST_RWLIST_TRAVERSE_SAFE_END;
04322
04323 AST_RWLIST_UNLOCK(&users);
04324
04325 if (webmanager_enabled && manager_enabled) {
04326 if (!webregged) {
04327 ast_http_uri_link(&rawmanuri);
04328 ast_http_uri_link(&manageruri);
04329 ast_http_uri_link(&managerxmluri);
04330 webregged = 1;
04331 }
04332 } else {
04333 if (webregged) {
04334 ast_http_uri_unlink(&rawmanuri);
04335 ast_http_uri_unlink(&manageruri);
04336 ast_http_uri_unlink(&managerxmluri);
04337 webregged = 0;
04338 }
04339 }
04340
04341 if (newhttptimeout > 0)
04342 httptimeout = newhttptimeout;
04343
04344 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled");
04345
04346 ast_tcptls_server_start(&ami_desc);
04347 if (ast_ssl_setup(amis_desc.tls_cfg))
04348 ast_tcptls_server_start(&amis_desc);
04349 return 0;
04350 }
04351
04352 int init_manager(void)
04353 {
04354 return __init_manager(0);
04355 }
04356
04357 int reload_manager(void)
04358 {
04359 return __init_manager(1);
04360 }
04361
04362 int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
04363 {
04364 AST_LIST_INSERT_HEAD(&s->session->datastores, datastore, entry);
04365
04366 return 0;
04367 }
04368
04369 int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
04370 {
04371 return AST_LIST_REMOVE(&s->session->datastores, datastore, entry) ? 0 : -1;
04372 }
04373
04374 struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
04375 {
04376 struct ast_datastore *datastore = NULL;
04377
04378 if (info == NULL)
04379 return NULL;
04380
04381 AST_LIST_TRAVERSE_SAFE_BEGIN(&s->session->datastores, datastore, entry) {
04382 if (datastore->info != info) {
04383 continue;
04384 }
04385
04386 if (uid == NULL) {
04387
04388 break;
04389 }
04390
04391 if ((datastore->uid != NULL) && !strcasecmp(uid, datastore->uid)) {
04392
04393 break;
04394 }
04395 }
04396 AST_LIST_TRAVERSE_SAFE_END;
04397
04398 return datastore;
04399 }