Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:49 2007

Asterisk developer's documentation :: Codename Pineapple


manager.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief The Asterisk Management Interface - AMI
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * At the moment this file contains a number of functions, namely:
00026  *
00027  * - data structures storing AMI state
00028  * - AMI-related API functions, used by internal asterisk components
00029  * - handlers for AMI-related CLI functions
00030  * - handlers for AMI functions (available through the AMI socket)
00031  * - the code for the main AMI listener thread and individual session threads
00032  * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
00033  *
00034  * \ref amiconf
00035  */
00036 
00037 /*! \addtogroup Group_AMI AMI functions
00038 */
00039 /*! @{
00040  Doxygen group */
00041 
00042 #include "asterisk.h"
00043 
00044 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 53128 $")
00045 
00046 #include <stdio.h>
00047 #include <stdlib.h>
00048 #include <string.h>
00049 #include <ctype.h>
00050 #include <sys/time.h>
00051 #include <sys/types.h>
00052 #include <netdb.h>
00053 #include <sys/socket.h>
00054 #include <netinet/in.h>
00055 #include <netinet/tcp.h>
00056 #include <arpa/inet.h>
00057 #include <signal.h>
00058 #include <errno.h>
00059 #include <unistd.h>
00060 #include <sys/mman.h>
00061 
00062 #include "asterisk/channel.h"
00063 #include "asterisk/file.h"
00064 #include "asterisk/manager.h"
00065 #include "asterisk/config.h"
00066 #include "asterisk/callerid.h"
00067 #include "asterisk/lock.h"
00068 #include "asterisk/logger.h"
00069 #include "asterisk/options.h"
00070 #include "asterisk/cli.h"
00071 #include "asterisk/app.h"
00072 #include "asterisk/pbx.h"
00073 #include "asterisk/md5.h"
00074 #include "asterisk/acl.h"
00075 #include "asterisk/utils.h"
00076 #include "asterisk/http.h"
00077 #include "asterisk/threadstorage.h"
00078 #include "asterisk/linkedlists.h"
00079 
00080 /*!
00081  * Linked list of events.
00082  * Global events are appended to the list by append_event().
00083  * The usecount is the number of stored pointers to the element,
00084  * excluding the list pointers. So an element that is only in
00085  * the list has a usecount of 0, not 1.
00086  *
00087  * Clients have a pointer to the last event processed, and for each
00088  * of these clients we track the usecount of the elements.
00089  * If we have a pointer to an entry in the list, it is safe to navigate
00090  * it forward because elements will not be deleted, but only appended.
00091  * The worst that can happen is seeing the pointer still NULL.
00092  *
00093  * When the usecount of an element drops to 0, and the element is the
00094  * first in the list, we can remove it. Removal is done within the
00095  * main thread, which is woken up for the purpose.
00096  *
00097  * For simplicity of implementation, we make sure the list is never empty.
00098  */
00099 struct eventqent {
00100    int usecount;     /*!< # of clients who still need the event */
00101    int category;
00102    unsigned int seq; /*!< sequence number */
00103    AST_LIST_ENTRY(eventqent) eq_next;
00104    char eventdata[1];   /*!< really variable size, allocated by append_event() */
00105 };
00106 
00107 static AST_LIST_HEAD_STATIC(all_events, eventqent);
00108 
00109 static int displayconnects = 1;
00110 static int timestampevents;
00111 static int httptimeout = 60;
00112 
00113 static int block_sockets;
00114 static int num_sessions;
00115 
00116 static int manager_debug;  /*!< enable some debugging code in the manager */
00117 
00118 /*!
00119  * Descriptor for a manager session, either on the AMI socket or over HTTP.
00120  * AMI session have managerid == 0; the entry is created upon a connect,
00121  * and destroyed with the socket.
00122  * HTTP sessions have managerid != 0, the value is used as a search key
00123  * to lookup sessions (using the mansession_id cookie).
00124  */
00125 struct mansession {
00126    pthread_t ms_t;      /*!< Execution thread, basically useless */
00127    ast_mutex_t __lock;  /*!< Thread lock -- don't use in action callbacks, it's already taken care of  */
00128             /* XXX need to document which fields it is protecting */
00129    struct sockaddr_in sin; /*!< address we are connecting from */
00130    FILE *f;    /*!< fdopen() on the underlying fd */
00131    int fd;        /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
00132    int inuse;     /*!< number of HTTP sessions using this entry */
00133    int needdestroy;  /*!< Whether an HTTP session should be destroyed */
00134    pthread_t waiting_thread;  /*!< Sleeping thread using this descriptor */
00135    unsigned long managerid;   /*!< Unique manager identifier, 0 for AMI sessions */
00136    time_t sessiontimeout;  /*!< Session timeout if HTTP */
00137    char username[80];   /*!< Logged in username */
00138    char challenge[10];  /*!< Authentication challenge */
00139    int authenticated;   /*!< Authentication status */
00140    int readperm;     /*!< Authorization for reading */
00141    int writeperm;    /*!< Authorization for writing */
00142    char inbuf[1025]; /*!< Buffer */
00143             /* we use the extra byte to add a '\0' and simplify parsing */
00144    int inlen;     /*!< number of buffered bytes */
00145    int send_events;  /*!<  XXX what ? */
00146    struct eventqent *last_ev; /*!< last event processed. */
00147    int writetimeout; /*!< Timeout for ast_carefulwrite() */
00148    AST_LIST_ENTRY(mansession) list;
00149 };
00150 
00151 #define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
00152 
00153 static AST_LIST_HEAD_STATIC(sessions, mansession);
00154 
00155 /*! \brief user descriptor, as read from the config file.
00156  * \note It is still missing some fields -- e.g. we can have multiple permit and deny
00157  * lines which are not supported here, and readperm/writeperm/writetimeout
00158  * are not stored.
00159  */
00160 struct ast_manager_user {
00161    char username[80];
00162    char *secret;
00163    char *deny;
00164    char *permit;
00165    char *read;
00166    char *write;
00167    int displayconnects; /*!< XXX unused */
00168    int keep;   /*!< mark entries created on a reload */
00169    AST_LIST_ENTRY(ast_manager_user) list;
00170 };
00171 
00172 /*! \brief list of users found in the config file */
00173 static AST_LIST_HEAD_STATIC(users, ast_manager_user);
00174 
00175 /*! \brief list of actions registered */
00176 static struct manager_action *first_action;
00177 AST_RWLOCK_DEFINE_STATIC(actionlock);
00178 
00179 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
00180 
00181 /*! \brief Add a custom hook to be called when an event is fired */
00182 void ast_manager_register_hook(struct manager_custom_hook *hook)
00183 {
00184    AST_RWLIST_WRLOCK(&manager_hooks);
00185    AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
00186    AST_RWLIST_UNLOCK(&manager_hooks);
00187    return;
00188 }
00189 
00190 /*! \brief Delete a custom hook to be called when an event is fired */
00191 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
00192 {
00193    AST_RWLIST_WRLOCK(&manager_hooks);
00194    AST_RWLIST_REMOVE(&manager_hooks, hook, list);
00195    AST_RWLIST_UNLOCK(&manager_hooks);
00196    return;
00197 }
00198 
00199 /*! \brief
00200  * Event list management functions.
00201  * We assume that the event list always has at least one element,
00202  * and the delete code will not remove the last entry even if the
00203  * 
00204  */
00205 #if 0
00206 static time_t __deb(time_t start, const char *msg)
00207 {
00208    time_t now = time(NULL);
00209    ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
00210    if (start != 0 && now - start > 5)
00211       ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
00212    return now;
00213 }
00214 
00215 static void LOCK_EVENTS(void)
00216 {
00217    time_t start = __deb(0, "about to lock events");
00218    AST_LIST_LOCK(&all_events);
00219    __deb(start, "done lock events");
00220 }
00221 
00222 static void UNLOCK_EVENTS(void)
00223 {
00224    __deb(0, "about to unlock events");
00225    AST_LIST_UNLOCK(&all_events);
00226 }
00227 
00228 static void LOCK_SESS(void)
00229 {
00230    time_t start = __deb(0, "about to lock sessions");
00231    AST_LIST_LOCK(&sessions);
00232    __deb(start, "done lock sessions");
00233 }
00234 
00235 static void UNLOCK_SESS(void)
00236 {
00237    __deb(0, "about to unlock sessions");
00238    AST_LIST_UNLOCK(&sessions);
00239 }
00240 #endif
00241 
00242 /*!
00243  * Grab a reference to the last event, update usecount as needed.
00244  * Can handle a NULL pointer.
00245  */
00246 static struct eventqent *grab_last(void)
00247 {
00248    struct eventqent *ret;
00249 
00250    AST_LIST_LOCK(&all_events);
00251    ret = AST_LIST_LAST(&all_events);
00252    /* the list is never empty now, but may become so when
00253     * we optimize it in the future, so be prepared.
00254     */
00255    if (ret)
00256       ast_atomic_fetchadd_int(&ret->usecount, 1);
00257    AST_LIST_UNLOCK(&all_events);
00258    return ret;
00259 }
00260 
00261 /*!
00262  * Purge unused events. Remove elements from the head
00263  * as long as their usecount is 0 and there is a next element.
00264  */
00265 static void purge_events(void)
00266 {
00267    struct eventqent *ev;
00268 
00269    AST_LIST_LOCK(&all_events);
00270    while ( (ev = AST_LIST_FIRST(&all_events)) &&
00271        ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
00272       AST_LIST_REMOVE_HEAD(&all_events, eq_next);
00273       free(ev);
00274    }
00275    AST_LIST_UNLOCK(&all_events);
00276 }
00277 
00278 /*!
00279  * helper functions to convert back and forth between
00280  * string and numeric representation of set of flags
00281  */
00282 static struct permalias {
00283    int num;
00284    char *label;
00285 } perms[] = {
00286    { EVENT_FLAG_SYSTEM, "system" },
00287    { EVENT_FLAG_CALL, "call" },
00288    { EVENT_FLAG_LOG, "log" },
00289    { EVENT_FLAG_VERBOSE, "verbose" },
00290    { EVENT_FLAG_COMMAND, "command" },
00291    { EVENT_FLAG_AGENT, "agent" },
00292    { EVENT_FLAG_USER, "user" },
00293    { EVENT_FLAG_CONFIG, "config" },
00294    { -1, "all" },
00295    { 0, "none" },
00296 };
00297 
00298 /*! \brief Convert authority code to a list of options */
00299 static char *authority_to_str(int authority, struct ast_str **res)
00300 {
00301    int i;
00302    char *sep = "";
00303 
00304    (*res)->used = 0;
00305    for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
00306       if (authority & perms[i].num) {
00307          ast_str_append(res, 0, "%s%s", sep, perms[i].label);
00308          sep = ",";
00309       }
00310    }
00311 
00312    if ((*res)->used == 0)  /* replace empty string with something sensible */
00313       ast_str_append(res, 0, "<none>");
00314 
00315    return (*res)->str;
00316 }
00317 
00318 /*! Tells you if smallstr exists inside bigstr
00319    which is delim by delim and uses no buf or stringsep
00320    ast_instring("this|that|more","this",'|') == 1;
00321 
00322    feel free to move this to app.c -anthm */
00323 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
00324 {
00325    const char *val = bigstr, *next;
00326 
00327    do {
00328       if ((next = strchr(val, delim))) {
00329          if (!strncmp(val, smallstr, (next - val)))
00330             return 1;
00331          else
00332             continue;
00333       } else
00334          return !strcmp(smallstr, val);
00335    } while (*(val = (next + 1)));
00336 
00337    return 0;
00338 }
00339 
00340 static int get_perm(const char *instr)
00341 {
00342    int x = 0, ret = 0;
00343 
00344    if (!instr)
00345       return 0;
00346 
00347    for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
00348       if (ast_instring(instr, perms[x].label, ','))
00349          ret |= perms[x].num;
00350    }
00351 
00352    return ret;
00353 }
00354 
00355 /*!
00356  * A number returns itself, false returns 0, true returns all flags,
00357  * other strings return the flags that are set.
00358  */
00359 static int strings_to_mask(const char *string)
00360 {
00361    const char *p;
00362 
00363    if (ast_strlen_zero(string))
00364       return -1;
00365 
00366    for (p = string; *p; p++)
00367       if (*p < '0' || *p > '9')
00368          break;
00369    if (!p)  /* all digits */
00370       return atoi(string);
00371    if (ast_false(string))
00372       return 0;
00373    if (ast_true(string)) { /* all permissions */
00374       int x, ret = 0;
00375       for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
00376          ret |= perms[x].num;
00377       return ret;
00378    }
00379    return get_perm(string);
00380 }
00381 
00382 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
00383 {
00384    struct manager_action *cur;
00385    int l = strlen(word), which = 0;
00386    char *ret = NULL;
00387 
00388    ast_rwlock_rdlock(&actionlock);
00389    for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
00390       if (!strncasecmp(word, cur->action, l) && ++which > state) {
00391          ret = ast_strdup(cur->action);
00392          break;   /* make sure we exit even if ast_strdup() returns NULL */
00393       }
00394    }
00395    ast_rwlock_unlock(&actionlock);
00396 
00397    return ret;
00398 }
00399 
00400 /*!
00401  * lookup an entry in the list of registered users.
00402  * must be called with the list lock held.
00403  */
00404 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
00405 {
00406    struct ast_manager_user *user = NULL;
00407 
00408    AST_LIST_TRAVERSE(&users, user, list)
00409       if (!strcasecmp(user->username, name))
00410          break;
00411    return user;
00412 }
00413 
00414 /*! \note The actionlock is read-locked by the caller of this function */
00415 static int handle_showmancmd(int fd, int argc, char *argv[])
00416 {
00417    struct manager_action *cur;
00418    struct ast_str *authority = ast_str_alloca(80);
00419    int num;
00420 
00421    if (argc != 4)
00422       return RESULT_SHOWUSAGE;
00423 
00424    for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
00425       for (num = 3; num < argc; num++) {
00426          if (!strcasecmp(cur->action, argv[num])) {
00427             ast_cli(fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
00428                cur->action, cur->synopsis,
00429                authority_to_str(cur->authority, &authority),
00430                S_OR(cur->description, "") );
00431          }
00432       }
00433    }
00434 
00435    return RESULT_SUCCESS;
00436 }
00437 
00438 static int handle_mandebug(int fd, int argc, char *argv[])
00439 {
00440    if (argc == 2)
00441       ast_cli(fd, "manager debug is %s\n", manager_debug? "on" : "off");
00442    else if (argc == 3) {
00443       if (!strcasecmp(argv[2], "on"))
00444          manager_debug = 1;
00445       else if (!strcasecmp(argv[2], "off"))
00446          manager_debug = 0;
00447       else
00448          return RESULT_SHOWUSAGE;
00449    }
00450    return RESULT_SUCCESS;
00451 }
00452 
00453 static int handle_showmanager(int fd, int argc, char *argv[])
00454 {
00455    struct ast_manager_user *user = NULL;
00456 
00457    if (argc != 4)
00458       return RESULT_SHOWUSAGE;
00459 
00460    AST_LIST_LOCK(&users);
00461 
00462    if (!(user = get_manager_by_name_locked(argv[3]))) {
00463       ast_cli(fd, "There is no manager called %s\n", argv[3]);
00464       AST_LIST_UNLOCK(&users);
00465       return -1;
00466    }
00467 
00468    ast_cli(fd,"\n");
00469    ast_cli(fd,
00470       "       username: %s\n"
00471       "         secret: %s\n"
00472       "           deny: %s\n"
00473       "         permit: %s\n"
00474       "           read: %s\n"
00475       "          write: %s\n"
00476       "displayconnects: %s\n",
00477       (user->username ? user->username : "(N/A)"),
00478       (user->secret ? user->secret : "(N/A)"),
00479       (user->deny ? user->deny : "(N/A)"),
00480       (user->permit ? user->permit : "(N/A)"),
00481       (user->read ? user->read : "(N/A)"),
00482       (user->write ? user->write : "(N/A)"),
00483       (user->displayconnects ? "yes" : "no"));
00484 
00485    AST_LIST_UNLOCK(&users);
00486 
00487    return RESULT_SUCCESS;
00488 }
00489 
00490 
00491 static int handle_showmanagers(int fd, int argc, char *argv[])
00492 {
00493    struct ast_manager_user *user = NULL;
00494    int count_amu = 0;
00495 
00496    if (argc != 3)
00497       return RESULT_SHOWUSAGE;
00498 
00499    AST_LIST_LOCK(&users);
00500 
00501    /* If there are no users, print out something along those lines */
00502    if (AST_LIST_EMPTY(&users)) {
00503       ast_cli(fd, "There are no manager users.\n");
00504       AST_LIST_UNLOCK(&users);
00505       return RESULT_SUCCESS;
00506    }
00507 
00508    ast_cli(fd, "\nusername\n--------\n");
00509 
00510    AST_LIST_TRAVERSE(&users, user, list) {
00511       ast_cli(fd, "%s\n", user->username);
00512       count_amu++;
00513    }
00514 
00515    AST_LIST_UNLOCK(&users);
00516 
00517    ast_cli(fd,"-------------------\n");
00518    ast_cli(fd,"%d manager users configured.\n", count_amu);
00519 
00520    return RESULT_SUCCESS;
00521 }
00522 
00523 
00524 /*! \brief  CLI command  manager list commands */
00525 static int handle_showmancmds(int fd, int argc, char *argv[])
00526 {
00527    struct manager_action *cur;
00528    struct ast_str *authority = ast_str_alloca(80);
00529    char *format = "  %-15.15s  %-15.15s  %-55.55s\n";
00530 
00531    ast_cli(fd, format, "Action", "Privilege", "Synopsis");
00532    ast_cli(fd, format, "------", "---------", "--------");
00533 
00534    ast_rwlock_rdlock(&actionlock);
00535    for (cur = first_action; cur; cur = cur->next) /* Walk the list of actions */
00536       ast_cli(fd, format, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
00537    ast_rwlock_unlock(&actionlock);
00538 
00539    return RESULT_SUCCESS;
00540 }
00541 
00542 /*! \brief CLI command manager list connected */
00543 static int handle_showmanconn(int fd, int argc, char *argv[])
00544 {
00545    struct mansession *s;
00546    char *format = "  %-15.15s  %-15.15s\n";
00547 
00548    ast_cli(fd, format, "Username", "IP Address");
00549 
00550    AST_LIST_LOCK(&sessions);
00551    AST_LIST_TRAVERSE(&sessions, s, list)
00552       ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr));
00553    AST_LIST_UNLOCK(&sessions);
00554 
00555    return RESULT_SUCCESS;
00556 }
00557 
00558 /*! \brief CLI command manager list eventq */
00559 /* Should change to "manager show connected" */
00560 static int handle_showmaneventq(int fd, int argc, char *argv[])
00561 {
00562    struct eventqent *s;
00563 
00564    AST_LIST_LOCK(&all_events);
00565    AST_LIST_TRAVERSE(&all_events, s, eq_next) {
00566       ast_cli(fd, "Usecount: %d\n",s->usecount);
00567       ast_cli(fd, "Category: %d\n", s->category);
00568       ast_cli(fd, "Event:\n%s", s->eventdata);
00569    }
00570    AST_LIST_UNLOCK(&all_events);
00571 
00572    return RESULT_SUCCESS;
00573 }
00574 
00575 static char showmancmd_help[] =
00576 "Usage: manager show command <actionname>\n"
00577 "  Shows the detailed description for a specific Asterisk manager interface command.\n";
00578 
00579 static char showmancmds_help[] =
00580 "Usage: manager show commands\n"
00581 "  Prints a listing of all the available Asterisk manager interface commands.\n";
00582 
00583 static char showmanconn_help[] =
00584 "Usage: manager show connected\n"
00585 "  Prints a listing of the users that are currently connected to the\n"
00586 "Asterisk manager interface.\n";
00587 
00588 static char showmaneventq_help[] =
00589 "Usage: manager show eventq\n"
00590 "  Prints a listing of all events pending in the Asterisk manger\n"
00591 "event queue.\n";
00592 
00593 static char showmanagers_help[] =
00594 "Usage: manager show users\n"
00595 "       Prints a listing of all managers that are currently configured on that\n"
00596 " system.\n";
00597 
00598 static char showmanager_help[] =
00599 " Usage: manager show user <user>\n"
00600 "        Display all information related to the manager user specified.\n";
00601 
00602 static struct ast_cli_entry cli_manager[] = {
00603    { { "manager", "show", "command", NULL },
00604    handle_showmancmd, "Show a manager interface command",
00605    showmancmd_help, complete_show_mancmd },
00606 
00607    { { "manager", "show", "commands", NULL },
00608    handle_showmancmds, "List manager interface commands",
00609    showmancmds_help },
00610 
00611    { { "manager", "show", "connected", NULL },
00612    handle_showmanconn, "List connected manager interface users",
00613    showmanconn_help },
00614 
00615    { { "manager", "show", "eventq", NULL },
00616    handle_showmaneventq, "List manager interface queued events",
00617    showmaneventq_help },
00618 
00619    { { "manager", "show", "users", NULL },
00620    handle_showmanagers, "List configured manager users",
00621    showmanagers_help, NULL, NULL },
00622 
00623    { { "manager", "show", "user", NULL },
00624    handle_showmanager, "Display information on a specific manager user",
00625    showmanager_help, NULL, NULL },
00626 
00627    { { "manager", "debug", NULL },
00628    handle_mandebug, "Show, enable, disable debugging of the manager code",
00629    "Usage: manager debug [on|off]\n Show, enable, disable debugging of the manager code.\n", NULL, NULL },
00630 };
00631 
00632 /*
00633  * Decrement the usecount for the event; if it goes to zero,
00634  * (why check for e->next ?) wakeup the
00635  * main thread, which is in charge of freeing the record.
00636  * Returns the next record.
00637  */
00638 static struct eventqent *unref_event(struct eventqent *e)
00639 {
00640    ast_atomic_fetchadd_int(&e->usecount, -1);
00641    return AST_LIST_NEXT(e, eq_next);
00642 }
00643 
00644 static void ref_event(struct eventqent *e)
00645 {
00646    ast_atomic_fetchadd_int(&e->usecount, 1);
00647 }
00648 
00649 /*
00650  * destroy a session, leaving the usecount
00651  */
00652 static void free_session(struct mansession *s)
00653 {
00654    struct eventqent *eqe = s->last_ev;
00655    if (s->f != NULL)
00656       fclose(s->f);
00657    ast_mutex_destroy(&s->__lock);
00658    free(s);
00659    unref_event(eqe);
00660 }
00661 
00662 static void destroy_session(struct mansession *s)
00663 {
00664    AST_LIST_LOCK(&sessions);
00665    AST_LIST_REMOVE(&sessions, s, list);
00666    AST_LIST_UNLOCK(&sessions);
00667 
00668    ast_atomic_fetchadd_int(&num_sessions, -1);
00669    free_session(s);
00670 }
00671 
00672 const char *astman_get_header(const struct message *m, char *var)
00673 {
00674    int x, l = strlen(var);
00675 
00676    for (x = 0; x < m->hdrcount; x++) {
00677       const char *h = m->headers[x];
00678       if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
00679          return h + l + 2;
00680    }
00681 
00682    return "";
00683 }
00684 
00685 struct ast_variable *astman_get_variables(const struct message *m)
00686 {
00687    int varlen, x, y;
00688    struct ast_variable *head = NULL, *cur;
00689 
00690    AST_DECLARE_APP_ARGS(args,
00691       AST_APP_ARG(vars)[32];
00692    );
00693 
00694    varlen = strlen("Variable: ");
00695 
00696    for (x = 0; x < m->hdrcount; x++) {
00697       char *parse, *var, *val;
00698 
00699       if (strncasecmp("Variable: ", m->headers[x], varlen))
00700          continue;
00701       parse = ast_strdupa(m->headers[x] + varlen);
00702 
00703       AST_STANDARD_APP_ARGS(args, parse);
00704       if (!args.argc)
00705          continue;
00706       for (y = 0; y < args.argc; y++) {
00707          if (!args.vars[y])
00708             continue;
00709          var = val = ast_strdupa(args.vars[y]);
00710          strsep(&val, "=");
00711          if (!val || ast_strlen_zero(var))
00712             continue;
00713          cur = ast_variable_new(var, val);
00714          cur->next = head;
00715          head = cur;
00716       }
00717    }
00718 
00719    return head;
00720 }
00721 
00722 /*!
00723  * helper function to send a string to the socket.
00724  * Return -1 on error (e.g. buffer full).
00725  */
00726 static int send_string(struct mansession *s, char *string)
00727 {
00728    int len = strlen(string);  /* residual length */
00729    char *src = string;
00730    struct timeval start = ast_tvnow();
00731    int n = 0;
00732 
00733    for (;;) {
00734       int elapsed;
00735       struct pollfd fd;
00736       n = fwrite(src, 1, len, s->f);   /* try to write the string, non blocking */
00737       if (n == len /* ok */ || n < 0 /* error */)
00738          break;
00739       len -= n;   /* skip already written data */
00740       src += n;
00741       fd.fd = s->fd;
00742       fd.events = POLLOUT;
00743       n = -1;     /* error marker */
00744       elapsed = ast_tvdiff_ms(ast_tvnow(), start);
00745       if (elapsed > s->writetimeout)
00746          break;
00747       if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
00748          break;
00749    }
00750    fflush(s->f);
00751    return n < 0 ? -1 : 0;
00752 }
00753 
00754 /* XXX see if it can be moved inside the function */
00755 AST_THREADSTORAGE(astman_append_buf);
00756 #define ASTMAN_APPEND_BUF_INITSIZE   256
00757 
00758 /*!
00759  * utility functions for creating AMI replies
00760  */
00761 void astman_append(struct mansession *s, const char *fmt, ...)
00762 {
00763    va_list ap;
00764    struct ast_str *buf;
00765 
00766    if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
00767       return;
00768 
00769    va_start(ap, fmt);
00770    ast_str_set_va(&buf, 0, fmt, ap);
00771    va_end(ap);
00772 
00773    if (s->f != NULL)
00774       send_string(s, buf->str);
00775    else
00776       ast_verbose("fd == -1 in astman_append, should not happen\n");
00777 }
00778 
00779 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
00780    Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
00781    hold the session lock _or_ be running in an action callback (in which case s->busy will
00782    be non-zero). In either of these cases, there is no need to lock-protect the session's
00783    fd, since no other output will be sent (events will be queued), and no input will
00784    be read until either the current action finishes or get_input() obtains the session
00785    lock.
00786  */
00787 
00788 /*! \brief send a response with an optional message,
00789  * and terminate it with an empty line.
00790  * m is used only to grab the 'ActionID' field.
00791  *
00792  * Use the explicit constant MSG_MOREDATA to remove the empty line.
00793  * XXX MSG_MOREDATA should go to a header file.
00794  */
00795 #define MSG_MOREDATA ((char *)astman_send_response)
00796 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
00797 {
00798    const char *id = astman_get_header(m,"ActionID");
00799 
00800    astman_append(s, "Response: %s\r\n", resp);
00801    if (!ast_strlen_zero(id))
00802       astman_append(s, "ActionID: %s\r\n", id);
00803    if (listflag)
00804       astman_append(s, "Eventlist: %s\r\n", listflag);   /* Start, complete, cancelled */
00805    if (msg == MSG_MOREDATA)
00806       return;
00807    else if (msg)
00808       astman_append(s, "Message: %s\r\n\r\n", msg);
00809    else
00810       astman_append(s, "\r\n");
00811 }
00812 
00813 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
00814 {
00815    astman_send_response_full(s, m, resp, msg, NULL);
00816 }
00817 
00818 void astman_send_error(struct mansession *s, const struct message *m, char *error)
00819 {
00820    astman_send_response_full(s, m, "Error", error, NULL);
00821 }
00822 
00823 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
00824 {
00825    astman_send_response_full(s, m, "Success", msg, NULL);
00826 }
00827 
00828 static void astman_start_ack(struct mansession *s, const struct message *m)
00829 {
00830    astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
00831 }
00832 
00833 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
00834 {
00835    astman_send_response_full(s, m, "Success", msg, listflag);
00836 }
00837 
00838 
00839 /*! \brief
00840    Rather than braindead on,off this now can also accept a specific int mask value
00841    or a ',' delim list of mask strings (the same as manager.conf) -anthm
00842 */
00843 static int set_eventmask(struct mansession *s, const char *eventmask)
00844 {
00845    int maskint = strings_to_mask(eventmask);
00846 
00847    ast_mutex_lock(&s->__lock);
00848    if (maskint >= 0)
00849       s->send_events = maskint;
00850    ast_mutex_unlock(&s->__lock);
00851 
00852    return maskint;
00853 }
00854 
00855 /*
00856  * Here we start with action_ handlers for AMI actions,
00857  * and the internal functions used by them.
00858  * Generally, the handlers are called action_foo()
00859  */
00860 
00861 /* helper function for action_login() */
00862 static int authenticate(struct mansession *s, const struct message *m)
00863 {
00864    const char *user = astman_get_header(m, "Username");
00865    int error = -1;
00866    struct ast_ha *ha = NULL;
00867    char *password = NULL;
00868    int readperm = 0, writeperm = 0;
00869 
00870    if (ast_strlen_zero(user)) /* missing username */
00871       return -1;
00872 
00873     {
00874    /*
00875     * XXX there should be no need to scan the config file again here,
00876     * suffices to call get_manager_by_name_locked() to fetch
00877     * the user's entry.
00878     */
00879    struct ast_config *cfg = ast_config_load("manager.conf");
00880    char *cat = NULL;
00881    struct ast_variable *v;
00882 
00883    if (!cfg)
00884       return -1;
00885    while ( (cat = ast_category_browse(cfg, cat)) ) {
00886       /* "general" is not a valid user */
00887       if (strcasecmp(cat, user) || !strcasecmp(cat, "general"))
00888          continue;
00889       /* collect parameters for the user's entry */
00890       for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
00891          if (!strcasecmp(v->name, "secret"))
00892             password = ast_strdupa(v->value);
00893          else if (!strcasecmp(v->name, "read"))
00894             readperm = get_perm(v->value);
00895          else if (!strcasecmp(v->name, "write"))
00896             writeperm = get_perm(v->value);
00897          else if (!strcasecmp(v->name, "permit") ||
00898                !strcasecmp(v->name, "deny")) {
00899             ha = ast_append_ha(v->name, v->value, ha, NULL);
00900          } else if (!strcasecmp(v->name, "writetimeout")) {
00901             int val = atoi(v->value);
00902    
00903             if (val < 100)
00904                ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
00905             else
00906                s->writetimeout = val;
00907          }
00908       }
00909       break;
00910    }
00911 
00912    ast_config_destroy(cfg);
00913    if (!cat) {
00914       /* Didn't find the user in manager.conf, check users.conf */
00915       int hasmanager = 0;
00916       cfg = ast_config_load("users.conf");
00917       if (!cfg)
00918          return -1;
00919       while ( (cat = ast_category_browse(cfg, cat)) ) {
00920          if (!strcasecmp(cat, user) && strcasecmp(cat, "general"))
00921             break;
00922       }
00923       if (!cat) {
00924          ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
00925          ast_config_destroy(cfg);
00926          return -1;
00927       }
00928       /* collect parameters for the user's entry from users.conf */
00929       for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
00930          if (!strcasecmp(v->name, "secret"))
00931             password = ast_strdupa(v->value);
00932          else if (!strcasecmp(v->name, "read"))
00933             readperm = get_perm(v->value);
00934          else if (!strcasecmp(v->name, "write"))
00935             writeperm = get_perm(v->value);
00936          else if (!strcasecmp(v->name, "permit") ||
00937                !strcasecmp(v->name, "deny")) {
00938             ha = ast_append_ha(v->name, v->value, ha, NULL);
00939          } else if (!strcasecmp(v->name, "writetimeout")) {
00940             int val = atoi(v->value);
00941    
00942             if (val < 100)
00943                ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
00944             else
00945                s->writetimeout = val;
00946          } else if (!strcasecmp(v->name, "hasmanager")) {
00947             hasmanager = ast_true(v->value);
00948          }
00949       }
00950       ast_config_destroy(cfg);
00951       if (!hasmanager) {
00952          ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
00953          return -1;
00954       }
00955    }
00956 
00957    }
00958 
00959    if (ha) {
00960       int good = ast_apply_ha(ha, &(s->sin));
00961       ast_free_ha(ha);
00962       if (!good) {
00963          ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
00964          return -1;
00965       }
00966    }
00967    if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
00968       const char *key = astman_get_header(m, "Key");
00969       if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge)) {
00970          int x;
00971          int len = 0;
00972          char md5key[256] = "";
00973          struct MD5Context md5;
00974          unsigned char digest[16];
00975 
00976          MD5Init(&md5);
00977          MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
00978          MD5Update(&md5, (unsigned char *) password, strlen(password));
00979          MD5Final(digest, &md5);
00980          for (x=0; x<16; x++)
00981             len += sprintf(md5key + len, "%2.2x", digest[x]);
00982          if (!strcmp(md5key, key))
00983             error = 0;
00984       }
00985    } else if (password) {
00986       const char *pass = astman_get_header(m, "Secret");
00987       if (!strcmp(password, pass))
00988          error = 0;
00989    }
00990    if (error) {
00991       ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
00992       return -1;
00993    }
00994    ast_copy_string(s->username, user, sizeof(s->username));
00995    s->readperm = readperm;
00996    s->writeperm = writeperm;
00997    set_eventmask(s, astman_get_header(m, "Events"));
00998    return 0;
00999 }
01000 
01001 /*! \brief Manager PING */
01002 static char mandescr_ping[] =
01003 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the\n"
01004 "  manager connection open.\n"
01005 "Variables: NONE\n";
01006 
01007 static int action_ping(struct mansession *s, const struct message *m)
01008 {
01009    astman_send_response(s, m, "Pong", NULL);
01010    return 0;
01011 }
01012 
01013 static char mandescr_getconfig[] =
01014 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
01015 "file by category and contents.\n"
01016 "Variables:\n"
01017 "   Filename: Configuration filename (e.g. foo.conf)\n";
01018 
01019 static int action_getconfig(struct mansession *s, const struct message *m)
01020 {
01021    struct ast_config *cfg;
01022    const char *fn = astman_get_header(m, "Filename");
01023    int catcount = 0;
01024    int lineno = 0;
01025    char *category=NULL;
01026    struct ast_variable *v;
01027 
01028    if (ast_strlen_zero(fn)) {
01029       astman_send_error(s, m, "Filename not specified");
01030       return 0;
01031    }
01032    if (!(cfg = ast_config_load_with_comments(fn))) {
01033       astman_send_error(s, m, "Config file not found");
01034       return 0;
01035    }
01036    astman_start_ack(s, m);
01037    while ((category = ast_category_browse(cfg, category))) {
01038       lineno = 0;
01039       astman_append(s, "Category-%06d: %s\r\n", catcount, category);
01040       for (v = ast_variable_browse(cfg, category); v; v = v->next)
01041          astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
01042       catcount++;
01043    }
01044    ast_config_destroy(cfg);
01045    astman_append(s, "\r\n");
01046 
01047    return 0;
01048 }
01049 
01050 /* helper function for action_updateconfig */
01051 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg)
01052 {
01053    int x;
01054    char hdr[40];
01055    const char *action, *cat, *var, *value, *match;
01056    struct ast_category *category;
01057    struct ast_variable *v;
01058 
01059    for (x=0;x<100000;x++) {
01060       snprintf(hdr, sizeof(hdr), "Action-%06d", x);
01061       action = astman_get_header(m, hdr);
01062       if (ast_strlen_zero(action))
01063          break;
01064       snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
01065       cat = astman_get_header(m, hdr);
01066       snprintf(hdr, sizeof(hdr), "Var-%06d", x);
01067       var = astman_get_header(m, hdr);
01068       snprintf(hdr, sizeof(hdr), "Value-%06d", x);
01069       value = astman_get_header(m, hdr);
01070       snprintf(hdr, sizeof(hdr), "Match-%06d", x);
01071       match = astman_get_header(m, hdr);
01072       if (!strcasecmp(action, "newcat")) {
01073          if (!ast_strlen_zero(cat)) {
01074             category = ast_category_new(cat);
01075             if (category) {
01076                ast_category_append(cfg, category);
01077             }
01078          }
01079       } else if (!strcasecmp(action, "renamecat")) {
01080          if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
01081             category = ast_category_get(cfg, cat);
01082             if (category)
01083                ast_category_rename(category, value);
01084          }
01085       } else if (!strcasecmp(action, "delcat")) {
01086          if (!ast_strlen_zero(cat))
01087             ast_category_delete(cfg, cat);
01088       } else if (!strcasecmp(action, "update")) {
01089          if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
01090             ast_variable_update(category, var, value, match);
01091       } else if (!strcasecmp(action, "delete")) {
01092          if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
01093             ast_variable_delete(category, var, match);
01094       } else if (!strcasecmp(action, "append")) {
01095          if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
01096             (category = ast_category_get(cfg, cat)) &&
01097             (v = ast_variable_new(var, value))){
01098             if (match && !strcasecmp(match, "object"))
01099                v->object = 1;
01100             ast_variable_append(category, v);
01101          }
01102       }
01103    }
01104 }
01105 
01106 static char mandescr_updateconfig[] =
01107 "Description: A 'UpdateConfig' action will dump the contents of a configuration\n"
01108 "file by category and contents.\n"
01109 "Variables (X's represent 6 digit number beginning with 000000):\n"
01110 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
01111 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
01112 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
01113 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
01114 "   Cat-XXXXXX:    Category to operate on\n"
01115 "   Var-XXXXXX:    Variable to work on\n"
01116 "   Value-XXXXXX:  Value to work on\n"
01117 "   Match-XXXXXX:  Extra match required to match line\n";
01118 
01119 static int action_updateconfig(struct mansession *s, const struct message *m)
01120 {
01121    struct ast_config *cfg;
01122    const char *sfn = astman_get_header(m, "SrcFilename");
01123    const char *dfn = astman_get_header(m, "DstFilename");
01124    int res;
01125    const char *rld = astman_get_header(m, "Reload");
01126 
01127    if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
01128       astman_send_error(s, m, "Filename not specified");
01129       return 0;
01130    }
01131    if (!(cfg = ast_config_load_with_comments(sfn))) {
01132       astman_send_error(s, m, "Config file not found");
01133       return 0;
01134    }
01135    handle_updates(s, m, cfg);
01136    res = config_text_file_save(dfn, cfg, "Manager");
01137    ast_config_destroy(cfg);
01138    if (res) {
01139       astman_send_error(s, m, "Save of config failed");
01140       return 0;
01141    }
01142    astman_send_ack(s, m, NULL);
01143    if (!ast_strlen_zero(rld)) {
01144       if (ast_true(rld))
01145          rld = NULL;
01146       ast_module_reload(rld);
01147    }
01148    return 0;
01149 }
01150 
01151 /*! \brief Manager WAITEVENT */
01152 static char mandescr_waitevent[] =
01153 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
01154 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
01155 "session, events will be generated and queued.\n"
01156 "Variables: \n"
01157 "   Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
01158 
01159 static int action_waitevent(struct mansession *s, const struct message *m)
01160 {
01161    const char *timeouts = astman_get_header(m, "Timeout");
01162    int timeout = -1;
01163    int x;
01164    int needexit = 0;
01165    const char *id = astman_get_header(m,"ActionID");
01166    char idText[256] = "";
01167 
01168    if (!ast_strlen_zero(id))
01169       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01170 
01171    if (!ast_strlen_zero(timeouts)) {
01172       sscanf(timeouts, "%i", &timeout);
01173       if (timeout < -1)
01174          timeout = -1;
01175       /* XXX maybe put an upper bound, or prevent the use of 0 ? */
01176    }
01177 
01178    ast_mutex_lock(&s->__lock);
01179    if (s->waiting_thread != AST_PTHREADT_NULL)
01180       pthread_kill(s->waiting_thread, SIGURG);
01181 
01182    if (s->managerid) { /* AMI-over-HTTP session */
01183       /*
01184        * Make sure the timeout is within the expire time of the session,
01185        * as the client will likely abort the request if it does not see
01186        * data coming after some amount of time.
01187        */
01188       time_t now = time(NULL);
01189       int max = s->sessiontimeout - now - 10;
01190 
01191       if (max < 0)   /* We are already late. Strange but possible. */
01192          max = 0;
01193       if (timeout < 0 || timeout > max)
01194          timeout = max;
01195       if (!s->send_events) /* make sure we record events */
01196          s->send_events = -1;
01197    }
01198    ast_mutex_unlock(&s->__lock);
01199 
01200    /* XXX should this go inside the lock ? */
01201    s->waiting_thread = pthread_self(); /* let new events wake up this thread */
01202    if (option_debug)
01203       ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
01204 
01205    for (x=0; x < timeout || timeout < 0; x++) {
01206       ast_mutex_lock(&s->__lock);
01207       if (NEW_EVENT(s))
01208          needexit = 1;
01209       /* We can have multiple HTTP session point to the same mansession entry.
01210        * The way we deal with it is not very nice: newcomers kick out the previous
01211        * HTTP session. XXX this needs to be improved.
01212        */
01213       if (s->waiting_thread != pthread_self())
01214          needexit = 1;
01215       if (s->needdestroy)
01216          needexit = 1;
01217       ast_mutex_unlock(&s->__lock);
01218       if (needexit)
01219          break;
01220       if (s->managerid == 0) {   /* AMI session */
01221          if (ast_wait_for_input(s->fd, 1000))
01222             break;
01223       } else { /* HTTP session */
01224          sleep(1);
01225       }
01226    }
01227    if (option_debug)
01228       ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
01229    ast_mutex_lock(&s->__lock);
01230    if (s->waiting_thread == pthread_self()) {
01231       struct eventqent *eqe;
01232       astman_send_response(s, m, "Success", "Waiting for Event completed.");
01233       while ( (eqe = NEW_EVENT(s)) ) {
01234          ref_event(eqe);
01235          if (((s->readperm & eqe->category) == eqe->category) &&
01236              ((s->send_events & eqe->category) == eqe->category)) {
01237             astman_append(s, "%s", eqe->eventdata);
01238          }
01239          s->last_ev = unref_event(s->last_ev);
01240       }
01241       astman_append(s,
01242          "Event: WaitEventComplete\r\n"
01243          "%s"
01244          "\r\n", idText);
01245       s->waiting_thread = AST_PTHREADT_NULL;
01246    } else {
01247       if (option_debug)
01248          ast_log(LOG_DEBUG, "Abandoning event request!\n");
01249    }
01250    ast_mutex_unlock(&s->__lock);
01251    return 0;
01252 }
01253 
01254 static char mandescr_listcommands[] =
01255 "Description: Returns the action name and synopsis for every\n"
01256 "  action that is available to the user\n"
01257 "Variables: NONE\n";
01258 
01259 /*! \note The actionlock is read-locked by the caller of this function */
01260 static int action_listcommands(struct mansession *s, const struct message *m)
01261 {
01262    struct manager_action *cur;
01263    struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
01264 
01265    astman_start_ack(s, m);
01266    for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
01267       if ((s->writeperm & cur->authority) == cur->authority)
01268          astman_append(s, "%s: %s (Priv: %s)\r\n",
01269             cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
01270    }
01271    astman_append(s, "\r\n");
01272 
01273    return 0;
01274 }
01275 
01276 static char mandescr_events[] =
01277 "Description: Enable/Disable sending of events to this manager\n"
01278 "  client.\n"
01279 "Variables:\n"
01280 "  EventMask: 'on' if all events should be sent,\n"
01281 "     'off' if no events should be sent,\n"
01282 "     'system,call,log' to select which flags events should have to be sent.\n";
01283 
01284 static int action_events(struct mansession *s, const struct message *m)
01285 {
01286    const char *mask = astman_get_header(m, "EventMask");
01287    int res;
01288 
01289    res = set_eventmask(s, mask);
01290    if (res > 0)
01291       astman_send_response(s, m, "Events On", NULL);
01292    else if (res == 0)
01293       astman_send_response(s, m, "Events Off", NULL);
01294 
01295    return 0;
01296 }
01297 
01298 static char mandescr_logoff[] =
01299 "Description: Logoff this manager session\n"
01300 "Variables: NONE\n";
01301 
01302 static int action_logoff(struct mansession *s, const struct message *m)
01303 {
01304    astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
01305    return -1;
01306 }
01307 
01308 static int action_login(struct mansession *s, const struct message *m)
01309 {
01310    if (authenticate(s, m)) {
01311       sleep(1);
01312       astman_send_error(s, m, "Authentication failed");
01313       return -1;
01314    }
01315    s->authenticated = 1;
01316    if (option_verbose > 1) {
01317       if (displayconnects) {
01318          ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
01319       }
01320    }
01321    ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
01322    astman_send_ack(s, m, "Authentication accepted");
01323    return 0;
01324 }
01325 
01326 static int action_challenge(struct mansession *s, const struct message *m)
01327 {
01328    const char *authtype = astman_get_header(m, "AuthType");
01329 
01330    if (!strcasecmp(authtype, "MD5")) {
01331       if (ast_strlen_zero(s->challenge))
01332          snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
01333       ast_mutex_lock(&s->__lock);
01334       astman_start_ack(s, m);
01335       astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
01336       ast_mutex_unlock(&s->__lock);
01337    } else {
01338       astman_send_error(s, m, "Must specify AuthType");
01339    }
01340    return 0;
01341 }
01342 
01343 static char mandescr_hangup[] =
01344 "Description: Hangup a channel\n"
01345 "Variables: \n"
01346 "  Channel: The channel name to be hungup\n";
01347 
01348 static int action_hangup(struct mansession *s, const struct message *m)
01349 {
01350    struct ast_channel *c = NULL;
01351    const char *name = astman_get_header(m, "Channel");
01352    if (ast_strlen_zero(name)) {
01353       astman_send_error(s, m, "No channel specified");
01354       return 0;
01355    }
01356    c = ast_get_channel_by_name_locked(name);
01357    if (!c) {
01358       astman_send_error(s, m, "No such channel");
01359       return 0;
01360    }
01361    ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
01362    ast_channel_unlock(c);
01363    astman_send_ack(s, m, "Channel Hungup");
01364    return 0;
01365 }
01366 
01367 static char mandescr_setvar[] =
01368 "Description: Set a global or local channel variable.\n"
01369 "Variables: (Names marked with * are required)\n"
01370 "  Channel: Channel to set variable for\n"
01371 "  *Variable: Variable name\n"
01372 "  *Value: Value\n";
01373 
01374 static int action_setvar(struct mansession *s, const struct message *m)
01375 {
01376         struct ast_channel *c = NULL;
01377    const char *name = astman_get_header(m, "Channel");
01378    const char *varname = astman_get_header(m, "Variable");
01379    const char *varval = astman_get_header(m, "Value");
01380 
01381    if (ast_strlen_zero(varname)) {
01382       astman_send_error(s, m, "No variable specified");
01383       return 0;
01384    }
01385 
01386    if (ast_strlen_zero(varval)) {
01387       astman_send_error(s, m, "No value specified");
01388       return 0;
01389    }
01390 
01391    if (!ast_strlen_zero(name)) {
01392       c = ast_get_channel_by_name_locked(name);
01393       if (!c) {
01394          astman_send_error(s, m, "No such channel");
01395          return 0;
01396       }
01397    }
01398 
01399    pbx_builtin_setvar_helper(c, varname, varval);
01400 
01401    if (c)
01402       ast_channel_unlock(c);
01403 
01404    astman_send_ack(s, m, "Variable Set");
01405 
01406    return 0;
01407 }
01408 
01409 static char mandescr_getvar[] =
01410 "Description: Get the value of a global or local channel variable.\n"
01411 "Variables: (Names marked with * are required)\n"
01412 "  Channel: Channel to read variable from\n"
01413 "  *Variable: Variable name\n"
01414 "  ActionID: Optional Action id for message matching.\n";
01415 
01416 static int action_getvar(struct mansession *s, const struct message *m)
01417 {
01418    struct ast_channel *c = NULL;
01419    const char *name = astman_get_header(m, "Channel");
01420    const char *varname = astman_get_header(m, "Variable");
01421    char *varval;
01422    char workspace[1024];
01423 
01424    if (ast_strlen_zero(varname)) {
01425       astman_send_error(s, m, "No variable specified");
01426       return 0;
01427    }
01428 
01429    if (!ast_strlen_zero(name)) {
01430       c = ast_get_channel_by_name_locked(name);
01431       if (!c) {
01432          astman_send_error(s, m, "No such channel");
01433          return 0;
01434       }
01435    }
01436 
01437    if (varname[strlen(varname) - 1] == ')') {
01438       ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
01439    } else {
01440       pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
01441    }
01442 
01443    if (c)
01444       ast_channel_unlock(c);
01445    astman_start_ack(s, m);
01446    astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
01447 
01448    return 0;
01449 }
01450 
01451 
01452 /*! \brief Manager "status" command to show channels */
01453 /* Needs documentation... */
01454 static int action_status(struct mansession *s, const struct message *m)
01455 {
01456    const char *name = astman_get_header(m,"Channel");
01457    struct ast_channel *c;
01458    char bridge[256];
01459    struct timeval now = ast_tvnow();
01460    long elapsed_seconds = 0;
01461    int all = ast_strlen_zero(name); /* set if we want all channels */
01462    const char *id = astman_get_header(m,"ActionID");
01463    char idText[256] = "";
01464 
01465    if (!ast_strlen_zero(id))
01466       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01467 
01468    astman_send_ack(s, m, "Channel status will follow");
01469    if (all)
01470       c = ast_channel_walk_locked(NULL);
01471    else {
01472       c = ast_get_channel_by_name_locked(name);
01473       if (!c) {
01474          astman_send_error(s, m, "No such channel");
01475          return 0;
01476       }
01477    }
01478    /* if we look by name, we break after the first iteration */
01479    while (c) {
01480       if (c->_bridge)
01481          snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
01482       else
01483          bridge[0] = '\0';
01484       if (c->pbx) {
01485          if (c->cdr) {
01486             elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01487          }
01488          astman_append(s,
01489          "Event: Status\r\n"
01490          "Privilege: Call\r\n"
01491          "Channel: %s\r\n"
01492          "CallerIDNum: %s\r\n"
01493          "CallerIDName: %s\r\n"
01494          "Account: %s\r\n"
01495          "State: %s\r\n"
01496          "Context: %s\r\n"
01497          "Extension: %s\r\n"
01498          "Priority: %d\r\n"
01499          "Seconds: %ld\r\n"
01500          "%s"
01501          "Uniqueid: %s\r\n"
01502          "%s"
01503          "\r\n",
01504          c->name,
01505          S_OR(c->cid.cid_num, "<unknown>"),
01506          S_OR(c->cid.cid_name, "<unknown>"),
01507          c->accountcode,
01508          ast_state2str(c->_state), c->context,
01509          c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
01510       } else {
01511          astman_append(s,
01512          "Event: Status\r\n"
01513          "Privilege: Call\r\n"
01514          "Channel: %s\r\n"
01515          "CallerIDNum: %s\r\n"
01516          "CallerIDName: %s\r\n"
01517          "Account: %s\r\n"
01518          "State: %s\r\n"
01519          "%s"
01520          "Uniqueid: %s\r\n"
01521          "%s"
01522          "\r\n",
01523          c->name,
01524          S_OR(c->cid.cid_num, "<unknown>"),
01525          S_OR(c->cid.cid_name, "<unknown>"),
01526          c->accountcode,
01527          ast_state2str(c->_state), bridge, c->uniqueid, idText);
01528       }
01529       ast_channel_unlock(c);
01530       if (!all)
01531          break;
01532       c = ast_channel_walk_locked(c);
01533    }
01534    astman_append(s,
01535    "Event: StatusComplete\r\n"
01536    "%s"
01537    "\r\n",idText);
01538    return 0;
01539 }
01540 
01541 static char mandescr_sendtext[] =
01542 "Description: Sends A Text Message while in a call.\n"
01543 "Variables: (Names marked with * are required)\n"
01544 "       *Channel: Channel to send message to\n"
01545 "       *Message: Message to send\n"
01546 "       ActionID: Optional Action id for message matching.\n";
01547 
01548 static int action_sendtext(struct mansession *s, const struct message *m)
01549 {
01550    struct ast_channel *c = NULL;
01551    const char *name = astman_get_header(m, "Channel");
01552    const char *textmsg = astman_get_header(m, "Message");
01553    int res = 0;
01554 
01555    if (ast_strlen_zero(name)) {
01556       astman_send_error(s, m, "No channel specified");
01557       return 0;
01558    }
01559 
01560    if (ast_strlen_zero(textmsg)) {
01561       astman_send_error(s, m, "No Message specified");
01562       return 0;
01563    }
01564 
01565    c = ast_get_channel_by_name_locked(name);
01566    if (!c) {
01567       astman_send_error(s, m, "No such channel");
01568       return 0;
01569    }
01570 
01571    res = ast_sendtext(c, textmsg);
01572    ast_mutex_unlock(&c->lock);
01573    
01574    if (res > 0)
01575       astman_send_ack(s, m, "Success");
01576    else
01577       astman_send_error(s, m, "Failure");
01578    
01579    return res;
01580 }
01581 
01582 static char mandescr_redirect[] =
01583 "Description: Redirect (transfer) a call.\n"
01584 "Variables: (Names marked with * are required)\n"
01585 "  *Channel: Channel to redirect\n"
01586 "  ExtraChannel: Second call leg to transfer (optional)\n"
01587 "  *Exten: Extension to transfer to\n"
01588 "  *Context: Context to transfer to\n"
01589 "  *Priority: Priority to transfer to\n"
01590 "  ActionID: Optional Action id for message matching.\n";
01591 
01592 /*! \brief  action_redirect: The redirect manager command */
01593 static int action_redirect(struct mansession *s, const struct message *m)
01594 {
01595    const char *name = astman_get_header(m, "Channel");
01596    const char *name2 = astman_get_header(m, "ExtraChannel");
01597    const char *exten = astman_get_header(m, "Exten");
01598    const char *context = astman_get_header(m, "Context");
01599    const char *priority = astman_get_header(m, "Priority");
01600    struct ast_channel *chan, *chan2 = NULL;
01601    int pi = 0;
01602    int res;
01603 
01604    if (ast_strlen_zero(name)) {
01605       astman_send_error(s, m, "Channel not specified");
01606       return 0;
01607    }
01608    if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
01609       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
01610          astman_send_error(s, m, "Invalid priority\n");
01611          return 0;
01612       }
01613    }
01614    /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
01615    chan = ast_get_channel_by_name_locked(name);
01616    if (!chan) {
01617       char buf[BUFSIZ];
01618       snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
01619       astman_send_error(s, m, buf);
01620       return 0;
01621    }
01622    if (!ast_strlen_zero(name2))
01623       chan2 = ast_get_channel_by_name_locked(name2);
01624    res = ast_async_goto(chan, context, exten, pi);
01625    if (!res) {
01626       if (!ast_strlen_zero(name2)) {
01627          if (chan2)
01628             res = ast_async_goto(chan2, context, exten, pi);
01629          else
01630             res = -1;
01631          if (!res)
01632             astman_send_ack(s, m, "Dual Redirect successful");
01633          else
01634             astman_send_error(s, m, "Secondary redirect failed");
01635       } else
01636          astman_send_ack(s, m, "Redirect successful");
01637    } else
01638       astman_send_error(s, m, "Redirect failed");
01639    if (chan)
01640       ast_channel_unlock(chan);
01641    if (chan2)
01642       ast_channel_unlock(chan2);
01643    return 0;
01644 }
01645 
01646 static char mandescr_command[] =
01647 "Description: Run a CLI command.\n"
01648 "Variables: (Names marked with * are required)\n"
01649 "  *Command: Asterisk CLI command to run\n"
01650 "  ActionID: Optional Action id for message matching.\n";
01651 
01652 /*! \brief  Manager command "command" - execute CLI command */
01653 static int action_command(struct mansession *s, const struct message *m)
01654 {
01655    const char *cmd = astman_get_header(m, "Command");
01656    const char *id = astman_get_header(m, "ActionID");
01657    char *buf;
01658    char template[] = "/tmp/ast-ami-XXXXXX";  /* template for temporary file */
01659    int fd = mkstemp(template);
01660    off_t l;
01661 
01662    astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
01663    if (!ast_strlen_zero(id))
01664       astman_append(s, "ActionID: %s\r\n", id);
01665    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
01666    ast_cli_command(fd, cmd);  /* XXX need to change this to use a FILE * */
01667    l = lseek(fd, 0, SEEK_END);   /* how many chars available */
01668    buf = alloca(l+1);
01669    lseek(fd, 0, SEEK_SET);
01670    read(fd, buf, l);
01671    buf[l] = '\0';
01672    close(fd);
01673    unlink(template);
01674    astman_append(s, buf);
01675    astman_append(s, "--END COMMAND--\r\n\r\n");
01676    return 0;
01677 }
01678 
01679 /* helper function for originate */
01680 struct fast_originate_helper {
01681    char tech[AST_MAX_EXTENSION];
01682    char data[AST_MAX_EXTENSION];
01683    int timeout;
01684    char app[AST_MAX_APP];
01685    char appdata[AST_MAX_EXTENSION];
01686    char cid_name[AST_MAX_EXTENSION];
01687    char cid_num[AST_MAX_EXTENSION];
01688    char context[AST_MAX_CONTEXT];
01689    char exten[AST_MAX_EXTENSION];
01690    char idtext[AST_MAX_EXTENSION];
01691    char account[AST_MAX_ACCOUNT_CODE];
01692    int priority;
01693    struct ast_variable *vars;
01694 };
01695 
01696 static void *fast_originate(void *data)
01697 {
01698    struct fast_originate_helper *in = data;
01699    int res;
01700    int reason = 0;
01701    struct ast_channel *chan = NULL;
01702    char requested_channel[AST_CHANNEL_NAME];
01703 
01704    if (!ast_strlen_zero(in->app)) {
01705       res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
01706          S_OR(in->cid_num, NULL),
01707          S_OR(in->cid_name, NULL),
01708          in->vars, in->account, &chan);
01709    } else {
01710       res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
01711          S_OR(in->cid_num, NULL),
01712          S_OR(in->cid_name, NULL),
01713          in->vars, in->account, &chan);
01714    }
01715 
01716    if (!chan)
01717       snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);   
01718    /* Tell the manager what happened with the channel */
01719    manager_event(EVENT_FLAG_CALL, "OriginateResponse",
01720       "%s"
01721       "Response: %s\r\n"
01722       "Channel: %s\r\n"
01723       "Context: %s\r\n"
01724       "Exten: %s\r\n"
01725       "Reason: %d\r\n"
01726       "Uniqueid: %s\r\n"
01727       "CallerIDNum: %s\r\n"
01728       "CallerIDName: %s\r\n",
01729       in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason, 
01730       chan ? chan->uniqueid : "<null>",
01731       S_OR(in->cid_num, "<unknown>"),
01732       S_OR(in->cid_name, "<unknown>")
01733       );
01734 
01735    /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
01736    if (chan)
01737       ast_channel_unlock(chan);
01738    free(in);
01739    return NULL;
01740 }
01741 
01742 static char mandescr_originate[] =
01743 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
01744 "  Application/Data\n"
01745 "Variables: (Names marked with * are required)\n"
01746 "  *Channel: Channel name to call\n"
01747 "  Exten: Extension to use (requires 'Context' and 'Priority')\n"
01748 "  Context: Context to use (requires 'Exten' and 'Priority')\n"
01749 "  Priority: Priority to use (requires 'Exten' and 'Context')\n"
01750 "  Application: Application to use\n"
01751 "  Data: Data to use (requires 'Application')\n"
01752 "  Timeout: How long to wait for call to be answered (in ms)\n"
01753 "  CallerID: Caller ID to be set on the outgoing channel\n"
01754 "  Variable: Channel variable to set, multiple Variable: headers are allowed\n"
01755 "  Account: Account code\n"
01756 "  Async: Set to 'true' for fast origination\n";
01757 
01758 static int action_originate(struct mansession *s, const struct message *m)
01759 {
01760    const char *name = astman_get_header(m, "Channel");
01761    const char *exten = astman_get_header(m, "Exten");
01762    const char *context = astman_get_header(m, "Context");
01763    const char *priority = astman_get_header(m, "Priority");
01764    const char *timeout = astman_get_header(m, "Timeout");
01765    const char *callerid = astman_get_header(m, "CallerID");
01766    const char *account = astman_get_header(m, "Account");
01767    const char *app = astman_get_header(m, "Application");
01768    const char *appdata = astman_get_header(m, "Data");
01769    const char *async = astman_get_header(m, "Async");
01770    const char *id = astman_get_header(m, "ActionID");
01771    struct ast_variable *vars = astman_get_variables(m);
01772    char *tech, *data;
01773    char *l = NULL, *n = NULL;
01774    int pi = 0;
01775    int res;
01776    int to = 30000;
01777    int reason = 0;
01778    char tmp[256];
01779    char tmp2[256];
01780 
01781    pthread_t th;
01782    pthread_attr_t attr;
01783    if (!name) {
01784       astman_send_error(s, m, "Channel not specified");
01785       return 0;
01786    }
01787    if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
01788       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
01789          astman_send_error(s, m, "Invalid priority\n");
01790          return 0;
01791       }
01792    }
01793    if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
01794       astman_send_error(s, m, "Invalid timeout\n");
01795       return 0;
01796    }
01797    ast_copy_string(tmp, name, sizeof(tmp));
01798    tech = tmp;
01799    data = strchr(tmp, '/');
01800    if (!data) {
01801       astman_send_error(s, m, "Invalid channel\n");
01802       return 0;
01803    }
01804    *data++ = '\0';
01805    ast_copy_string(tmp2, callerid, sizeof(tmp2));
01806    ast_callerid_parse(tmp2, &n, &l);
01807    if (n) {
01808       if (ast_strlen_zero(n))
01809          n = NULL;
01810    }
01811    if (l) {
01812       ast_shrink_phone_number(l);
01813       if (ast_strlen_zero(l))
01814          l = NULL;
01815    }
01816    if (ast_true(async)) {
01817       struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
01818       if (!fast) {
01819          res = -1;
01820       } else {
01821          if (!ast_strlen_zero(id))
01822             snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
01823          ast_copy_string(fast->tech, tech, sizeof(fast->tech));
01824             ast_copy_string(fast->data, data, sizeof(fast->data));
01825          ast_copy_string(fast->app, app, sizeof(fast->app));
01826          ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
01827          if (l)
01828             ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
01829          if (n)
01830             ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
01831          fast->vars = vars;
01832          ast_copy_string(fast->context, context, sizeof(fast->context));
01833          ast_copy_string(fast->exten, exten, sizeof(fast->exten));
01834          ast_copy_string(fast->account, account, sizeof(fast->account));
01835          fast->timeout = to;
01836          fast->priority = pi;
01837          pthread_attr_init(&attr);
01838          pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01839          if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
01840             res = -1;
01841          } else {
01842             res = 0;
01843          }
01844          pthread_attr_destroy(&attr);
01845       }
01846    } else if (!ast_strlen_zero(app)) {
01847          res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
01848       } else {
01849       if (exten && context && pi)
01850             res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
01851       else {
01852          astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
01853          return 0;
01854       }
01855    }
01856    if (!res)
01857       astman_send_ack(s, m, "Originate successfully queued");
01858    else
01859       astman_send_error(s, m, "Originate failed");
01860    return 0;
01861 }
01862 
01863 /*! \brief Help text for manager command mailboxstatus
01864  */
01865 static char mandescr_mailboxstatus[] =
01866 "Description: Checks a voicemail account for status.\n"
01867 "Variables: (Names marked with * are required)\n"
01868 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
01869 "  ActionID: Optional ActionID for message matching.\n"
01870 "Returns number of messages.\n"
01871 "  Message: Mailbox Status\n"
01872 "  Mailbox: <mailboxid>\n"
01873 "  Waiting: <count>\n"
01874 "\n";
01875 
01876 static int action_mailboxstatus(struct mansession *s, const struct message *m)
01877 {
01878    const char *mailbox = astman_get_header(m, "Mailbox");
01879    int ret;
01880 
01881    if (ast_strlen_zero(mailbox)) {
01882       astman_send_error(s, m, "Mailbox not specified");
01883       return 0;
01884    }
01885    ret = ast_app_has_voicemail(mailbox, NULL);
01886    astman_start_ack(s, m);
01887    astman_append(s, "Message: Mailbox Status\r\n"
01888           "Mailbox: %s\r\n"
01889           "Waiting: %d\r\n\r\n", mailbox, ret);
01890    return 0;
01891 }
01892 
01893 static char mandescr_mailboxcount[] =
01894 "Description: Checks a voicemail account for new messages.\n"
01895 "Variables: (Names marked with * are required)\n"
01896 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
01897 "  ActionID: Optional ActionID for message matching.\n"
01898 "Returns number of new and old messages.\n"
01899 "  Message: Mailbox Message Count\n"
01900 "  Mailbox: <mailboxid>\n"
01901 "  NewMessages: <count>\n"
01902 "  OldMessages: <count>\n"
01903 "\n";
01904 static int action_mailboxcount(struct mansession *s, const struct message *m)
01905 {
01906    const char *mailbox = astman_get_header(m, "Mailbox");
01907    int newmsgs = 0, oldmsgs = 0;
01908 
01909    if (ast_strlen_zero(mailbox)) {
01910       astman_send_error(s, m, "Mailbox not specified");
01911       return 0;
01912    }
01913    ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
01914    astman_start_ack(s, m);
01915    astman_append(s,   "Message: Mailbox Message Count\r\n"
01916             "Mailbox: %s\r\n"
01917             "NewMessages: %d\r\n"
01918             "OldMessages: %d\r\n"
01919             "\r\n",
01920             mailbox, newmsgs, oldmsgs);
01921    return 0;
01922 }
01923 
01924 static char mandescr_extensionstate[] =
01925 "Description: Report the extension state for given extension.\n"
01926 "  If the extension has a hint, will use devicestate to check\n"
01927 "  the status of the device connected to the extension.\n"
01928 "Variables: (Names marked with * are required)\n"
01929 "  *Exten: Extension to check state on\n"
01930 "  *Context: Context for extension\n"
01931 "  ActionId: Optional ID for this transaction\n"
01932 "Will return an \"Extension Status\" message.\n"
01933 "The response will include the hint for the extension and the status.\n";
01934 
01935 static int action_extensionstate(struct mansession *s, const struct message *m)
01936 {
01937    const char *exten = astman_get_header(m, "Exten");
01938    const char *context = astman_get_header(m, "Context");
01939    char hint[256] = "";
01940    int status;
01941    if (ast_strlen_zero(exten)) {
01942       astman_send_error(s, m, "Extension not specified");
01943       return 0;
01944    }
01945    if (ast_strlen_zero(context))
01946       context = "default";
01947    status = ast_extension_state(NULL, context, exten);
01948    ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
01949    astman_start_ack(s, m);
01950    astman_append(s,   "Message: Extension Status\r\n"
01951             "Exten: %s\r\n"
01952             "Context: %s\r\n"
01953             "Hint: %s\r\n"
01954             "Status: %d\r\n\r\n",
01955             exten, context, hint, status);
01956    return 0;
01957 }
01958 
01959 static char mandescr_timeout[] =
01960 "Description: Hangup a channel after a certain time.\n"
01961 "Variables: (Names marked with * are required)\n"
01962 "  *Channel: Channel name to hangup\n"
01963 "  *Timeout: Maximum duration of the call (sec)\n"
01964 "Acknowledges set time with 'Timeout Set' message\n";
01965 
01966 static int action_timeout(struct mansession *s, const struct message *m)
01967 {
01968    struct ast_channel *c;
01969    const char *name = astman_get_header(m, "Channel");
01970    int timeout = atoi(astman_get_header(m, "Timeout"));
01971 
01972    if (ast_strlen_zero(name)) {
01973       astman_send_error(s, m, "No channel specified");
01974       return 0;
01975    }
01976    if (!timeout) {
01977       astman_send_error(s, m, "No timeout specified");
01978       return 0;
01979    }
01980    c = ast_get_channel_by_name_locked(name);
01981    if (!c) {
01982       astman_send_error(s, m, "No such channel");
01983       return 0;
01984    }
01985    ast_channel_setwhentohangup(c, timeout);
01986    ast_channel_unlock(c);
01987    astman_send_ack(s, m, "Timeout Set");
01988    return 0;
01989 }
01990 
01991 /*!
01992  * Send any applicable events to the client listening on this socket.
01993  * Wait only for a finite time on each event, and drop all events whether
01994  * they are successfully sent or not.
01995  */
01996 static int process_events(struct mansession *s)
01997 {
01998    int ret = 0;
01999 
02000    ast_mutex_lock(&s->__lock);
02001    if (s->f != NULL) {
02002       struct eventqent *eqe;
02003 
02004       while ( (eqe = NEW_EVENT(s)) ) {
02005          ref_event(eqe);
02006          if (!ret && s->authenticated &&
02007              (s->readperm & eqe->category) == eqe->category &&
02008              (s->send_events & eqe->category) == eqe->category) {
02009             if (send_string(s, eqe->eventdata) < 0)
02010                ret = -1;   /* don't send more */
02011          }
02012          s->last_ev = unref_event(s->last_ev);
02013       }
02014    }
02015    ast_mutex_unlock(&s->__lock);
02016    return ret;
02017 }
02018 
02019 static char mandescr_userevent[] =
02020 "Description: Send an event to manager sessions.\n"
02021 "Variables: (Names marked with * are required)\n"
02022 "       *UserEvent: EventStringToSend\n"
02023 "       Header1: Content1\n"
02024 "       HeaderN: ContentN\n";
02025 
02026 static int action_userevent(struct mansession *s, const struct message *m)
02027 {
02028    const char *event = astman_get_header(m, "UserEvent");
02029    char body[2048] = "";
02030    int x, bodylen = 0;
02031    for (x = 0; x < m->hdrcount; x++) {
02032       if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
02033          ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
02034          bodylen += strlen(m->headers[x]);
02035          ast_copy_string(body + bodylen, "\r\n", 3);
02036          bodylen += 2;
02037       }
02038    }
02039 
02040    manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
02041    return 0;
02042 }
02043 
02044 /*
02045  * Done with the action handlers here, we start with the code in charge
02046  * of accepting connections and serving them.
02047  * accept_thread() forks a new thread for each connection, session_do(),
02048  * which in turn calls get_input() repeatedly until a full message has
02049  * been accumulated, and then invokes process_message() to pass it to
02050  * the appropriate handler.
02051  */
02052 
02053 /*
02054  * Process an AMI message, performing desired action.
02055  * Return 0 on success, -1 on error that require the session to be destroyed.
02056  */
02057 static int process_message(struct mansession *s, const struct message *m)
02058 {
02059    char action[80] = "";
02060    int ret = 0;
02061    struct manager_action *tmp;
02062 
02063    ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
02064    if (option_debug)
02065       ast_log(LOG_DEBUG, "Manager received command '%s'\n", action);
02066 
02067    if (ast_strlen_zero(action)) {
02068       astman_send_error(s, m, "Missing action in request");
02069       return 0;
02070    }
02071 
02072    if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
02073       ast_mutex_lock(&s->__lock);
02074       astman_send_error(s, m, "Permission denied");
02075       ast_mutex_unlock(&s->__lock);
02076       return 0;
02077    }
02078    ast_rwlock_rdlock(&actionlock);  
02079    for (tmp = first_action ; tmp; tmp = tmp->next) {
02080       if (strcasecmp(action, tmp->action))
02081          continue;
02082       if ((s->writeperm & tmp->authority) == tmp->authority) {
02083          if (tmp->func(s, m)) {  /* error */
02084             return -1;
02085          }
02086       } else
02087          astman_send_error(s, m, "Permission denied");
02088       break;
02089    }
02090    ast_rwlock_unlock(&actionlock);
02091    if (!tmp) {
02092       ast_mutex_lock(&s->__lock);
02093       astman_send_error(s, m, "Invalid/unknown command. Use Action: ListCommands to show available commands.");
02094       ast_mutex_unlock(&s->__lock);
02095    }
02096    if (ret)
02097       return ret;
02098    /* Once done with our message, deliver any pending events */
02099    return process_events(s);
02100 }
02101 
02102 /*!
02103  * Read one full line (including crlf) from the manager socket.
02104  * \r\n is the only valid terminator for the line.
02105  * (Note that, later, '\0' will be considered as the end-of-line marker,
02106  * so everything between the '\0' and the '\r\n' will not be used).
02107  * Also note that we assume output to have at least "maxlen" space.
02108  */
02109 static int get_input(struct mansession *s, char *output)
02110 {
02111    int res, x;
02112    int maxlen = sizeof(s->inbuf) - 1;
02113    char *src = s->inbuf;
02114 
02115    /*
02116     * Look for \r\n within the buffer. If found, copy to the output
02117     * buffer and return, trimming the \r\n (not used afterwards).
02118     */
02119    for (x = 0; x < s->inlen; x++) {
02120       int cr;  /* set if we have \r */
02121       if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
02122          cr = 2;  /* Found. Update length to include \r\n */
02123       else if (src[x] == '\n')
02124          cr = 1;  /* also accept \n only */
02125       else
02126          continue;
02127       memmove(output, src, x);   /*... but trim \r\n */
02128       output[x] = '\0';    /* terminate the string */
02129       x += cr;       /* number of bytes used */
02130       s->inlen -= x;       /* remaining size */
02131       memmove(src, src + x, s->inlen); /* remove used bytes */
02132       return 1;
02133    }
02134    if (s->inlen >= maxlen) {
02135       /* no crlf found, and buffer full - sorry, too long for us */
02136       ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
02137       s->inlen = 0;
02138    }
02139    res = 0;
02140    while (res == 0) {
02141       /* XXX do we really need this locking ? */
02142       ast_mutex_lock(&s->__lock);
02143       s->waiting_thread = pthread_self();
02144       ast_mutex_unlock(&s->__lock);
02145 
02146       res = ast_wait_for_input(s->fd, -1);   /* return 0 on timeout ? */
02147 
02148       ast_mutex_lock(&s->__lock);
02149       s->waiting_thread = AST_PTHREADT_NULL;
02150       ast_mutex_unlock(&s->__lock);
02151    }
02152    if (res < 0) {
02153       /* If we get a signal from some other thread (typically because
02154        * there are new events queued), return 0 to notify the caller.
02155        */
02156       if (errno == EINTR)
02157          return 0;
02158       ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
02159       return -1;
02160    }
02161    ast_mutex_lock(&s->__lock);
02162    res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
02163    if (res < 1)
02164       res = -1;   /* error return */
02165    else {
02166       s->inlen += res;
02167       src[s->inlen] = '\0';
02168       res = 0;
02169    }
02170    ast_mutex_unlock(&s->__lock);
02171    return res;
02172 }
02173 
02174 static int do_message(struct mansession *s)
02175 {
02176    struct message m = { 0 };
02177    char header_buf[sizeof(s->inbuf)] = { '\0' };
02178    int res;
02179 
02180    for (;;) {
02181       /* Check if any events are pending and do them if needed */
02182       if (process_events(s))
02183          return -1;
02184       res = get_input(s, header_buf);
02185       if (res == 0) {
02186          continue;
02187       } else if (res > 0) {
02188          if (ast_strlen_zero(header_buf))
02189             return process_message(s, &m) ? -1 : 0;
02190          else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
02191             m.headers[m.hdrcount++] = ast_strdupa(header_buf);
02192       } else {
02193          return res;
02194       }
02195    }
02196 }
02197 
02198 /*! \brief The body of the individual manager session.
02199  * Call get_input() to read one line at a time
02200  * (or be woken up on new events), collect the lines in a
02201  * message until found an empty line, and execute the request.
02202  * In any case, deliver events asynchronously through process_events()
02203  * (called from here if no line is available, or at the end of
02204  * process_message(). )
02205  */
02206 static void *session_do(void *data)
02207 {
02208    struct server_instance *ser = data;
02209    struct mansession *s = ast_calloc(1, sizeof(*s));
02210    int flags;
02211    int res;
02212 
02213    if (s == NULL)
02214       goto done;
02215 
02216    s->writetimeout = 100;
02217    s->waiting_thread = AST_PTHREADT_NULL;
02218 
02219    flags = fcntl(ser->fd, F_GETFL);
02220    if (!block_sockets) /* make sure socket is non-blocking */
02221       flags |= O_NONBLOCK;
02222    else
02223       flags &= ~O_NONBLOCK;
02224    fcntl(ser->fd, F_SETFL, flags);
02225 
02226    ast_mutex_init(&s->__lock);
02227    s->send_events = -1;
02228    /* these fields duplicate those in the 'ser' structure */
02229    s->fd = ser->fd;
02230    s->f = ser->f;
02231    s->sin = ser->requestor;
02232 
02233    ast_atomic_fetchadd_int(&num_sessions, 1);
02234    AST_LIST_LOCK(&sessions);
02235    AST_LIST_INSERT_HEAD(&sessions, s, list);
02236    AST_LIST_UNLOCK(&sessions);
02237    /* Hook to the tail of the event queue */
02238    s->last_ev = grab_last();
02239    s->f = ser->f;
02240    astman_append(s, "Asterisk Call Manager/1.0\r\n"); /* welcome prompt */
02241    for (;;) {
02242       if ((res = do_message(s)) < 0)
02243          break;
02244    }
02245    /* session is over, explain why and terminate */
02246    if (s->authenticated) {
02247       if (option_verbose > 1) {
02248          if (displayconnects)
02249             ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
02250       }
02251       ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
02252    } else {
02253       if (option_verbose > 1) {
02254          if (displayconnects)
02255             ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
02256       }
02257       ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
02258    }
02259    destroy_session(s);
02260 
02261 done:
02262    free(ser);
02263    return NULL;
02264 }
02265 
02266 /*! \brief remove at most n_max stale session from the list. */
02267 static void purge_sessions(int n_max)
02268 {
02269    struct mansession *s;
02270    time_t now = time(NULL);
02271 
02272    AST_LIST_LOCK(&sessions);
02273    AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
02274       if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
02275          AST_LIST_REMOVE_CURRENT(&sessions, list);
02276          ast_atomic_fetchadd_int(&num_sessions, -1);
02277          if (s->authenticated && (option_verbose > 1) && displayconnects) {
02278             ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
02279                s->username, ast_inet_ntoa(s->sin.sin_addr));
02280          }
02281          free_session(s);  /* XXX outside ? */
02282          if (--n_max <= 0)
02283             break;
02284       }
02285    }
02286    AST_LIST_TRAVERSE_SAFE_END
02287    AST_LIST_UNLOCK(&sessions);
02288 }
02289 
02290 /*
02291  * events are appended to a queue from where they
02292  * can be dispatched to clients.
02293  */
02294 static int append_event(const char *str, int category)
02295 {
02296    struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
02297    static int seq;   /* sequence number */
02298 
02299    if (!tmp)
02300       return -1;
02301 
02302    /* need to init all fields, because ast_malloc() does not */
02303    tmp->usecount = 0;
02304    tmp->category = category;
02305    tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
02306    AST_LIST_NEXT(tmp, eq_next) = NULL;
02307    strcpy(tmp->eventdata, str);
02308 
02309    AST_LIST_LOCK(&all_events);
02310    AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
02311    AST_LIST_UNLOCK(&all_events);
02312 
02313    return 0;
02314 }
02315 
02316 /* XXX see if can be moved inside the function */
02317 AST_THREADSTORAGE(manager_event_buf);
02318 #define MANAGER_EVENT_BUF_INITSIZE   256
02319 
02320 /*! \brief  manager_event: Send AMI event to client */
02321 int __manager_event(int category, const char *event,
02322    const char *file, int line, const char *func, const char *fmt, ...)
02323 {
02324    struct mansession *s;
02325    struct manager_custom_hook *hook;
02326    struct ast_str *auth = ast_str_alloca(80);
02327    const char *cat_str;
02328    va_list ap;
02329    struct timeval now;
02330    struct ast_str *buf;
02331 
02332    /* Abort if there aren't any manager sessions */
02333    if (!num_sessions)
02334       return 0;
02335 
02336    if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
02337       return -1;
02338 
02339    cat_str = authority_to_str(category, &auth);
02340    ast_str_set(&buf, 0,
02341          "Event: %s\r\nPrivilege: %s\r\n",
02342           event, cat_str);
02343 
02344    if (timestampevents) {
02345       now = ast_tvnow();
02346       ast_str_append(&buf, 0,
02347             "Timestamp: %ld.%06lu\r\n",
02348              now.tv_sec, (unsigned long) now.tv_usec);
02349    }
02350    if (manager_debug) {
02351       static int seq;
02352       ast_str_append(&buf, 0,
02353             "SequenceNumber: %d\r\n",
02354              ast_atomic_fetchadd_int(&seq, 1));
02355       ast_str_append(&buf, 0,
02356             "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
02357    }
02358 
02359    va_start(ap, fmt);
02360    ast_str_append_va(&buf, 0, fmt, ap);
02361    va_end(ap);
02362 
02363    ast_str_append(&buf, 0, "\r\n");
02364 
02365    append_event(buf->str, category);
02366 
02367    /* Wake up any sleeping sessions */
02368    AST_LIST_LOCK(&sessions);
02369    AST_LIST_TRAVERSE(&sessions, s, list) {
02370       ast_mutex_lock(&s->__lock);
02371       if (s->waiting_thread != AST_PTHREADT_NULL)
02372          pthread_kill(s->waiting_thread, SIGURG);
02373       ast_mutex_unlock(&s->__lock);
02374    }
02375    AST_LIST_UNLOCK(&sessions);
02376 
02377    AST_RWLIST_RDLOCK(&manager_hooks);
02378    if (!AST_RWLIST_EMPTY(&manager_hooks)) {
02379       AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
02380          hook->helper(category, event, buf->str);
02381       }
02382    }
02383    AST_RWLIST_UNLOCK(&manager_hooks);
02384 
02385    return 0;
02386 }
02387 
02388 /*
02389  * support functions to register/unregister AMI action handlers,
02390  */
02391 int ast_manager_unregister(char *action)
02392 {
02393    struct manager_action *cur, *prev;
02394 
02395    ast_rwlock_wrlock(&actionlock);
02396    for (cur = first_action, prev = NULL; cur; prev = cur, cur = cur->next) {
02397       if (!strcasecmp(action, cur->action)) {
02398          if (prev)
02399             prev->next = cur->next;
02400          else
02401             first_action = cur->next;
02402          free(cur);
02403          if (option_verbose > 1)
02404             ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
02405          break;
02406       }
02407    }
02408    ast_rwlock_unlock(&actionlock);
02409    return 0;
02410 }
02411 
02412 static int manager_state_cb(char *context, char *exten, int state, void *data)
02413 {
02414    /* Notify managers of change */
02415    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
02416    return 0;
02417 }
02418 
02419 static int ast_manager_register_struct(struct manager_action *act)
02420 {
02421    struct manager_action *cur, *prev = NULL;
02422    int ret;
02423 
02424    ast_rwlock_wrlock(&actionlock);
02425    for (cur = first_action; cur; prev = cur, cur = cur->next) {
02426       ret = strcasecmp(cur->action, act->action);
02427       if (ret == 0) {
02428          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
02429          ast_rwlock_unlock(&actionlock);
02430          return -1;
02431       }
02432       if (ret > 0)   /* Insert these alphabetically */
02433          break;
02434    }
02435    if (prev)
02436       prev->next = act;
02437    else
02438       first_action = act;
02439    act->next = cur;
02440 
02441    if (option_verbose > 1)
02442       ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
02443    ast_rwlock_unlock(&actionlock);
02444    return 0;
02445 }
02446 
02447 /*! \brief register a new command with manager, including online help. This is
02448    the preferred way to register a manager command */
02449 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
02450 {
02451    struct manager_action *cur;
02452 
02453    cur = ast_malloc(sizeof(*cur));
02454    if (!cur)
02455       return -1;
02456 
02457    cur->action = action;
02458    cur->authority = auth;
02459    cur->func = func;
02460    cur->synopsis = synopsis;
02461    cur->description = description;
02462    cur->next = NULL;
02463 
02464    ast_manager_register_struct(cur);
02465 
02466    return 0;
02467 }
02468 /*! @}
02469  END Doxygen group */
02470 
02471 /*
02472  * The following are support functions for AMI-over-http.
02473  * The common entry point is generic_http_callback(),
02474  * which extracts HTTP header and URI fields and reformats
02475  * them into AMI messages, locates a proper session
02476  * (using the mansession_id Cookie or GET variable),
02477  * and calls process_message() as for regular AMI clients.
02478  * When done, the output (which goes to a temporary file)
02479  * is read back into a buffer and reformatted as desired,
02480  * then fed back to the client over the original socket.
02481  */
02482 
02483 enum output_format {
02484    FORMAT_RAW,
02485    FORMAT_HTML,
02486    FORMAT_XML,
02487 };
02488 
02489 static char *contenttype[] = {
02490    [FORMAT_RAW] = "plain",
02491    [FORMAT_HTML] = "html",
02492    [FORMAT_XML] =  "xml",
02493 };
02494 
02495 /*!
02496  * locate an http session in the list. The search key (ident) is
02497  * the value of the mansession_id cookie (0 is not valid and means
02498  * a session on the AMI socket).
02499  */
02500 static struct mansession *find_session(unsigned long ident)
02501 {
02502    struct mansession *s;
02503 
02504    if (ident == 0)
02505       return NULL;
02506 
02507    AST_LIST_LOCK(&sessions);
02508    AST_LIST_TRAVERSE(&sessions, s, list) {
02509       ast_mutex_lock(&s->__lock);
02510       if (s->managerid == ident && !s->needdestroy) {
02511          ast_atomic_fetchadd_int(&s->inuse, 1);
02512          break;
02513       }
02514       ast_mutex_unlock(&s->__lock);
02515    }
02516    AST_LIST_UNLOCK(&sessions);
02517 
02518    return s;
02519 }
02520 
02521 /*
02522  * convert to xml with various conversion:
02523  * mode & 1 -> lowercase;
02524  * mode & 2 -> replace non-alphanumeric chars with underscore
02525  */
02526 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
02527 {
02528    /* store in a local buffer to avoid calling ast_str_append too often */
02529    char buf[256];
02530    char *dst = buf;
02531    int space = sizeof(buf);
02532    /* repeat until done and nothing to flush */
02533    for ( ; *src || dst != buf ; src++) {
02534       if (*src == '\0' || space < 10) {   /* flush */
02535          *dst++ = '\0';
02536          ast_str_append(out, 0, "%s", buf);
02537          dst = buf;
02538          space = sizeof(buf);
02539          if (*src == '\0')
02540             break;
02541       }
02542          
02543       if ( (mode & 2) && !isalnum(*src)) {
02544          *dst++ = '_';
02545          space--;
02546          continue;
02547       }
02548       switch (*src) {
02549       case '<':
02550          strcpy(dst, "&lt;");
02551          dst += 4;
02552          space -= 4;
02553          break;
02554       case '>':
02555          strcpy(dst, "&gt;");
02556          dst += 4;
02557          space -= 4;
02558          break;
02559       case '\"':
02560          strcpy(dst, "&quot;");
02561          dst += 6;
02562          space -= 6;
02563          break;
02564       case '\'':
02565          strcpy(dst, "&apos;");
02566          dst += 6;
02567          space -= 6;
02568          break;
02569       case '&':
02570          strcpy(dst, "&amp;");
02571          dst += 5;
02572          space -= 5;
02573          break;
02574 
02575       default:
02576          *dst++ = mode ? tolower(*src) : *src;
02577          space--;
02578       }
02579    }
02580 }
02581 
02582 /*! \brief Convert the input into XML or HTML.
02583  * The input is supposed to be a sequence of lines of the form
02584  * Name: value
02585  * optionally followed by a blob of unformatted text.
02586  * A blank line is a section separator. Basically, this is a
02587  * mixture of the format of Manager Interface and CLI commands.
02588  * The unformatted text is considered as a single value of a field
02589  * named 'Opaque-data'.
02590  *
02591  * At the moment the output format is the following (but it may
02592  * change depending on future requirements so don't count too
02593  * much on it when writing applications):
02594  *
02595  * General: the unformatted text is used as a value of
02596  * XML output:  to be completed
02597  *   Each section is within <response type="object" id="xxx">
02598  *   where xxx is taken from ajaxdest variable or defaults to unknown
02599  *   Each row is reported as an attribute Name="value" of an XML
02600  *   entity named from the variable ajaxobjtype, default to "generic"
02601  *
02602  * HTML output:
02603  *   each Name-value pair is output as a single row of a two-column table.
02604  *   Sections (blank lines in the input) are separated by a <HR>
02605  *
02606  */
02607 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
02608 {
02609    struct ast_variable *v;
02610    char *dest = NULL;
02611    char *var, *val;
02612    char *objtype = NULL;
02613    int in_data = 0;  /* parsing data */
02614    int inobj = 0;
02615    int xml = (format == FORMAT_XML);
02616 
02617    for (v = vars; v; v = v->next) {
02618       if (!dest && !strcasecmp(v->name, "ajaxdest"))
02619          dest = v->value;
02620       else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
02621          objtype = v->value;
02622    }
02623    if (!dest)
02624       dest = "unknown";
02625    if (!objtype)
02626       objtype = "generic";
02627 #if 0
02628    /* determine how large is the response.
02629     * This is a heuristic - counting colons (for headers),
02630     * newlines (for extra arguments), and escaped chars.
02631     * XXX needs to be checked carefully for overflows.
02632     * Even better, use some code that allows extensible strings.
02633     */
02634    for (x = 0; in[x]; x++) {
02635       if (in[x] == ':')
02636          colons++;
02637       else if (in[x] == '\n')
02638          breaks++;
02639       else if (strchr("&\"<>", in[x]))
02640          escaped++;
02641    }
02642    len = (size_t) (1 + strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
02643    out = ast_malloc(len);
02644    if (!out)
02645       return NULL;
02646    tmp = out;
02647    *tmp = '\0';
02648 #endif
02649    /* we want to stop when we find an empty line */
02650    while (in && *in) {
02651       val = strsep(&in, "\r\n"); /* mark start and end of line */
02652       if (in && *in == '\n')     /* remove trailing \n if any */
02653          in++;
02654       ast_trim_blanks(val);
02655       if (0)
02656          ast_verbose("inobj %d in_data %d line <%s>\n", inobj, in_data, val);
02657       if (ast_strlen_zero(val)) {
02658          if (in_data) { /* close data */
02659             ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
02660             in_data = 0;
02661          }
02662          ast_str_append(out, 0, xml ? " /></response>\n" :
02663             "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
02664          inobj = 0;
02665          continue;
02666       }
02667       /* we expect Name: value lines */
02668       if (in_data) {
02669          var = NULL;
02670       } else {
02671          var = strsep(&val, ":");
02672          if (val) {  /* found the field name */
02673             val = ast_skip_blanks(val);
02674             ast_trim_blanks(var);
02675          } else {    /* field name not found, move to opaque mode */
02676             val = var;
02677             var = "Opaque-data";
02678          }
02679       }
02680       if (!inobj) {
02681          if (xml)
02682             ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
02683          else
02684             ast_str_append(out, 0, "<body>\n");
02685          inobj = 1;
02686       }
02687       if (!in_data) {   /* build appropriate line start */
02688          ast_str_append(out, 0, xml ? " " : "<tr><td>");
02689          xml_copy_escape(out, var, xml ? 1 | 2 : 0);
02690          ast_str_append(out, 0, xml ? "='" : "</td><td>");
02691          if (!strcmp(var, "Opaque-data"))
02692             in_data = 1;
02693       }
02694       xml_copy_escape(out, val, 0); /* data field */
02695       if (!in_data)
02696          ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
02697       else
02698          ast_str_append(out, 0, xml ? "\n" : "<br>\n");
02699    }
02700    if (inobj)
02701       ast_str_append(out, 0, xml ? " /></response>\n" :
02702          "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
02703 }
02704 
02705 static struct ast_str *generic_http_callback(enum output_format format,
02706                     struct sockaddr_in *requestor, const char *uri,
02707                     struct ast_variable *params, int *status,
02708                     char **title, int *contentlength)
02709 {
02710    struct mansession *s = NULL;
02711    unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
02712    int blastaway = 0;
02713    struct ast_variable *v;
02714    char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
02715    struct ast_str *out = NULL;
02716    struct message m = { 0 };
02717    unsigned int x;
02718    size_t hdrlen;
02719 
02720    for (v = params; v; v = v->next) {
02721       if (!strcasecmp(v->name, "mansession_id")) {
02722          sscanf(v->value, "%lx", &ident);
02723          break;
02724       }
02725    }
02726 
02727    if (!(s = find_session(ident))) {
02728       /* Create new session.
02729        * While it is not in the list we don't need any locking
02730        */
02731       if (!(s = ast_calloc(1, sizeof(*s)))) {
02732          *status = 500;
02733          goto generic_callback_out;
02734       }
02735       s->sin = *requestor;
02736       s->fd = -1;
02737       s->waiting_thread = AST_PTHREADT_NULL;
02738       s->send_events = 0;
02739       ast_mutex_init(&s->__lock);
02740       ast_mutex_lock(&s->__lock);
02741       s->inuse = 1;
02742       s->managerid = rand() | 1; /* make sure it is non-zero */
02743       s->last_ev = grab_last();
02744       AST_LIST_LOCK(&sessions);
02745       AST_LIST_INSERT_HEAD(&sessions, s, list);
02746       AST_LIST_UNLOCK(&sessions);
02747       ast_atomic_fetchadd_int(&num_sessions, 1);
02748    }
02749 
02750    ast_mutex_unlock(&s->__lock);
02751 
02752    if (!(out = ast_str_create(1024))) {
02753       *status = 500;
02754       goto generic_callback_out;
02755    }
02756 
02757    s->fd = mkstemp(template); /* create a temporary file for command output */
02758    unlink(template);
02759    s->f = fdopen(s->fd, "w+");
02760 
02761    for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
02762       hdrlen = strlen(v->name) + strlen(v->value) + 3;
02763       m.headers[m.hdrcount] = alloca(hdrlen);
02764       snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
02765       m.hdrcount = x + 1;
02766    }
02767 
02768    if (process_message(s, &m)) {
02769       if (s->authenticated) {
02770          if (option_verbose > 1) {
02771             if (displayconnects)
02772                ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
02773          }
02774          ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
02775       } else {
02776          if (option_verbose > 1) {
02777             if (displayconnects)
02778                ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
02779          }
02780          ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
02781       }
02782       s->needdestroy = 1;
02783    }
02784 
02785    ast_str_append(&out, 0,
02786              "Content-type: text/%s\r\n"
02787              "Cache-Control: no-cache;\r\n"
02788              "Set-Cookie: mansession_id=\"%08lx\"; Version=\"1\"; Max-Age=%d\r\n"
02789              "\r\n",
02790          contenttype[format],
02791          s->managerid, httptimeout);
02792 
02793    if (format == FORMAT_XML) {
02794       ast_str_append(&out, 0, "<ajax-response>\n");
02795    } else if (format == FORMAT_HTML) {
02796 
02797 #define ROW_FMT   "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
02798 #define TEST_STRING \
02799    "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
02800    user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
02801    <input type=\"submit\"></form>"
02802 
02803       ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
02804       ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
02805       ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
02806       ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
02807    }
02808 
02809    if (s->f != NULL) {  /* have temporary output */
02810       char *buf;
02811       size_t l = ftell(s->f);
02812 
02813       if (format == FORMAT_XML || format == FORMAT_HTML) {
02814          if (l) {
02815             if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0))) {
02816                xml_translate(&out, buf, params, format);
02817                munmap(buf, l);
02818             }
02819          } else {
02820             xml_translate(&out, "", params, format);
02821          }
02822       }
02823       fclose(s->f);
02824       s->f = NULL;
02825       s->fd = -1;
02826    }
02827 
02828    if (format == FORMAT_XML) {
02829       ast_str_append(&out, 0, "</ajax-response>\n");
02830    } else if (format == FORMAT_HTML)
02831       ast_str_append(&out, 0, "</table></body>\r\n");
02832 
02833    ast_mutex_lock(&s->__lock);
02834    /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
02835    s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
02836 
02837    if (s->needdestroy) {
02838       if (s->inuse == 1) {
02839          if (option_debug)
02840             ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
02841          blastaway = 1;
02842       } else {
02843          if (option_debug)
02844             ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
02845          if (s->waiting_thread != AST_PTHREADT_NULL)
02846             pthread_kill(s->waiting_thread, SIGURG);
02847          s->inuse--;
02848       }
02849    } else
02850       s->inuse--;
02851    ast_mutex_unlock(&s->__lock);
02852 
02853    if (blastaway)
02854       destroy_session(s);
02855 generic_callback_out:
02856    if (*status != 200)
02857       return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
02858    return out;
02859 }
02860 
02861 static struct ast_str *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
02862 {
02863    return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
02864 }
02865 
02866 static struct ast_str *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
02867 {
02868    return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
02869 }
02870 
02871 static struct ast_str *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
02872 {
02873    return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
02874 }
02875 
02876 struct ast_http_uri rawmanuri = {
02877    .description = "Raw HTTP Manager Event Interface",
02878    .uri = "rawman",
02879    .has_subtree = 0,
02880    .callback = rawman_http_callback,
02881 };
02882 
02883 struct ast_http_uri manageruri = {
02884    .description = "HTML Manager Event Interface",
02885    .uri = "manager",
02886    .has_subtree = 0,
02887    .callback = manager_http_callback,
02888 };
02889 
02890 struct ast_http_uri managerxmluri = {
02891    .description = "XML Manager Event Interface",
02892    .uri = "mxml",
02893    .has_subtree = 0,
02894    .callback = mxml_http_callback,
02895 };
02896 
02897 static int registered = 0;
02898 static int webregged = 0;
02899 
02900 /*! \brief cleanup code called at each iteration of server_root,
02901  * guaranteed to happen every 5 seconds at most
02902  */
02903 static void purge_old_stuff(void *data)
02904 {
02905    purge_sessions(1);
02906    purge_events();
02907 }
02908 
02909 struct tls_config ami_tls_cfg;
02910 static struct server_args ami_desc = {
02911         .accept_fd = -1,
02912         .master = AST_PTHREADT_NULL,
02913         .tls_cfg = NULL, 
02914         .poll_timeout = 5000, /* wake up every 5 seconds */
02915    .periodic_fn = purge_old_stuff,
02916         .name = "AMI server",
02917         .accept_fn = server_root,   /* thread doing the accept() */
02918         .worker_fn = session_do, /* thread handling the session */
02919 };
02920 
02921 static struct server_args amis_desc = {
02922         .accept_fd = -1,
02923         .master = AST_PTHREADT_NULL,
02924         .tls_cfg = &ami_tls_cfg, 
02925         .poll_timeout = -1,   /* the other does the periodic cleanup */
02926         .name = "AMI TLS server",
02927         .accept_fn = server_root,   /* thread doing the accept() */
02928         .worker_fn = session_do, /* thread handling the session */
02929 };
02930 
02931 int init_manager(void)
02932 {
02933    struct ast_config *cfg = NULL;
02934    const char *val;
02935    char *cat = NULL;
02936    int webenabled = 0;
02937    int enabled = 0;
02938    int newhttptimeout = 60;
02939    int have_sslbindaddr = 0;
02940    struct hostent *hp;
02941    struct ast_hostent ahp;
02942    struct ast_manager_user *user = NULL;
02943    struct ast_variable *var;
02944 
02945    if (!registered) {
02946       /* Register default actions */
02947       ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
02948       ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
02949       ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
02950       ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
02951       ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
02952       ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
02953       ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
02954       ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
02955       ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
02956       ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
02957       ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
02958       ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
02959       ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
02960       ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
02961       ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
02962       ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
02963       ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
02964       ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
02965       ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
02966       ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
02967       ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
02968       ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
02969 
02970       ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
02971       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
02972       registered = 1;
02973       /* Append placeholder event so master_eventq never runs dry */
02974       append_event("Event: Placeholder\r\n\r\n", 0);
02975    }
02976    displayconnects = 1;
02977    cfg = ast_config_load("manager.conf");
02978    if (!cfg) {
02979       ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
02980       return 0;
02981    }
02982 
02983    /* default values */
02984    memset(&ami_desc.sin, 0, sizeof(struct sockaddr_in));
02985    memset(&amis_desc.sin, 0, sizeof(amis_desc.sin));
02986    amis_desc.sin.sin_port = htons(5039);
02987    ami_desc.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
02988 
02989    ami_tls_cfg.enabled = 0;
02990    if (ami_tls_cfg.certfile)
02991       free(ami_tls_cfg.certfile);
02992    ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
02993    if (ami_tls_cfg.cipher)
02994       free(ami_tls_cfg.cipher);
02995    ami_tls_cfg.cipher = ast_strdup("");
02996 
02997    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
02998       val = var->value;
02999       if (!strcasecmp(var->name, "ssenable"))
03000          ami_tls_cfg.enabled = ast_true(val);
03001       else if (!strcasecmp(var->name, "ssbindport"))
03002          amis_desc.sin.sin_port = htons(atoi(val));
03003       else if (!strcasecmp(var->name, "ssbindaddr")) {
03004          if ((hp = ast_gethostbyname(val, &ahp))) {
03005             memcpy(&amis_desc.sin.sin_addr, hp->h_addr, sizeof(amis_desc.sin.sin_addr));
03006             have_sslbindaddr = 1;
03007          } else {
03008             ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val);
03009          }
03010       } else if (!strcasecmp(var->name, "sslcert")) {
03011          free(ami_tls_cfg.certfile);
03012          ami_tls_cfg.certfile = ast_strdup(val);
03013       } else if (!strcasecmp(var->name, "sslcipher")) {
03014          free(ami_tls_cfg.cipher);
03015          ami_tls_cfg.cipher = ast_strdup(val);
03016       } else if (!strcasecmp(var->name, "enabled")) {
03017          enabled = ast_true(val);
03018       } else if (!strcasecmp(var->name, "block-sockets")) {
03019          block_sockets = ast_true(val);
03020       } else if (!strcasecmp(var->name, "webenabled")) {
03021          webenabled = ast_true(val);
03022       } else if (!strcasecmp(var->name, "port")) {
03023          ami_desc.sin.sin_port = htons(atoi(val));
03024       } else if (!strcasecmp(var->name, "bindaddr")) {
03025          if (!inet_aton(val, &ami_desc.sin.sin_addr)) {
03026             ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
03027             memset(&ami_desc.sin.sin_addr, 0, sizeof(ami_desc.sin.sin_addr));
03028          }
03029       } else if (!strcasecmp(var->name, "displayconnects")) {
03030          displayconnects = ast_true(val);
03031       } else if (!strcasecmp(var->name, "timestampevents")) {
03032          timestampevents = ast_true(val);
03033       } else if (!strcasecmp(var->name, "debug")) {
03034          manager_debug = ast_true(val);
03035       } else if (!strcasecmp(var->name, "httptimeout")) {
03036          newhttptimeout = atoi(val);
03037       } else {
03038          ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
03039             var->name, val);
03040       }  
03041    }
03042 
03043    if (enabled)
03044       ami_desc.sin.sin_family = AF_INET;
03045    if (!have_sslbindaddr)
03046       amis_desc.sin.sin_addr = ami_desc.sin.sin_addr;
03047    if (ami_tls_cfg.enabled)
03048       amis_desc.sin.sin_family = AF_INET;
03049 
03050    
03051    AST_LIST_LOCK(&users);
03052 
03053    while ((cat = ast_category_browse(cfg, cat))) {
03054 
03055       if (!strcasecmp(cat, "general"))
03056          continue;
03057 
03058       /* Look for an existing entry, if none found - create one and add it to the list */
03059       if (!(user = get_manager_by_name_locked(cat))) {
03060          if (!(user = ast_calloc(1, sizeof(*user))))
03061             break;
03062          /* Copy name over */
03063          ast_copy_string(user->username, cat, sizeof(user->username));
03064          /* Insert into list */
03065          AST_LIST_INSERT_TAIL(&users, user, list);
03066       }
03067 
03068       /* Make sure we keep this user and don't destroy it during cleanup */
03069       user->keep = 1;
03070 
03071       var = ast_variable_browse(cfg, cat);
03072       while (var) {
03073          if (!strcasecmp(var->name, "secret")) {
03074             if (user->secret)
03075                free(user->secret);
03076             user->secret = ast_strdup(var->value);
03077          } else if (!strcasecmp(var->name, "deny") ) {
03078             if (user->deny)
03079                free(user->deny);
03080             user->deny = ast_strdup(var->value);
03081          } else if (!strcasecmp(var->name, "permit") ) {
03082             if (user->permit)
03083                free(user->permit);
03084             user->permit = ast_strdup(var->value);
03085          }  else if (!strcasecmp(var->name, "read") ) {
03086             if (user->read)
03087                free(user->read);
03088             user->read = ast_strdup(var->value);
03089          }  else if (!strcasecmp(var->name, "write") ) {
03090             if (user->write)
03091                free(user->write);
03092             user->write = ast_strdup(var->value);
03093          }  else if (!strcasecmp(var->name, "displayconnects") )
03094             user->displayconnects = ast_true(var->value);
03095          else {
03096             if (option_debug)
03097                ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
03098          }
03099          var = var->next;
03100       }
03101    }
03102 
03103    /* Perform cleanup - essentially prune out old users that no longer exist */
03104    AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
03105       if (user->keep) { /* valid record. clear flag for the next round */
03106          user->keep = 0;
03107          continue;
03108       }
03109       /* We do not need to keep this user so take them out of the list */
03110       AST_LIST_REMOVE_CURRENT(&users, list);
03111       /* Free their memory now */
03112       if (user->secret)
03113          free(user->secret);
03114       if (user->deny)
03115          free(user->deny);
03116       if (user->permit)
03117          free(user->permit);
03118       if (user->read)
03119          free(user->read);
03120       if (user->write)
03121          free(user->write);
03122       free(user);
03123    }
03124    AST_LIST_TRAVERSE_SAFE_END
03125 
03126    AST_LIST_UNLOCK(&users);
03127 
03128    ast_config_destroy(cfg);
03129 
03130    if (webenabled && enabled) {
03131       if (!webregged) {
03132          ast_http_uri_link(&rawmanuri);
03133          ast_http_uri_link(&manageruri);
03134          ast_http_uri_link(&managerxmluri);
03135          webregged = 1;
03136       }
03137    } else {
03138       if (webregged) {
03139          ast_http_uri_unlink(&rawmanuri);
03140          ast_http_uri_unlink(&manageruri);
03141          ast_http_uri_unlink(&managerxmluri);
03142          webregged = 0;
03143       }
03144    }
03145 
03146    if (newhttptimeout > 0)
03147       httptimeout = newhttptimeout;
03148 
03149    server_start(&ami_desc);
03150    if (ssl_setup(amis_desc.tls_cfg))
03151       server_start(&amis_desc);
03152    return 0;
03153 }
03154 
03155 int reload_manager(void)
03156 {
03157    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
03158    return init_manager();
03159 }

Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen