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