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