Fri Jan 29 14:25:16 2010

Asterisk developer's documentation


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  * Channel Management and more
00026  * 
00027  * \ref amiconf
00028  */
00029 
00030 /*! \addtogroup Group_AMI AMI functions 
00031 */
00032 /*! @{ 
00033  Doxygen group */
00034 
00035 #include "asterisk.h"
00036 
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 232581 $")
00038 
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #include <string.h>
00042 #include <ctype.h>
00043 #include <sys/time.h>
00044 #include <sys/types.h>
00045 #include <netdb.h>
00046 #include <sys/socket.h>
00047 #include <netinet/in.h>
00048 #include <netinet/tcp.h>
00049 #include <arpa/inet.h>
00050 #include <signal.h>
00051 #include <errno.h>
00052 #include <unistd.h>
00053 #include <sys/mman.h>
00054 
00055 #include "asterisk/version.h"
00056 #include "asterisk/channel.h"
00057 #include "asterisk/file.h"
00058 #include "asterisk/manager.h"
00059 #include "asterisk/config.h"
00060 #include "asterisk/callerid.h"
00061 #include "asterisk/lock.h"
00062 #include "asterisk/logger.h"
00063 #include "asterisk/options.h"
00064 #include "asterisk/cli.h"
00065 #include "asterisk/app.h"
00066 #include "asterisk/pbx.h"
00067 #include "asterisk/md5.h"
00068 #include "asterisk/acl.h"
00069 #include "asterisk/utils.h"
00070 #include "asterisk/http.h"
00071 #include "asterisk/threadstorage.h"
00072 #include "asterisk/linkedlists.h"
00073 #include "asterisk/term.h"
00074 #include "asterisk/astobj2.h"
00075 
00076 struct fast_originate_helper {
00077    char tech[AST_MAX_EXTENSION];
00078    /*! data can contain a channel name, extension number, username, password, etc. */
00079    char data[512];
00080    int timeout;
00081    int format;
00082    char app[AST_MAX_APP];
00083    char appdata[AST_MAX_EXTENSION];
00084    char cid_name[AST_MAX_EXTENSION];
00085    char cid_num[AST_MAX_EXTENSION];
00086    char context[AST_MAX_CONTEXT];
00087    char exten[AST_MAX_EXTENSION];
00088    char idtext[AST_MAX_EXTENSION];
00089    char account[AST_MAX_ACCOUNT_CODE];
00090    int priority;
00091    struct ast_variable *vars;
00092 };
00093 
00094 struct eventqent {
00095    int usecount;
00096    int category;
00097    struct eventqent *next;
00098    char eventdata[1];
00099 };
00100 
00101 static int manager_enabled = 0;
00102 static int webmanager_enabled = 0;
00103 
00104 static int portno = DEFAULT_MANAGER_PORT;
00105 static int asock = -1;
00106 static int displayconnects = 1;
00107 static int timestampevents;
00108 static int httptimeout = 60;
00109 
00110 static pthread_t t;
00111 static int block_sockets;
00112 static int num_sessions;
00113 
00114 /* Protected by the sessions list lock */
00115 struct eventqent *master_eventq = NULL;
00116 
00117 AST_THREADSTORAGE(manager_event_buf, manager_event_buf_init);
00118 #define MANAGER_EVENT_BUF_INITSIZE   256
00119 
00120 AST_THREADSTORAGE(astman_append_buf, astman_append_buf_init);
00121 #define ASTMAN_APPEND_BUF_INITSIZE   256
00122 
00123 static struct permalias {
00124    int num;
00125    char *label;
00126 } perms[] = {
00127    { EVENT_FLAG_SYSTEM, "system" },
00128    { EVENT_FLAG_CALL, "call" },
00129    { EVENT_FLAG_LOG, "log" },
00130    { EVENT_FLAG_VERBOSE, "verbose" },
00131    { EVENT_FLAG_COMMAND, "command" },
00132    { EVENT_FLAG_AGENT, "agent" },
00133    { EVENT_FLAG_USER, "user" },
00134    { EVENT_FLAG_CONFIG, "config" },
00135    { -1, "all" },
00136    { 0, "none" },
00137 };
00138 
00139 #define MAX_BLACKLIST_CMD_LEN 2
00140 static struct {
00141    char *words[AST_MAX_CMD_LEN];
00142 } command_blacklist[] = {
00143    {{ "module", "load", NULL }},
00144    {{ "module", "unload", NULL }},
00145    {{ "restart", "gracefully", NULL }},
00146 };
00147 
00148 /* In order to understand what the heck is going on with the
00149  * mansession_session and mansession structs, we need to have a bit of a history
00150  * lesson.
00151  *
00152  * In the beginning, there was the mansession. The mansession contained data that was
00153  * intrinsic to a manager session, such as the time that it started, the name of the logged-in
00154  * user, etc. In addition to these parameters were the f and fd parameters. For typical manager
00155  * sessions, these were used to represent the TCP socket over which the AMI session was taking
00156  * place. It makes perfect sense for these fields to be a part of the session-specific data since
00157  * the session actually defines this information.
00158  *
00159  * Then came the HTTP AMI sessions. With these, the f and fd fields need to be opened and closed
00160  * for every single action that occurs. Thus the f and fd fields aren't really specific to the session
00161  * but rather to the action that is being executed. Because a single session may execute many commands
00162  * at once, some sort of safety needed to be added in order to be sure that we did not end up with fd
00163  * leaks from one action overwriting the f and fd fields used by a previous action before the previous action
00164  * has had a chance to properly close its handles.
00165  *
00166  * The initial idea to solve this was to use thread synchronization, but this prevented multiple actions
00167  * from being run at the same time in a single session. Some manager actions may block for a long time, thus
00168  * creating a large queue of actions to execute. In addition, this fix did not address the basic architectural
00169  * issue that for HTTP manager sessions, the f and fd variables are not really a part of the session, but are
00170  * part of the action instead.
00171  *
00172  * The new idea was to create a structure on the stack for each HTTP Manager action. This structure would
00173  * contain the action-specific information, such as which file to write to. In order to maintain expectations
00174  * of action handlers and not have to change the public API of the manager code, we would need to name this
00175  * new stacked structure 'mansession' and contain within it the old mansession struct that we used to use.
00176  * We renamed the old mansession struct 'mansession_session' to hopefully convey that what is in this structure
00177  * is session-specific data. The structure that it is wrapped in, called a 'mansession' really contains action-specific
00178  * data.
00179  */
00180 struct mansession_session {
00181    /*! Execution thread */
00182    pthread_t t;
00183    /*! Thread lock -- don't use in action callbacks, it's already taken care of  */
00184    ast_mutex_t __lock;
00185    /*! socket address */
00186    struct sockaddr_in sin;
00187    /*! TCP socket */
00188    int fd;
00189    /*! Whether an HTTP manager is in use */
00190    int inuse;
00191    /*! Whether an HTTP session should be destroyed */
00192    int needdestroy;
00193    /*! Whether an HTTP session has someone waiting on events */
00194    pthread_t waiting_thread;
00195    /*! Unique manager identifer */
00196    uint32_t managerid;
00197    /*! Session timeout if HTTP */
00198    time_t sessiontimeout;
00199    /*! Output from manager interface */
00200    struct ast_dynamic_str *outputstr;
00201    /*! Logged in username */
00202    char username[80];
00203    /*! Authentication challenge */
00204    char challenge[10];
00205    /*! Authentication status */
00206    int authenticated;
00207    /*! Authorization for reading */
00208    int readperm;
00209    /*! Authorization for writing */
00210    int writeperm;
00211    /*! Buffer */
00212    char inbuf[1024];
00213    int inlen;
00214    int send_events;
00215    int displaysystemname;     /*!< Add system name to manager responses and events */
00216    /* Queued events that we've not had the ability to send yet */
00217    struct eventqent *eventq;
00218    /* Timeout for ast_carefulwrite() */
00219    int writetimeout;
00220    int pending_event;         /*!< Pending events indicator in case when waiting_thread is NULL */
00221    AST_LIST_ENTRY(mansession_session) list;
00222 };
00223 
00224 /* In case you didn't read that giant block of text above the mansession_session struct, the
00225  * 'mansession' struct is named this solely to keep the API the same in Asterisk. This structure really
00226  * represents data that is different from Manager action to Manager action. The mansession_session pointer
00227  * contained within points to session-specific data.
00228  */
00229 struct mansession {
00230    FILE *f;
00231    struct mansession_session *session;
00232    int fd;
00233 };
00234 
00235 static AST_LIST_HEAD_STATIC(sessions, mansession_session);
00236 
00237 struct ast_manager_user {
00238    char username[80];
00239    char *secret;
00240    char *deny;
00241    char *permit;
00242    char *read;
00243    char *write;
00244    unsigned int displayconnects:1;
00245    int keep;
00246    AST_LIST_ENTRY(ast_manager_user) list;
00247 };
00248 
00249 static AST_LIST_HEAD_STATIC(users, ast_manager_user);
00250 
00251 static struct manager_action *first_action;
00252 AST_RWLOCK_DEFINE_STATIC(actionlock);
00253 
00254 int check_manager_enabled()
00255 {
00256    return manager_enabled;
00257 }
00258 
00259 int check_webmanager_enabled()
00260 {
00261    return (webmanager_enabled && manager_enabled);
00262 }
00263 
00264 /*! \brief Convert authority code to string with serveral options */
00265 static char *authority_to_str(int authority, char *res, int reslen)
00266 {
00267    int running_total = 0, i;
00268 
00269    memset(res, 0, reslen);
00270    for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
00271       if (authority & perms[i].num) {
00272          if (*res) {
00273             strncat(res, ",", (reslen > running_total) ? reslen - running_total - 1 : 0);
00274             running_total++;
00275          }
00276          strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total - 1 : 0);
00277          running_total += strlen(perms[i].label);
00278       }
00279    }
00280 
00281    if (ast_strlen_zero(res))
00282       ast_copy_string(res, "<none>", reslen);
00283    
00284    return res;
00285 }
00286 
00287 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
00288 {
00289    struct manager_action *cur;
00290    int which = 0;
00291    char *ret = NULL;
00292 
00293    ast_rwlock_rdlock(&actionlock);
00294    for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
00295       if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
00296          ret = ast_strdup(cur->action);
00297          break;   /* make sure we exit even if ast_strdup() returns NULL */
00298       }
00299    }
00300    ast_rwlock_unlock(&actionlock);
00301 
00302    return ret;
00303 }
00304 
00305 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
00306 {
00307    while (*src && (*maxlen > 6)) {
00308       switch (*src) {
00309       case '<':
00310          strcpy(*dst, "&lt;");
00311          (*dst) += 4;
00312          *maxlen -= 4;
00313          break;
00314       case '>':
00315          strcpy(*dst, "&gt;");
00316          (*dst) += 4;
00317          *maxlen -= 4;
00318          break;
00319       case '\"':
00320          strcpy(*dst, "&quot;");
00321          (*dst) += 6;
00322          *maxlen -= 6;
00323          break;
00324       case '\'':
00325          strcpy(*dst, "&apos;");
00326          (*dst) += 6;
00327          *maxlen -= 6;
00328          break;
00329       case '&':
00330          strcpy(*dst, "&amp;");
00331          (*dst) += 5;
00332          *maxlen -= 5;
00333          break;      
00334       default:
00335          *(*dst)++ = lower ? tolower(*src) : *src;
00336          (*maxlen)--;
00337       }
00338       src++;
00339    }
00340 }
00341 
00342 struct variable_count {
00343    char *varname;
00344    int count;
00345 };
00346 
00347 static int compress_char(char c)
00348 {
00349    c &= 0x7f;
00350    if (c < 32)
00351       return 0;
00352    else if (c >= 'a' && c <= 'z')
00353       return c - 64;
00354    else if (c > 'z')
00355       return '_';
00356    else
00357       return c - 32;
00358 }
00359 
00360 static int variable_count_hash_fn(const void *vvc, const int flags)
00361 {
00362    const struct variable_count *vc = vvc;
00363    int res = 0, i;
00364    for (i = 0; i < 5; i++) {
00365       if (vc->varname[i] == '\0')
00366          break;
00367       res += compress_char(vc->varname[i]) << (i * 6);
00368    }
00369    return res;
00370 }
00371 
00372 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
00373 {
00374    /* Due to the simplicity of struct variable_count, it makes no difference
00375     * if you pass in objects or strings, the same operation applies. This is
00376     * due to the fact that the hash occurs on the first element, which means
00377     * the address of both the struct and the string are exactly the same. */
00378    struct variable_count *vc = obj;
00379    char *str = vstr;
00380    return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
00381 }
00382 
00383 static char *xml_translate(char *in, struct ast_variable *vars)
00384 {
00385    struct ast_variable *v;
00386    char *dest = NULL;
00387    char *out, *tmp, *var, *val;
00388    char *objtype = NULL;
00389    int colons = 0;
00390    int breaks = 0;
00391    size_t len;
00392    int count = 1;
00393    int escaped = 0;
00394    int inobj = 0;
00395    int x;
00396    struct variable_count *vc = NULL;
00397    struct ao2_container *vco = NULL;
00398 
00399    for (v = vars; v; v = v->next) {
00400       if (!dest && !strcasecmp(v->name, "ajaxdest"))
00401          dest = v->value;
00402       else if (!objtype && !strcasecmp(v->name, "ajaxobjtype")) 
00403          objtype = v->value;
00404    }
00405    if (!dest)
00406       dest = "unknown";
00407    if (!objtype)
00408       objtype = "generic";
00409    for (x = 0; in[x]; x++) {
00410       if (in[x] == ':')
00411          colons++;
00412       else if (in[x] == '\n')
00413          breaks++;
00414       else if (strchr("&\"<>\'", in[x]))
00415          escaped++;
00416    }
00417    len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
00418    out = ast_malloc(len);
00419    if (!out)
00420       return 0;
00421    tmp = out;
00422    while (*in) {
00423       var = in;
00424       while (*in && (*in >= 32))
00425          in++;
00426       if (*in) {
00427          if ((count > 3) && inobj) {
00428             ast_build_string(&tmp, &len, " /></response>\n");
00429             inobj = 0;
00430 
00431             /* Entity is closed, so close out the name cache */
00432             ao2_ref(vco, -1);
00433             vco = NULL;
00434          }
00435          count = 0;
00436          while (*in && (*in < 32)) {
00437             *in = '\0';
00438             in++;
00439             count++;
00440          }
00441          val = strchr(var, ':');
00442          if (val) {
00443             *val = '\0';
00444             val++;
00445             if (*val == ' ')
00446                val++;
00447             if (!inobj) {
00448                vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
00449                ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
00450                inobj = 1;
00451             }
00452 
00453             /* Check if the var has been used already */
00454             if ((vc = ao2_find(vco, var, 0)))
00455                vc->count++;
00456             else {
00457                /* Create a new entry for this one */
00458                vc = ao2_alloc(sizeof(*vc), NULL);
00459                vc->varname = var;
00460                vc->count = 1;
00461                ao2_link(vco, vc);
00462             }
00463 
00464             ast_build_string(&tmp, &len, " ");
00465             xml_copy_escape(&tmp, &len, var, 1);
00466             if (vc->count > 1)
00467                ast_build_string(&tmp, &len, "-%d", vc->count);
00468             ast_build_string(&tmp, &len, "='");
00469             xml_copy_escape(&tmp, &len, val, 0);
00470             ast_build_string(&tmp, &len, "'");
00471             ao2_ref(vc, -1);
00472          }
00473       }
00474    }
00475    if (inobj)
00476       ast_build_string(&tmp, &len, " /></response>\n");
00477    if (vco)
00478       ao2_ref(vco, -1);
00479    return out;
00480 }
00481 
00482 static char *html_translate(char *in)
00483 {
00484    int x;
00485    int colons = 0;
00486    int breaks = 0;
00487    size_t len;
00488    int count = 1;
00489    char *tmp, *var, *val, *out;
00490 
00491    for (x=0; in[x]; x++) {
00492       if (in[x] == ':')
00493          colons++;
00494       if (in[x] == '\n')
00495          breaks++;
00496    }
00497    len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
00498    out = ast_malloc(len);
00499    if (!out)
00500       return 0;
00501    tmp = out;
00502    while (*in) {
00503       var = in;
00504       while (*in && (*in >= 32))
00505          in++;
00506       if (*in) {
00507          if ((count % 4) == 0){
00508             ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00509          }
00510          count = 0;
00511          while (*in && (*in < 32)) {
00512             *in = '\0';
00513             in++;
00514             count++;
00515          }
00516          val = strchr(var, ':');
00517          if (val) {
00518             *val = '\0';
00519             val++;
00520             if (*val == ' ')
00521                val++;
00522             ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
00523          }
00524       }
00525    }
00526    return out;
00527 }
00528 
00529 
00530 
00531 static struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
00532 {
00533    struct ast_manager_user *user = NULL;
00534 
00535    AST_LIST_TRAVERSE(&users, user, list)
00536       if (!strcasecmp(user->username, name))
00537          break;
00538    return user;
00539 }
00540 
00541 void astman_append(struct mansession *s, const char *fmt, ...)
00542 {
00543    va_list ap;
00544    struct ast_dynamic_str *buf;
00545 
00546    ast_mutex_lock(&s->session->__lock);
00547 
00548    if (!(buf = ast_dynamic_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
00549       ast_mutex_unlock(&s->session->__lock);
00550       return;
00551    }
00552 
00553    va_start(ap, fmt);
00554    ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap);
00555    va_end(ap);
00556    
00557    if (s->fd > -1)
00558       ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->session->writetimeout);
00559    else {
00560       if (!s->session->outputstr && !(s->session->outputstr = ast_calloc(1, sizeof(*s->session->outputstr)))) {
00561          ast_mutex_unlock(&s->session->__lock);
00562          return;
00563       }
00564 
00565       ast_dynamic_str_append(&s->session->outputstr, 0, "%s", buf->str);   
00566    }
00567 
00568    ast_mutex_unlock(&s->session->__lock);
00569 }
00570 
00571 static int handle_showmancmd(int fd, int argc, char *argv[])
00572 {
00573    struct manager_action *cur;
00574    char authority[80];
00575    int num;
00576 
00577    if (argc != 4)
00578       return RESULT_SHOWUSAGE;
00579 
00580    ast_rwlock_rdlock(&actionlock);
00581    for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
00582       for (num = 3; num < argc; num++) {
00583          if (!strcasecmp(cur->action, argv[num])) {
00584             ast_cli(fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n", cur->action, cur->synopsis, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->description ? cur->description : "");
00585          }
00586       }
00587    }
00588    ast_rwlock_unlock(&actionlock);
00589 
00590    return RESULT_SUCCESS;
00591 }
00592 
00593 static int handle_showmanager(int fd, int argc, char *argv[])
00594 {
00595    struct ast_manager_user *user = NULL;
00596 
00597    if (argc != 4)
00598       return RESULT_SHOWUSAGE;
00599 
00600    AST_LIST_LOCK(&users);
00601 
00602    if (!(user = ast_get_manager_by_name_locked(argv[3]))) {
00603       ast_cli(fd, "There is no manager called %s\n", argv[3]);
00604       AST_LIST_UNLOCK(&users);
00605       return -1;
00606    }
00607 
00608    ast_cli(fd,"\n");
00609    ast_cli(fd,
00610       "       username: %s\n"
00611       "         secret: %s\n"
00612       "           deny: %s\n"
00613       "         permit: %s\n"
00614       "           read: %s\n"
00615       "          write: %s\n"
00616       "displayconnects: %s\n",
00617       (user->username ? user->username : "(N/A)"),
00618       (user->secret ? "<Set>" : "(N/A)"),
00619       (user->deny ? user->deny : "(N/A)"),
00620       (user->permit ? user->permit : "(N/A)"),
00621       (user->read ? user->read : "(N/A)"),
00622       (user->write ? user->write : "(N/A)"),
00623       (user->displayconnects ? "yes" : "no"));
00624 
00625    AST_LIST_UNLOCK(&users);
00626 
00627    return RESULT_SUCCESS;
00628 }
00629 
00630 
00631 static int handle_showmanagers(int fd, int argc, char *argv[])
00632 {
00633    struct ast_manager_user *user = NULL;
00634    int count_amu = 0;
00635 
00636    if (argc != 3)
00637       return RESULT_SHOWUSAGE;
00638 
00639    AST_LIST_LOCK(&users);
00640 
00641    /* If there are no users, print out something along those lines */
00642    if (AST_LIST_EMPTY(&users)) {
00643       ast_cli(fd, "There are no manager users.\n");
00644       AST_LIST_UNLOCK(&users);
00645       return RESULT_SUCCESS;
00646    }
00647 
00648    ast_cli(fd, "\nusername\n--------\n");
00649 
00650    AST_LIST_TRAVERSE(&users, user, list) {
00651       ast_cli(fd, "%s\n", user->username);
00652       count_amu++;
00653    }
00654 
00655    AST_LIST_UNLOCK(&users);
00656 
00657    ast_cli(fd,"-------------------\n");
00658    ast_cli(fd,"%d manager users configured.\n", count_amu);
00659 
00660    return RESULT_SUCCESS;
00661 }
00662 
00663 
00664 /*! \brief  CLI command 
00665    Should change to "manager show commands" */
00666 static int handle_showmancmds(int fd, int argc, char *argv[])
00667 {
00668    struct manager_action *cur;
00669    char authority[80];
00670    char *format = "  %-15.15s  %-15.15s  %-55.55s\n";
00671 
00672    ast_cli(fd, format, "Action", "Privilege", "Synopsis");
00673    ast_cli(fd, format, "------", "---------", "--------");
00674    
00675    ast_rwlock_rdlock(&actionlock);
00676    for (cur = first_action; cur; cur = cur->next) /* Walk the list of actions */
00677       ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
00678    ast_rwlock_unlock(&actionlock);
00679    
00680    return RESULT_SUCCESS;
00681 }
00682 
00683 /*! \brief CLI command show manager connected */
00684 /* Should change to "manager show connected" */
00685 static int handle_showmanconn(int fd, int argc, char *argv[])
00686 {
00687    struct mansession_session *s;
00688    char wtout[32];
00689    char *format = "  %-15.15s  %-15.15s  %-15.15s\n";
00690 
00691    ast_cli(fd, format, "Username", "IP Address", "Timeout");
00692    
00693    AST_LIST_LOCK(&sessions);
00694    AST_LIST_TRAVERSE(&sessions, s, list) {
00695       memset(wtout, 0, sizeof(wtout));
00696       snprintf(wtout, sizeof(wtout)-1, "%d", s->writetimeout);
00697       ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr), wtout);
00698    }
00699    AST_LIST_UNLOCK(&sessions);
00700 
00701    return RESULT_SUCCESS;
00702 }
00703 
00704 /*! \brief CLI command show manager eventq */
00705 /* Should change to "manager show eventq" */
00706 static int handle_showmaneventq(int fd, int argc, char *argv[])
00707 {
00708    struct eventqent *s;
00709 
00710    AST_LIST_LOCK(&sessions);
00711    for (s = master_eventq; s; s = s->next) {
00712       ast_cli(fd, "Usecount: %d\n",s->usecount);
00713       ast_cli(fd, "Category: %d\n", s->category);
00714       ast_cli(fd, "Event:\n%s", s->eventdata);
00715    }
00716    AST_LIST_UNLOCK(&sessions);
00717 
00718    return RESULT_SUCCESS;
00719 }
00720 
00721 static char showmancmd_help[] = 
00722 "Usage: manager show command <actionname>\n"
00723 "  Shows the detailed description for a specific Asterisk manager interface command.\n";
00724 
00725 static char showmancmds_help[] = 
00726 "Usage: manager show commands\n"
00727 "  Prints a listing of all the available Asterisk manager interface commands.\n";
00728 
00729 static char showmanconn_help[] = 
00730 "Usage: manager show connected\n"
00731 "  Prints a listing of the users that are currently connected to the\n"
00732 "Asterisk manager interface.\n";
00733 
00734 static char showmaneventq_help[] = 
00735 "Usage: manager show eventq\n"
00736 "  Prints a listing of all events pending in the Asterisk manger\n"
00737 "event queue.\n";
00738 
00739 static char showmanagers_help[] =
00740 "Usage: manager show users\n"
00741 "       Prints a listing of all managers that are currently configured on that\n"
00742 " system.\n";
00743 
00744 static char showmanager_help[] =
00745 " Usage: manager show user <user>\n"
00746 "        Display all information related to the manager user specified.\n";
00747 
00748 static struct ast_cli_entry cli_show_manager_command_deprecated = {
00749    { "show", "manager", "command", NULL },
00750    handle_showmancmd, NULL,
00751    NULL, complete_show_mancmd };
00752 
00753 static struct ast_cli_entry cli_show_manager_commands_deprecated = {
00754    { "show", "manager", "commands", NULL },
00755    handle_showmancmds, NULL,
00756    NULL };
00757 
00758 static struct ast_cli_entry cli_show_manager_connected_deprecated = {
00759    { "show", "manager", "connected", NULL },
00760    handle_showmanconn, NULL,
00761    NULL };
00762 
00763 static struct ast_cli_entry cli_show_manager_eventq_deprecated = {
00764    { "show", "manager", "eventq", NULL },
00765    handle_showmaneventq, NULL,
00766    NULL };
00767 
00768 static struct ast_cli_entry cli_manager[] = {
00769    { { "manager", "show", "command", NULL },
00770    handle_showmancmd, "Show a manager interface command",
00771    showmancmd_help, complete_show_mancmd, &cli_show_manager_command_deprecated },
00772 
00773    { { "manager", "show", "commands", NULL },
00774    handle_showmancmds, "List manager interface commands",
00775    showmancmds_help, NULL, &cli_show_manager_commands_deprecated },
00776 
00777    { { "manager", "show", "connected", NULL },
00778    handle_showmanconn, "List connected manager interface users",
00779    showmanconn_help, NULL, &cli_show_manager_connected_deprecated },
00780 
00781    { { "manager", "show", "eventq", NULL },
00782    handle_showmaneventq, "List manager interface queued events",
00783    showmaneventq_help, NULL, &cli_show_manager_eventq_deprecated },
00784 
00785    { { "manager", "show", "users", NULL },
00786    handle_showmanagers, "List configured manager users",
00787    showmanagers_help, NULL, NULL },
00788 
00789    { { "manager", "show", "user", NULL },
00790    handle_showmanager, "Display information on a specific manager user",
00791    showmanager_help, NULL, NULL },
00792 };
00793 
00794 static void unuse_eventqent(struct eventqent *e)
00795 {
00796    if (ast_atomic_dec_and_test(&e->usecount) && e->next)
00797       pthread_kill(t, SIGURG);
00798 }
00799 
00800 static void free_session(struct mansession_session *s)
00801 {
00802    struct eventqent *eqe;
00803    if (s->fd > -1)
00804       close(s->fd);
00805    if (s->outputstr)
00806       free(s->outputstr);
00807    ast_mutex_destroy(&s->__lock);
00808    while (s->eventq) {
00809       eqe = s->eventq;
00810       s->eventq = s->eventq->next;
00811       unuse_eventqent(eqe);
00812    }
00813    free(s);
00814 }
00815 
00816 static void destroy_session(struct mansession_session *s)
00817 {
00818    AST_LIST_LOCK(&sessions);
00819    AST_LIST_REMOVE(&sessions, s, list);
00820    num_sessions--;
00821    free_session(s);
00822    AST_LIST_UNLOCK(&sessions);
00823 }
00824 
00825 const char *astman_get_header(const struct message *m, char *var)
00826 {
00827    char cmp[80];
00828    int x;
00829 
00830    snprintf(cmp, sizeof(cmp), "%s: ", var);
00831 
00832    for (x = 0; x < m->hdrcount; x++) {
00833       if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
00834          return m->headers[x] + strlen(cmp);
00835    }
00836 
00837    return "";
00838 }
00839 
00840 struct ast_variable *astman_get_variables(const struct message *m)
00841 {
00842    int varlen, x, y;
00843    struct ast_variable *head = NULL, *cur;
00844    char *var, *val;
00845 
00846    char *parse;    
00847    AST_DECLARE_APP_ARGS(args,
00848       AST_APP_ARG(vars)[32];
00849    );
00850 
00851    varlen = strlen("Variable: ");   
00852 
00853    for (x = 0; x < m->hdrcount; x++) {
00854       if (strncasecmp("Variable: ", m->headers[x], varlen))
00855          continue;
00856 
00857       parse = ast_strdupa(m->headers[x] + varlen);
00858 
00859       AST_STANDARD_APP_ARGS(args, parse);
00860       if (args.argc) {
00861          for (y = 0; y < args.argc; y++) {
00862             if (!args.vars[y])
00863                continue;
00864             var = val = ast_strdupa(args.vars[y]);
00865             strsep(&val, "=");
00866             if (!val || ast_strlen_zero(var))
00867                continue;
00868             cur = ast_variable_new(var, val);
00869             if (head) {
00870                cur->next = head;
00871                head = cur;
00872             } else
00873                head = cur;
00874          }
00875       }
00876    }
00877 
00878    return head;
00879 }
00880 
00881 /*! \note NOTE:
00882    Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
00883    hold the session lock _or_ be running in an action callback (in which case s->busy will
00884    be non-zero). In either of these cases, there is no need to lock-protect the session's
00885    fd, since no other output will be sent (events will be queued), and no input will
00886    be read until either the current action finishes or get_input() obtains the session
00887    lock.
00888  */
00889 void astman_send_error(struct mansession *s, const struct message *m, char *error)
00890 {
00891    const char *id = astman_get_header(m,"ActionID");
00892 
00893    astman_append(s, "Response: Error\r\n");
00894    if (!ast_strlen_zero(id))
00895       astman_append(s, "ActionID: %s\r\n", id);
00896    astman_append(s, "Message: %s\r\n\r\n", error);
00897 }
00898 
00899 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
00900 {
00901    const char *id = astman_get_header(m,"ActionID");
00902 
00903    astman_append(s, "Response: %s\r\n", resp);
00904    if (!ast_strlen_zero(id))
00905       astman_append(s, "ActionID: %s\r\n", id);
00906    if (msg)
00907       astman_append(s, "Message: %s\r\n\r\n", msg);
00908    else
00909       astman_append(s, "\r\n");
00910 }
00911 
00912 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
00913 {
00914    astman_send_response(s, m, "Success", msg);
00915 }
00916 
00917 /*! Tells you if smallstr exists inside bigstr
00918    which is delim by delim and uses no buf or stringsep
00919    ast_instring("this|that|more","this",',') == 1;
00920 
00921    feel free to move this to app.c -anthm */
00922 static int ast_instring(const char *bigstr, const char *smallstr, char delim) 
00923 {
00924    const char *val = bigstr, *next;
00925 
00926    do {
00927       if ((next = strchr(val, delim))) {
00928          if (!strncmp(val, smallstr, (next - val)))
00929             return 1;
00930          else
00931             continue;
00932       } else
00933          return !strcmp(smallstr, val);
00934 
00935    } while (*(val = (next + 1)));
00936 
00937    return 0;
00938 }
00939 
00940 static int get_perm(const char *instr)
00941 {
00942    int x = 0, ret = 0;
00943 
00944    if (!instr)
00945       return 0;
00946 
00947    for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
00948       if (ast_instring(instr, perms[x].label, ','))
00949          ret |= perms[x].num;
00950    }
00951    
00952    return ret;
00953 }
00954 
00955 static int ast_is_number(const char *string) 
00956 {
00957    int ret = 1, x = 0;
00958 
00959    if (!string)
00960       return 0;
00961 
00962    for (x = 0; x < strlen(string); x++) {
00963       if (!(string[x] >= 48 && string[x] <= 57)) {
00964          ret = 0;
00965          break;
00966       }
00967    }
00968    
00969    return ret ? atoi(string) : 0;
00970 }
00971 
00972 static int strings_to_mask(const char *string) 
00973 {
00974    int x, ret = -1;
00975    
00976    x = ast_is_number(string);
00977 
00978    if (x)
00979       ret = x;
00980    else if (ast_strlen_zero(string))
00981       ret = -1;
00982    else if (ast_false(string))
00983       ret = 0;
00984    else if (ast_true(string)) {
00985       ret = 0;
00986       for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
00987          ret |= perms[x].num;    
00988    } else {
00989       ret = 0;
00990       for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
00991          if (ast_instring(string, perms[x].label, ',')) 
00992             ret |= perms[x].num;    
00993       }
00994    }
00995 
00996    return ret;
00997 }
00998 
00999 /*! \brief
01000    Rather than braindead on,off this now can also accept a specific int mask value 
01001    or a ',' delim list of mask strings (the same as manager.conf) -anthm
01002 */
01003 static int set_eventmask(struct mansession_session *s, const char *eventmask)
01004 {
01005    int maskint = strings_to_mask(eventmask);
01006 
01007    ast_mutex_lock(&s->__lock);
01008    if (maskint >= 0) 
01009       s->send_events = maskint;
01010    ast_mutex_unlock(&s->__lock);
01011    
01012    return maskint;
01013 }
01014 
01015 static int authenticate(struct mansession *s, const struct message *m)
01016 {
01017    struct ast_config *cfg;
01018    char *cat;
01019    const char *user = astman_get_header(m, "Username");
01020    const char *pass = astman_get_header(m, "Secret");
01021    const char *authtype = astman_get_header(m, "AuthType");
01022    const char *key = astman_get_header(m, "Key");
01023    const char *events = astman_get_header(m, "Events");
01024    
01025    cfg = ast_config_load("manager.conf");
01026    if (!cfg)
01027       return -1;
01028    cat = ast_category_browse(cfg, NULL);
01029    while (cat) {
01030       if (strcasecmp(cat, "general")) {
01031          /* This is a user */
01032          if (!strcasecmp(cat, user)) {
01033             struct ast_variable *v;
01034             struct ast_ha *ha = NULL;
01035             char *password = NULL;
01036 
01037             for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
01038                if (!strcasecmp(v->name, "secret")) {
01039                   password = v->value;
01040                } else if (!strcasecmp(v->name, "displaysystemname")) {
01041                   if (ast_true(v->value)) {
01042                      if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
01043                         s->session->displaysystemname = 1;
01044                      } else {
01045                         ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
01046                      }
01047                   }
01048                } else if (!strcasecmp(v->name, "permit") ||
01049                      !strcasecmp(v->name, "deny")) {
01050                   ha = ast_append_ha(v->name, v->value, ha);
01051                } else if (!strcasecmp(v->name, "writetimeout")) {
01052                   int val = atoi(v->value);
01053 
01054                   if (val < 100)
01055                      ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
01056                   else
01057                      s->session->writetimeout = val;
01058                }
01059                      
01060             }
01061             if (ha && !ast_apply_ha(ha, &(s->session->sin))) {
01062                ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), user);
01063                ast_free_ha(ha);
01064                ast_config_destroy(cfg);
01065                return -1;
01066             } else if (ha)
01067                ast_free_ha(ha);
01068             if (!strcasecmp(authtype, "MD5")) {
01069                if (!ast_strlen_zero(key) && 
01070                    !ast_strlen_zero(s->session->challenge) && !ast_strlen_zero(password)) {
01071                   int x;
01072                   int len = 0;
01073                   char md5key[256] = "";
01074                   struct MD5Context md5;
01075                   unsigned char digest[16];
01076                   MD5Init(&md5);
01077                   MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
01078                   MD5Update(&md5, (unsigned char *) password, strlen(password));
01079                   MD5Final(digest, &md5);
01080                   for (x=0; x<16; x++)
01081                      len += sprintf(md5key + len, "%2.2x", digest[x]);
01082                   if (!strcmp(md5key, key))
01083                      break;
01084                   else {
01085                      ast_config_destroy(cfg);
01086                      return -1;
01087                   }
01088                } else {
01089                   ast_log(LOG_DEBUG, "MD5 authentication is not possible.  challenge: '%s'\n", 
01090                      S_OR(s->session->challenge, ""));
01091                   ast_config_destroy(cfg);
01092                   return -1;
01093                }
01094             } else if (password && !strcmp(password, pass)) {
01095                break;
01096             } else {
01097                ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), user);
01098                ast_config_destroy(cfg);
01099                return -1;
01100             }  
01101          }
01102       }
01103       cat = ast_category_browse(cfg, cat);
01104    }
01105    if (cat) {
01106       ast_copy_string(s->session->username, cat, sizeof(s->session->username));
01107       s->session->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
01108       s->session->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
01109       ast_config_destroy(cfg);
01110       if (events)
01111          set_eventmask(s->session, events);
01112       return 0;
01113    }
01114    ast_config_destroy(cfg);
01115    cfg = ast_config_load("users.conf");
01116    if (!cfg)
01117       return -1;
01118    cat = ast_category_browse(cfg, NULL);
01119    while (cat) {
01120       struct ast_variable *v;
01121       const char *password = NULL;
01122       int hasmanager = 0;
01123       const char *readperms = NULL;
01124       const char *writeperms = NULL;
01125 
01126       if (strcasecmp(cat, user) || !strcasecmp(cat, "general")) {
01127          cat = ast_category_browse(cfg, cat);
01128          continue;
01129       }
01130       for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
01131          if (!strcasecmp(v->name, "secret"))
01132             password = v->value;
01133          else if (!strcasecmp(v->name, "hasmanager"))
01134             hasmanager = ast_true(v->value);
01135          else if (!strcasecmp(v->name, "managerread"))
01136             readperms = v->value;
01137          else if (!strcasecmp(v->name, "managerwrite"))
01138             writeperms = v->value;
01139       }
01140       if (!hasmanager)
01141          break;
01142       if (!password || strcmp(password, pass)) {
01143          ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), user);
01144          ast_config_destroy(cfg);
01145          return -1;
01146       }
01147       ast_copy_string(s->session->username, cat, sizeof(s->session->username));
01148       s->session->readperm = readperms ? get_perm(readperms) : -1;
01149       s->session->writeperm = writeperms ? get_perm(writeperms) : -1;
01150       ast_config_destroy(cfg);
01151       if (events)
01152          set_eventmask(s->session, events);
01153       return 0;
01154    }
01155    ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), user);
01156    ast_config_destroy(cfg);
01157    return -1;
01158 }
01159 
01160 /*! \brief Manager PING */
01161 static char mandescr_ping[] = 
01162 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the\n"
01163 "  manager connection open.\n"
01164 "Variables: NONE\n";
01165 
01166 static int action_ping(struct mansession *s, const struct message *m)
01167 {
01168    astman_send_response(s, m, "Pong", NULL);
01169    return 0;
01170 }
01171 
01172 static char mandescr_getconfig[] =
01173 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
01174 "file by category and contents.\n"
01175 "Variables:\n"
01176 "   Filename: Configuration filename (e.g. foo.conf)\n";
01177 
01178 static int action_getconfig(struct mansession *s, const struct message *m)
01179 {
01180    struct ast_config *cfg;
01181    const char *fn = astman_get_header(m, "Filename");
01182    int catcount = 0;
01183    int lineno = 0;
01184    char *category=NULL;
01185    struct ast_variable *v;
01186    char idText[256] = "";
01187    const char *id = astman_get_header(m, "ActionID");
01188 
01189    if (!ast_strlen_zero(id))
01190       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01191 
01192    if (ast_strlen_zero(fn)) {
01193       astman_send_error(s, m, "Filename not specified");
01194       return 0;
01195    }
01196    if (!(cfg = ast_config_load_with_comments(fn))) {
01197       astman_send_error(s, m, "Config file not found");
01198       return 0;
01199    }
01200    astman_append(s, "Response: Success\r\n%s", idText);
01201    while ((category = ast_category_browse(cfg, category))) {
01202       lineno = 0;
01203       astman_append(s, "Category-%06d: %s\r\n", catcount, category);
01204       for (v = ast_variable_browse(cfg, category); v; v = v->next)
01205          astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
01206       catcount++;
01207    }
01208    ast_config_destroy(cfg);
01209    astman_append(s, "\r\n");
01210 
01211    return 0;
01212 }
01213 
01214 
01215 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg)
01216 {
01217    int x;
01218    char hdr[40];
01219    const char *action, *cat, *var, *value, *match;
01220    struct ast_category *category;
01221    struct ast_variable *v;
01222    
01223    for (x=0;x<100000;x++) {
01224       unsigned int object = 0;
01225 
01226       snprintf(hdr, sizeof(hdr), "Action-%06d", x);
01227       action = astman_get_header(m, hdr);
01228       if (ast_strlen_zero(action))
01229          break;
01230       snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
01231       cat = astman_get_header(m, hdr);
01232       snprintf(hdr, sizeof(hdr), "Var-%06d", x);
01233       var = astman_get_header(m, hdr);
01234       snprintf(hdr, sizeof(hdr), "Value-%06d", x);
01235       value = astman_get_header(m, hdr);
01236       if (!ast_strlen_zero(value) && *value == '>') {
01237          object = 1;
01238          value++;
01239       }
01240       snprintf(hdr, sizeof(hdr), "Match-%06d", x);
01241       match = astman_get_header(m, hdr);
01242       if (!strcasecmp(action, "newcat")) {
01243          if (!ast_strlen_zero(cat)) {
01244             category = ast_category_new(cat);
01245             if (category) {
01246                ast_category_append(cfg, category);
01247             }
01248          }
01249       } else if (!strcasecmp(action, "renamecat")) {
01250          if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
01251             category = ast_category_get(cfg, cat);
01252             if (category) 
01253                ast_category_rename(category, value);
01254          }
01255       } else if (!strcasecmp(action, "delcat")) {
01256          if (!ast_strlen_zero(cat))
01257             ast_category_delete(cfg, (char *) cat);
01258       } else if (!strcasecmp(action, "update")) {
01259          if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
01260             ast_variable_update(category, var, value, match, object);
01261       } else if (!strcasecmp(action, "delete")) {
01262          if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
01263             ast_variable_delete(category, (char *) var, (char *) match);
01264       } else if (!strcasecmp(action, "append")) {
01265          if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && 
01266             (category = ast_category_get(cfg, cat)) && 
01267             (v = ast_variable_new(var, value))){
01268             if (object || (match && !strcasecmp(match, "object")))
01269                v->object = 1;
01270             ast_variable_append(category, v);
01271          }
01272       }
01273    }
01274 }
01275 
01276 static char mandescr_updateconfig[] =
01277 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
01278 "configuration elements in Asterisk configuration files.\n"
01279 "Variables (X's represent 6 digit number beginning with 000000):\n"
01280 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
01281 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
01282 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
01283 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
01284 "   Cat-XXXXXX:    Category to operate on\n"
01285 "   Var-XXXXXX:    Variable to work on\n"
01286 "   Value-XXXXXX:  Value to work on\n"
01287 "   Match-XXXXXX:  Extra match required to match line\n";
01288 
01289 static int action_updateconfig(struct mansession *s, const struct message *m)
01290 {
01291    struct ast_config *cfg;
01292    const char *sfn = astman_get_header(m, "SrcFilename");
01293    const char *dfn = astman_get_header(m, "DstFilename");
01294    int res;
01295    char idText[256] = "";
01296    const char *id = astman_get_header(m, "ActionID");
01297    const char *rld = astman_get_header(m, "Reload");
01298 
01299    if (!ast_strlen_zero(id))
01300       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01301 
01302    if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
01303       astman_send_error(s, m, "Filename not specified");
01304       return 0;
01305    }
01306    if (!(cfg = ast_config_load_with_comments(sfn))) {
01307       astman_send_error(s, m, "Config file not found");
01308       return 0;
01309    }
01310    handle_updates(s, m, cfg);
01311    res = config_text_file_save(dfn, cfg, "Manager");
01312    ast_config_destroy(cfg);
01313    if (res) {
01314       astman_send_error(s, m, "Save of config failed");
01315       return 0;
01316    }
01317    astman_append(s, "Response: Success\r\n%s\r\n", idText);
01318    if (!ast_strlen_zero(rld)) {
01319       if (ast_true(rld))
01320          rld = NULL;
01321       ast_module_reload(rld); 
01322    }
01323    return 0;
01324 }
01325 
01326 /*! \brief Manager WAITEVENT */
01327 static char mandescr_waitevent[] = 
01328 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
01329 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
01330 "session, events will be generated and queued.\n"
01331 "Variables: \n"
01332 "   Timeout: Maximum time to wait for events\n";
01333 
01334 static int action_waitevent(struct mansession *s, const struct message *m)
01335 {
01336    const char *timeouts = astman_get_header(m, "Timeout");
01337    int timeout = -1, max;
01338    int x;
01339    int needexit = 0;
01340    time_t now;
01341    struct eventqent *eqe;
01342    const char *id = astman_get_header(m,"ActionID");
01343    char idText[256] = "";
01344 
01345    if (!ast_strlen_zero(id))
01346       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01347 
01348    if (!ast_strlen_zero(timeouts)) {
01349       sscanf(timeouts, "%30i", &timeout);
01350    }
01351    
01352    ast_mutex_lock(&s->session->__lock);
01353    if (s->session->waiting_thread != AST_PTHREADT_NULL) {
01354       pthread_kill(s->session->waiting_thread, SIGURG);
01355    }
01356    if (s->session->sessiontimeout) {
01357       time(&now);
01358       max = s->session->sessiontimeout - now - 10;
01359       if (max < 0)
01360          max = 0;
01361       if ((timeout < 0) || (timeout > max))
01362          timeout = max;
01363       if (!s->session->send_events)
01364          s->session->send_events = -1;
01365       /* Once waitevent is called, always queue events from now on */
01366    }
01367    ast_mutex_unlock(&s->session->__lock);
01368    s->session->waiting_thread = pthread_self();
01369    if (option_debug)
01370       ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
01371    for (x=0; ((x < timeout) || (timeout < 0)); x++) {
01372       ast_mutex_lock(&s->session->__lock);
01373       if (s->session->eventq && s->session->eventq->next)
01374          needexit = 1;
01375       if (s->session->waiting_thread != pthread_self())
01376          needexit = 1;
01377       if (s->session->needdestroy)
01378          needexit = 1;
01379       ast_mutex_unlock(&s->session->__lock);
01380       if (needexit)
01381          break;
01382       if (s->session->fd > 0) {
01383          if (ast_wait_for_input(s->session->fd, 1000))
01384             break;
01385       } else {
01386          sleep(1);
01387       }
01388    }
01389    if (option_debug)
01390       ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
01391    ast_mutex_lock(&s->session->__lock);
01392    if (s->session->waiting_thread == pthread_self()) {
01393       astman_send_response(s, m, "Success", "Waiting for Event...");
01394       /* Only show events if we're the most recent waiter */
01395       while(s->session->eventq->next) {
01396          eqe = s->session->eventq->next;
01397          if (((s->session->readperm & eqe->category) == eqe->category) &&
01398              ((s->session->send_events & eqe->category) == eqe->category)) {
01399             astman_append(s, "%s", eqe->eventdata);
01400          }
01401          unuse_eventqent(s->session->eventq);
01402          s->session->eventq = eqe;
01403       }
01404       astman_append(s,
01405          "Event: WaitEventComplete\r\n"
01406          "%s"
01407          "\r\n", idText);
01408       s->session->waiting_thread = AST_PTHREADT_NULL;
01409    } else {
01410       ast_log(LOG_DEBUG, "Abandoning event request!\n");
01411    }
01412    ast_mutex_unlock(&s->session->__lock);
01413    return 0;
01414 }
01415 
01416 static char mandescr_listcommands[] = 
01417 "Description: Returns the action name and synopsis for every\n"
01418 "  action that is available to the user\n"
01419 "Variables: NONE\n";
01420 
01421 /*! \note The actionlock is read-locked by the caller of this function */
01422 static int action_listcommands(struct mansession *s, const struct message *m)
01423 {
01424    struct manager_action *cur;
01425    char idText[256] = "";
01426    char temp[BUFSIZ];
01427    const char *id = astman_get_header(m,"ActionID");
01428 
01429    if (!ast_strlen_zero(id))
01430       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01431    astman_append(s, "Response: Success\r\n%s", idText);
01432    for (cur = first_action; cur; cur = cur->next) {
01433       if ((s->session->writeperm & cur->authority) == cur->authority)
01434          astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
01435    }
01436    astman_append(s, "\r\n");
01437 
01438    return 0;
01439 }
01440 
01441 static char mandescr_events[] = 
01442 "Description: Enable/Disable sending of events to this manager\n"
01443 "  client.\n"
01444 "Variables:\n"
01445 "  EventMask: 'on' if all events should be sent,\n"
01446 "     'off' if no events should be sent,\n"
01447 "     'system,call,log' to select which flags events should have to be sent.\n";
01448 
01449 static int action_events(struct mansession *s, const struct message *m)
01450 {
01451    const char *mask = astman_get_header(m, "EventMask");
01452    int res;
01453 
01454    res = set_eventmask(s->session, mask);
01455    if (res > 0)
01456       astman_send_response(s, m, "Events On", NULL);
01457    else if (res == 0)
01458       astman_send_response(s, m, "Events Off", NULL);
01459 
01460    return 0;
01461 }
01462 
01463 static char mandescr_logoff[] = 
01464 "Description: Logoff this manager session\n"
01465 "Variables: NONE\n";
01466 
01467 static int action_logoff(struct mansession *s, const struct message *m)
01468 {
01469    astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
01470    return -1;
01471 }
01472 
01473 static char mandescr_hangup[] = 
01474 "Description: Hangup a channel\n"
01475 "Variables: \n"
01476 "  Channel: The channel name to be hungup\n";
01477 
01478 static int action_hangup(struct mansession *s, const struct message *m)
01479 {
01480    struct ast_channel *c = NULL;
01481    const char *name = astman_get_header(m, "Channel");
01482    if (ast_strlen_zero(name)) {
01483       astman_send_error(s, m, "No channel specified");
01484       return 0;
01485    }
01486    c = ast_get_channel_by_name_locked(name);
01487    if (!c) {
01488       astman_send_error(s, m, "No such channel");
01489       return 0;
01490    }
01491    ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
01492    ast_channel_unlock(c);
01493    astman_send_ack(s, m, "Channel Hungup");
01494    return 0;
01495 }
01496 
01497 static char mandescr_setvar[] = 
01498 "Description: Set a global or local channel variable.\n"
01499 "Variables: (Names marked with * are required)\n"
01500 "  Channel: Channel to set variable for\n"
01501 "  *Variable: Variable name\n"
01502 "  *Value: Value\n";
01503 
01504 static int action_setvar(struct mansession *s, const struct message *m)
01505 {
01506         struct ast_channel *c = NULL;
01507    const char *name = astman_get_header(m, "Channel");
01508    const char *varname = astman_get_header(m, "Variable");
01509    const char *varval = astman_get_header(m, "Value");
01510    
01511    if (ast_strlen_zero(varname)) {
01512       astman_send_error(s, m, "No variable specified");
01513       return 0;
01514    }
01515    
01516    if (!ast_strlen_zero(name)) {
01517       c = ast_get_channel_by_name_locked(name);
01518       if (!c) {
01519          astman_send_error(s, m, "No such channel");
01520          return 0;
01521       }
01522    }
01523    
01524    pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
01525      
01526    if (c)
01527       ast_channel_unlock(c);
01528 
01529    astman_send_ack(s, m, "Variable Set"); 
01530 
01531    return 0;
01532 }
01533 
01534 static char mandescr_getvar[] = 
01535 "Description: Get the value of a global or local channel variable.\n"
01536 "Variables: (Names marked with * are required)\n"
01537 "  Channel: Channel to read variable from\n"
01538 "  *Variable: Variable name\n"
01539 "  ActionID: Optional Action id for message matching.\n";
01540 
01541 static int action_getvar(struct mansession *s, const struct message *m)
01542 {
01543    struct ast_channel *c = NULL;
01544    const char *name = astman_get_header(m, "Channel");
01545    const char *varname = astman_get_header(m, "Variable");
01546    const char *id = astman_get_header(m,"ActionID");
01547    char *varval;
01548    char workspace[1024] = "";
01549 
01550    if (ast_strlen_zero(varname)) {
01551       astman_send_error(s, m, "No variable specified");
01552       return 0;
01553    }
01554 
01555    if (!ast_strlen_zero(name)) {
01556       c = ast_get_channel_by_name_locked(name);
01557       if (!c) {
01558          astman_send_error(s, m, "No such channel");
01559          return 0;
01560       }
01561    }
01562 
01563    if (varname[strlen(varname) - 1] == ')') {
01564       char *copy = ast_strdupa(varname);
01565       if (!c) {
01566          c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager");
01567          if (c) {
01568             ast_func_read(c, copy, workspace, sizeof(workspace));
01569             ast_channel_free(c);
01570             c = NULL;
01571          } else
01572             ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution.  Function results may be blank.\n");
01573       } else
01574          ast_func_read(c, copy, workspace, sizeof(workspace));
01575       varval = workspace;
01576    } else {
01577       pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
01578    }
01579 
01580    if (c)
01581       ast_channel_unlock(c);
01582    astman_append(s, "Response: Success\r\n"
01583       "Variable: %s\r\nValue: %s\r\n", varname, varval);
01584    if (!ast_strlen_zero(id))
01585       astman_append(s, "ActionID: %s\r\n",id);
01586    astman_append(s, "\r\n");
01587 
01588    return 0;
01589 }
01590 
01591 
01592 /*! \brief Manager "status" command to show channels */
01593 /* Needs documentation... */
01594 static int action_status(struct mansession *s, const struct message *m)
01595 {
01596    const char *id = astman_get_header(m,"ActionID");
01597    const char *name = astman_get_header(m,"Channel");
01598    char idText[256] = "";
01599    struct ast_channel *c;
01600    char bridge[256];
01601    struct timeval now = ast_tvnow();
01602    long elapsed_seconds = 0;
01603    int all = ast_strlen_zero(name); /* set if we want all channels */
01604 
01605    if (!ast_strlen_zero(id))
01606       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01607    if (all)
01608       c = ast_channel_walk_locked(NULL);
01609    else {
01610       c = ast_get_channel_by_name_locked(name);
01611       if (!c) {
01612          astman_send_error(s, m, "No such channel");
01613          return 0;
01614       }
01615    }
01616    astman_send_ack(s, m, "Channel status will follow");
01617    /* if we look by name, we break after the first iteration */
01618    while (c) {
01619       if (c->_bridge)
01620          snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
01621       else
01622          bridge[0] = '\0';
01623       if (c->pbx) {
01624          if (c->cdr) {
01625             elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01626          }
01627          astman_append(s,
01628          "Event: Status\r\n"
01629          "Privilege: Call\r\n"
01630          "Channel: %s\r\n"
01631          "CallerID: %s\r\n"      /* This parameter is deprecated and will be removed post-1.4 */
01632          "CallerIDNum: %s\r\n"
01633          "CallerIDName: %s\r\n"
01634          "Account: %s\r\n"
01635          "State: %s\r\n"
01636          "Context: %s\r\n"
01637          "Extension: %s\r\n"
01638          "Priority: %d\r\n"
01639          "Seconds: %ld\r\n"
01640          "%s"
01641          "Uniqueid: %s\r\n"
01642          "%s"
01643          "\r\n",
01644          c->name, 
01645          S_OR(c->cid.cid_num, "<unknown>"), 
01646          S_OR(c->cid.cid_num, "<unknown>"), 
01647          S_OR(c->cid.cid_name, "<unknown>"), 
01648          c->accountcode,
01649          ast_state2str(c->_state), c->context,
01650          c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
01651       } else {
01652          astman_append(s,
01653          "Event: Status\r\n"
01654          "Privilege: Call\r\n"
01655          "Channel: %s\r\n"
01656          "CallerID: %s\r\n"      /* This parameter is deprecated and will be removed post-1.4 */
01657          "CallerIDNum: %s\r\n"
01658          "CallerIDName: %s\r\n"
01659          "Account: %s\r\n"
01660          "State: %s\r\n"
01661          "%s"
01662          "Uniqueid: %s\r\n"
01663          "%s"
01664          "\r\n",
01665          c->name, 
01666          S_OR(c->cid.cid_num, "<unknown>"), 
01667          S_OR(c->cid.cid_num, "<unknown>"), 
01668          S_OR(c->cid.cid_name, "<unknown>"), 
01669          c->accountcode,
01670          ast_state2str(c->_state), bridge, c->uniqueid, idText);
01671       }
01672       ast_channel_unlock(c);
01673       if (!all)
01674          break;
01675       c = ast_channel_walk_locked(c);
01676    }
01677    astman_append(s,
01678    "Event: StatusComplete\r\n"
01679    "%s"
01680    "\r\n",idText);
01681    return 0;
01682 }
01683 
01684 static char mandescr_redirect[] = 
01685 "Description: Redirect (transfer) a call.\n"
01686 "Variables: (Names marked with * are required)\n"
01687 "  *Channel: Channel to redirect\n"
01688 "  ExtraChannel: Second call leg to transfer (optional)\n"
01689 "  *Exten: Extension to transfer to\n"
01690 "  *Context: Context to transfer to\n"
01691 "  *Priority: Priority to transfer to\n"
01692 "  ActionID: Optional Action id for message matching.\n";
01693 
01694 /*! \brief  action_redirect: The redirect manager command */
01695 static int action_redirect(struct mansession *s, const struct message *m)
01696 {
01697    const char *name = astman_get_header(m, "Channel");
01698    const char *name2 = astman_get_header(m, "ExtraChannel");
01699    const char *exten = astman_get_header(m, "Exten");
01700    const char *context = astman_get_header(m, "Context");
01701    const char *priority = astman_get_header(m, "Priority");
01702    struct ast_channel *chan, *chan2 = NULL;
01703    int pi = 0;
01704    int res;
01705 
01706    if (ast_strlen_zero(name)) {
01707       astman_send_error(s, m, "Channel not specified");
01708       return 0;
01709    }
01710    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
01711       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
01712          astman_send_error(s, m, "Invalid priority");
01713          return 0;
01714       }
01715    }
01716    /* XXX watch out, possible deadlock!!! */
01717    chan = ast_get_channel_by_name_locked(name);
01718    if (!chan) {
01719       char buf[BUFSIZ];
01720       snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
01721       astman_send_error(s, m, buf);
01722       return 0;
01723    }
01724    if (ast_check_hangup(chan)) {
01725       astman_send_error(s, m, "Redirect failed, channel not up.");
01726       ast_channel_unlock(chan);
01727       return 0;
01728    }
01729    if (!ast_strlen_zero(name2))
01730       chan2 = ast_get_channel_by_name_locked(name2);
01731    if (chan2 && ast_check_hangup(chan2)) {
01732       astman_send_error(s, m, "Redirect failed, extra channel not up.");
01733       ast_channel_unlock(chan);
01734       ast_channel_unlock(chan2);
01735       return 0;
01736    }
01737    if (chan->pbx) {
01738       ast_channel_lock(chan);
01739       ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
01740       ast_channel_unlock(chan);
01741    }
01742    res = ast_async_goto(chan, context, exten, pi);
01743    if (!res) {
01744       if (!ast_strlen_zero(name2)) {
01745          if (chan2) {
01746             if (chan2->pbx) {
01747                ast_channel_lock(chan2);
01748                ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
01749                ast_channel_unlock(chan2);
01750             }
01751             res = ast_async_goto(chan2, context, exten, pi);
01752          } else {
01753             res = -1;
01754          }
01755          if (!res)
01756             astman_send_ack(s, m, "Dual Redirect successful");
01757          else
01758             astman_send_error(s, m, "Secondary redirect failed");
01759       } else
01760          astman_send_ack(s, m, "Redirect successful");
01761    } else
01762       astman_send_error(s, m, "Redirect failed");
01763    if (chan)
01764       ast_channel_unlock(chan);
01765    if (chan2)
01766       ast_channel_unlock(chan2);
01767    return 0;
01768 }
01769 
01770 static int check_blacklist(const char *cmd)
01771 {
01772    char *cmd_copy, *cur_cmd;
01773    char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
01774    int i;
01775 
01776    cmd_copy = ast_strdupa(cmd);
01777    for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
01778       cur_cmd = ast_strip(cur_cmd);
01779       if (ast_strlen_zero(cur_cmd)) {
01780          i--;
01781          continue;
01782       }
01783 
01784       cmd_words[i] = cur_cmd;
01785    }
01786 
01787    for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
01788       int j, match = 1;
01789 
01790       for (j = 0; command_blacklist[i].words[j]; j++) {
01791          if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
01792             match = 0;
01793             break;
01794          }
01795       }
01796 
01797       if (match) {
01798          return 1;
01799       }
01800    }
01801 
01802    return 0;
01803 }
01804 
01805 static char mandescr_atxfer[] =
01806 "Description: do attended transfer.\n"
01807 "Variables: (Names marked with * are required)\n"
01808 "  *Channel: transferer Channel\n"
01809 "  *Exten: Extension to transfer to\n"
01810 "  Context: Context to transfer to\n"
01811 "  ActionID: Optional Action id for message matching.\n";
01812 
01813 static int action_atxfer(struct mansession *s, const struct message *m)
01814 {
01815    struct ast_channel *c;
01816    const char *name = astman_get_header(m, "Channel");
01817    const char *exten = astman_get_header(m, "Exten");
01818    const char *context = astman_get_header(m, "Context");
01819    char *xferto;
01820    int len;
01821 
01822    if (ast_strlen_zero(name)) {
01823       astman_send_error(s, m, "No channel specified");
01824       return 0;
01825    }
01826    if (ast_strlen_zero(exten)) {
01827       astman_send_error(s, m, "No exten specified");
01828       return 0;
01829    }
01830    c = ast_get_channel_by_name_locked(name);
01831    if (!c) {
01832       astman_send_error(s, m, "No such channel");
01833       return 0;
01834    }
01835    len = asprintf(&xferto, "%s@%s", exten, context);
01836    if (len < 0) {
01837       astman_send_error(s, m, "Out of memory!");
01838       goto cleanup;
01839    }
01840    ast_queue_control_data(c, AST_CONTROL_ATXFERCMD, xferto, len+1);
01841    free(xferto);
01842    astman_send_ack(s, m, "Attended transfer started");
01843 cleanup:
01844    ast_channel_unlock(c);
01845    return 0;
01846 }
01847 
01848 static char mandescr_command[] = 
01849 "Description: Run a CLI command.\n"
01850 "Variables: (Names marked with * are required)\n"
01851 "  *Command: Asterisk CLI command to run\n"
01852 "  ActionID: Optional Action id for message matching.\n";
01853 
01854 /*! \brief  action_command: Manager command "command" - execute CLI command */
01855 static int action_command(struct mansession *s, const struct message *m)
01856 {
01857    const char *cmd = astman_get_header(m, "Command");
01858    const char *id = astman_get_header(m, "ActionID");
01859    char *buf, *final_buf;
01860    char template[] = "/tmp/ast-ami-XXXXXX";  /* template for temporary file */
01861    int fd;
01862    off_t l;
01863 
01864    if (ast_strlen_zero(cmd)) {
01865       astman_send_error(s, m, "No command provided");
01866       return 0;
01867    }
01868 
01869    if (check_blacklist(cmd)) {
01870       astman_send_error(s, m, "Command blacklisted");
01871       return 0;
01872    }
01873 
01874    fd = mkstemp(template);
01875 
01876    astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
01877    if (!ast_strlen_zero(id))
01878       astman_append(s, "ActionID: %s\r\n", id);
01879    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
01880    ast_cli_command(fd, cmd);  /* XXX need to change this to use a FILE * */
01881    l = lseek(fd, 0, SEEK_END);   /* how many chars available */
01882 
01883    /* This has a potential to overflow the stack.  Hence, use the heap. */
01884    buf = ast_calloc(1, l + 1);
01885    final_buf = ast_calloc(1, l + 1);
01886    if (buf) {
01887       lseek(fd, 0, SEEK_SET);
01888       if (read(fd, buf, l) < 0) {
01889          ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01890       }
01891       buf[l] = '\0';
01892       if (final_buf) {
01893          term_strip(final_buf, buf, l);
01894          final_buf[l] = '\0';
01895       }
01896       astman_append(s, "%s", S_OR(final_buf, buf));
01897       ast_free(buf);
01898    }
01899    close(fd);
01900    unlink(template);
01901    astman_append(s, "--END COMMAND--\r\n\r\n");
01902    if (final_buf)
01903       ast_free(final_buf);
01904    return 0;
01905 }
01906 
01907 static void *fast_originate(void *data)
01908 {
01909    struct fast_originate_helper *in = data;
01910    int res;
01911    int reason = 0;
01912    struct ast_channel *chan = NULL;
01913    char requested_channel[AST_CHANNEL_NAME];
01914 
01915    if (!ast_strlen_zero(in->app)) {
01916       res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
01917          S_OR(in->cid_num, NULL), 
01918          S_OR(in->cid_name, NULL),
01919          in->vars, in->account, &chan);
01920    } else {
01921       res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
01922          S_OR(in->cid_num, NULL), 
01923          S_OR(in->cid_name, NULL),
01924          in->vars, in->account, &chan);
01925    }
01926 
01927    if (!chan)
01928       snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);   
01929    /* Tell the manager what happened with the channel */
01930    manager_event(EVENT_FLAG_CALL, "OriginateResponse",
01931       "%s%s"
01932       "Response: %s\r\n"
01933       "Channel: %s\r\n"
01934       "Context: %s\r\n"
01935       "Exten: %s\r\n"
01936       "Reason: %d\r\n"
01937       "Uniqueid: %s\r\n"
01938       "CallerID: %s\r\n"      /* This parameter is deprecated and will be removed post-1.4 */
01939       "CallerIDNum: %s\r\n"
01940       "CallerIDName: %s\r\n",
01941       in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success", 
01942       chan ? chan->name : requested_channel, in->context, in->exten, reason, 
01943       chan ? chan->uniqueid : "<null>",
01944       S_OR(in->cid_num, "<unknown>"),
01945       S_OR(in->cid_num, "<unknown>"),
01946       S_OR(in->cid_name, "<unknown>")
01947       );
01948 
01949    /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
01950    if (chan)
01951       ast_channel_unlock(chan);
01952    free(in);
01953    return NULL;
01954 }
01955 
01956 static char mandescr_originate[] = 
01957 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
01958 "  Application/Data\n"
01959 "Variables: (Names marked with * are required)\n"
01960 "  *Channel: Channel name to call\n"
01961 "  Exten: Extension to use (requires 'Context' and 'Priority')\n"
01962 "  Context: Context to use (requires 'Exten' and 'Priority')\n"
01963 "  Priority: Priority to use (requires 'Exten' and 'Context')\n"
01964 "  Application: Application to use\n"
01965 "  Data: Data to use (requires 'Application')\n"
01966 "  Timeout: How long to wait for call to be answered (in ms. Default: 30000)\n"
01967 "  CallerID: Caller ID to be set on the outgoing channel\n"
01968 "  Variable: Channel variable to set, multiple Variable: headers are allowed\n"
01969 "  Account: Account code\n"
01970 "  Async: Set to 'true' for fast origination\n";
01971 
01972 static int action_originate(struct mansession *s, const struct message *m)
01973 {
01974    const char *name = astman_get_header(m, "Channel");
01975    const char *exten = astman_get_header(m, "Exten");
01976    const char *context = astman_get_header(m, "Context");
01977    const char *priority = astman_get_header(m, "Priority");
01978    const char *timeout = astman_get_header(m, "Timeout");
01979    const char *callerid = astman_get_header(m, "CallerID");
01980    const char *account = astman_get_header(m, "Account");
01981    const char *app = astman_get_header(m, "Application");
01982    const char *appdata = astman_get_header(m, "Data");
01983    const char *async = astman_get_header(m, "Async");
01984    const char *id = astman_get_header(m, "ActionID");
01985    const char *codecs = astman_get_header(m, "Codecs");
01986    struct ast_variable *vars = astman_get_variables(m);
01987    char *tech, *data;
01988    char *l = NULL, *n = NULL;
01989    int pi = 0;
01990    int res;
01991    int to = 30000;
01992    int reason = 0;
01993    char tmp[256];
01994    char tmp2[256];
01995    int format = AST_FORMAT_SLINEAR;
01996    
01997    pthread_t th;
01998    pthread_attr_t attr;
01999    if (ast_strlen_zero(name)) {
02000       astman_send_error(s, m, "Channel not specified");
02001       return 0;
02002    }
02003    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
02004       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
02005          astman_send_error(s, m, "Invalid priority");
02006          return 0;
02007       }
02008    }
02009    if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) {
02010       astman_send_error(s, m, "Invalid timeout");
02011       return 0;
02012    }
02013    ast_copy_string(tmp, name, sizeof(tmp));
02014    tech = tmp;
02015    data = strchr(tmp, '/');
02016    if (!data) {
02017       astman_send_error(s, m, "Invalid channel");
02018       return 0;
02019    }
02020    *data++ = '\0';
02021    ast_copy_string(tmp2, callerid, sizeof(tmp2));
02022    ast_callerid_parse(tmp2, &n, &l);
02023    if (n) {
02024       if (ast_strlen_zero(n))
02025          n = NULL;
02026    }
02027    if (l) {
02028       ast_shrink_phone_number(l);
02029       if (ast_strlen_zero(l))
02030          l = NULL;
02031    }
02032    if (!ast_strlen_zero(codecs)) {
02033       format = 0;
02034       ast_parse_allow_disallow(NULL, &format, codecs, 1);
02035    }
02036    if (ast_true(async)) {
02037       struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
02038       if (!fast) {
02039          res = -1;
02040       } else {
02041          if (!ast_strlen_zero(id))
02042             snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s", id);
02043          ast_copy_string(fast->tech, tech, sizeof(fast->tech));
02044             ast_copy_string(fast->data, data, sizeof(fast->data));
02045          ast_copy_string(fast->app, app, sizeof(fast->app));
02046          ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
02047          if (l)
02048             ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
02049          if (n)
02050             ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
02051          fast->vars = vars;   
02052          ast_copy_string(fast->context, context, sizeof(fast->context));
02053          ast_copy_string(fast->exten, exten, sizeof(fast->exten));
02054          ast_copy_string(fast->account, account, sizeof(fast->account));
02055          fast->format = format;
02056          fast->timeout = to;
02057          fast->priority = pi;
02058          pthread_attr_init(&attr);
02059          pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02060          if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
02061             ast_free(fast);
02062             res = -1;
02063          } else {
02064             res = 0;
02065          }
02066          pthread_attr_destroy(&attr);
02067       }
02068    } else if (!ast_strlen_zero(app)) {
02069          res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
02070       } else {
02071       if (exten && context && pi)
02072             res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
02073       else {
02074          astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
02075          return 0;
02076       }
02077    }   
02078    if (!res)
02079       astman_send_ack(s, m, "Originate successfully queued");
02080    else
02081       astman_send_error(s, m, "Originate failed");
02082    return 0;
02083 }
02084 
02085 /*! \brief Help text for manager command mailboxstatus
02086  */
02087 static char mandescr_mailboxstatus[] = 
02088 "Description: Checks a voicemail account for status.\n"
02089 "Variables: (Names marked with * are required)\n"
02090 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02091 "  ActionID: Optional ActionID for message matching.\n"
02092 "Returns number of messages.\n"
02093 "  Message: Mailbox Status\n"
02094 "  Mailbox: <mailboxid>\n"
02095 "  Waiting: <count>\n"
02096 "\n";
02097 
02098 static int action_mailboxstatus(struct mansession *s, const struct message *m)
02099 {
02100    const char *mailbox = astman_get_header(m, "Mailbox");
02101    const char *id = astman_get_header(m,"ActionID");
02102    char idText[256] = "";
02103    int ret;
02104    if (ast_strlen_zero(mailbox)) {
02105       astman_send_error(s, m, "Mailbox not specified");
02106       return 0;
02107    }
02108         if (!ast_strlen_zero(id))
02109                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02110    ret = ast_app_has_voicemail(mailbox, NULL);
02111    astman_append(s, "Response: Success\r\n"
02112                "%s"
02113                "Message: Mailbox Status\r\n"
02114                "Mailbox: %s\r\n"
02115                "Waiting: %d\r\n\r\n", idText, mailbox, ret);
02116    return 0;
02117 }
02118 
02119 static char mandescr_mailboxcount[] = 
02120 "Description: Checks a voicemail account for new messages.\n"
02121 "Variables: (Names marked with * are required)\n"
02122 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02123 "  ActionID: Optional ActionID for message matching.\n"
02124 "Returns number of new and old messages.\n"
02125 "  Message: Mailbox Message Count\n"
02126 "  Mailbox: <mailboxid>\n"
02127 "  NewMessages: <count>\n"
02128 "  OldMessages: <count>\n"
02129 "\n";
02130 static int action_mailboxcount(struct mansession *s, const struct message *m)
02131 {
02132    const char *mailbox = astman_get_header(m, "Mailbox");
02133    const char *id = astman_get_header(m,"ActionID");
02134    char idText[256] = "";
02135    int newmsgs = 0, oldmsgs = 0;
02136    if (ast_strlen_zero(mailbox)) {
02137       astman_send_error(s, m, "Mailbox not specified");
02138       return 0;
02139    }
02140    ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
02141    if (!ast_strlen_zero(id)) {
02142       snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
02143    }
02144    astman_append(s, "Response: Success\r\n"
02145                "%s"
02146                "Message: Mailbox Message Count\r\n"
02147                "Mailbox: %s\r\n"
02148                "NewMessages: %d\r\n"
02149                "OldMessages: %d\r\n" 
02150                "\r\n",
02151                 idText,mailbox, newmsgs, oldmsgs);
02152    return 0;
02153 }
02154 
02155 static char mandescr_extensionstate[] = 
02156 "Description: Report the extension state for given extension.\n"
02157 "  If the extension has a hint, will use devicestate to check\n"
02158 "  the status of the device connected to the extension.\n"
02159 "Variables: (Names marked with * are required)\n"
02160 "  *Exten: Extension to check state on\n"
02161 "  *Context: Context for extension\n"
02162 "  ActionId: Optional ID for this transaction\n"
02163 "Will return an \"Extension Status\" message.\n"
02164 "The response will include the hint for the extension and the status.\n";
02165 
02166 static int action_extensionstate(struct mansession *s, const struct message *m)
02167 {
02168    const char *exten = astman_get_header(m, "Exten");
02169    const char *context = astman_get_header(m, "Context");
02170    const char *id = astman_get_header(m,"ActionID");
02171    char idText[256] = "";
02172    char hint[256] = "";
02173    int status;
02174    if (ast_strlen_zero(exten)) {
02175       astman_send_error(s, m, "Extension not specified");
02176       return 0;
02177    }
02178    if (ast_strlen_zero(context))
02179       context = "default";
02180    status = ast_extension_state(NULL, context, exten);
02181    ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
02182         if (!ast_strlen_zero(id)) {
02183                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02184         }
02185    astman_append(s, "Response: Success\r\n"
02186                     "%s"
02187                "Message: Extension Status\r\n"
02188                "Exten: %s\r\n"
02189                "Context: %s\r\n"
02190                "Hint: %s\r\n"
02191                "Status: %d\r\n\r\n",
02192                idText,exten, context, hint, status);
02193    return 0;
02194 }
02195 
02196 static char mandescr_timeout[] = 
02197 "Description: Hangup a channel after a certain time.\n"
02198 "Variables: (Names marked with * are required)\n"
02199 "  *Channel: Channel name to hangup\n"
02200 "  *Timeout: Maximum duration of the call (sec)\n"
02201 "Acknowledges set time with 'Timeout Set' message\n";
02202 
02203 static int action_timeout(struct mansession *s, const struct message *m)
02204 {
02205    struct ast_channel *c = NULL;
02206    const char *name = astman_get_header(m, "Channel");
02207    int timeout = atoi(astman_get_header(m, "Timeout"));
02208    if (ast_strlen_zero(name)) {
02209       astman_send_error(s, m, "No channel specified");
02210       return 0;
02211    }
02212    if (!timeout) {
02213       astman_send_error(s, m, "No timeout specified");
02214       return 0;
02215    }
02216    c = ast_get_channel_by_name_locked(name);
02217    if (!c) {
02218       astman_send_error(s, m, "No such channel");
02219       return 0;
02220    }
02221    ast_channel_setwhentohangup(c, timeout);
02222    ast_channel_unlock(c);
02223    astman_send_ack(s, m, "Timeout Set");
02224    return 0;
02225 }
02226 
02227 static int process_events(struct mansession *s)
02228 {
02229    struct eventqent *eqe;
02230    int ret = 0;
02231    ast_mutex_lock(&s->session->__lock);
02232    if (!s->session->eventq)
02233       s->session->eventq = master_eventq;
02234    while(s->session->eventq->next) {
02235       eqe = s->session->eventq->next;
02236       if ((s->session->authenticated && (s->session->readperm & eqe->category) == eqe->category) &&
02237                ((s->session->send_events & eqe->category) == eqe->category)) {
02238          if (s->fd > -1) {
02239             if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->session->writetimeout) < 0)
02240                ret = -1;
02241          } else if (!s->session->outputstr && !(s->session->outputstr = ast_calloc(1, sizeof(*s->session->outputstr)))) 
02242             ret = -1;
02243          else 
02244             ast_dynamic_str_append(&s->session->outputstr, 0, "%s", eqe->eventdata);
02245       }
02246       unuse_eventqent(s->session->eventq);
02247       s->session->eventq = eqe;
02248    }
02249    ast_mutex_unlock(&s->session->__lock);
02250    return ret;
02251 }
02252 
02253 static char mandescr_userevent[] =
02254 "Description: Send an event to manager sessions.\n"
02255 "Variables: (Names marked with * are required)\n"
02256 "       *UserEvent: EventStringToSend\n"
02257 "       Header1: Content1\n"
02258 "       HeaderN: ContentN\n";
02259 
02260 static int action_userevent(struct mansession *s, const struct message *m)
02261 {
02262    const char *event = astman_get_header(m, "UserEvent");
02263    char body[2048] = "";
02264    int x, bodylen = 0, xlen;
02265    for (x = 0; x < m->hdrcount; x++) {
02266       if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
02267          if (sizeof(body) < bodylen + (xlen = strlen(m->headers[x])) + 3) {
02268             ast_log(LOG_WARNING, "UserEvent exceeds our buffer length.  Truncating.\n");
02269             break;
02270          }
02271          ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
02272          bodylen += xlen;
02273          ast_copy_string(body + bodylen, "\r\n", 3);
02274          bodylen += 2;
02275       }
02276    }
02277 
02278    astman_send_ack(s, m, "Event Sent");   
02279    manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
02280    return 0;
02281 }
02282 
02283 static char mandescr_coresettings[] =
02284 "Description: Query for Core PBX settings.\n"
02285 "Variables: (Names marked with * are optional)\n"
02286 "       *ActionID: ActionID of this transaction\n";
02287 
02288 /*! \brief Show PBX core settings information */
02289 static int action_coresettings(struct mansession *s, const struct message *m)
02290 {
02291    const char *actionid = astman_get_header(m, "ActionID");
02292    char idText[150] = "";
02293 
02294    if (!ast_strlen_zero(actionid))
02295       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02296 
02297    astman_append(s, "Response: Success\r\n"
02298          "%s"
02299          "AMIversion: %s\r\n"
02300          "AsteriskVersion: %s\r\n"
02301          "SystemName: %s\r\n"
02302          "CoreMaxCalls: %d\r\n"
02303          "CoreMaxLoadAvg: %f\r\n"
02304          "CoreRunUser: %s\r\n"
02305          "CoreRunGroup: %s\r\n"
02306          "CoreMaxFilehandles: %d\r\n" 
02307          "CoreRealTimeEnabled: %s\r\n"
02308          "CoreCDRenabled: %s\r\n"
02309          "CoreHTTPenabled: %s\r\n"
02310          "\r\n",
02311          idText,
02312          AMI_VERSION,
02313          ASTERISK_VERSION, 
02314          ast_config_AST_SYSTEM_NAME,
02315          option_maxcalls,
02316          option_maxload,
02317          ast_config_AST_RUN_USER,
02318          ast_config_AST_RUN_GROUP,
02319          option_maxfiles,
02320          ast_realtime_enabled() ? "Yes" : "No",
02321          check_cdr_enabled() ? "Yes" : "No",
02322          check_webmanager_enabled() ? "Yes" : "No"
02323          );
02324    return 0;
02325 }
02326 
02327 static char mandescr_corestatus[] =
02328 "Description: Query for Core PBX status.\n"
02329 "Variables: (Names marked with * are optional)\n"
02330 "       *ActionID: ActionID of this transaction\n";
02331 
02332 /*! \brief Show PBX core status information */
02333 static int action_corestatus(struct mansession *s, const struct message *m)
02334 {
02335    const char *actionid = astman_get_header(m, "ActionID");
02336    char idText[150];
02337    char startuptime[150];
02338    char reloadtime[150];
02339    struct tm tm;
02340 
02341    if (!ast_strlen_zero(actionid))
02342       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02343 
02344    ast_localtime(&ast_startuptime, &tm, NULL);
02345    strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
02346    ast_localtime(&ast_lastreloadtime, &tm, NULL);
02347    strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
02348 
02349    astman_append(s, "Response: Success\r\n"
02350          "%s"
02351          "CoreStartupTime: %s\r\n"
02352          "CoreReloadTime: %s\r\n"
02353          "CoreCurrentCalls: %d\r\n"
02354          "\r\n",
02355          idText,
02356          startuptime,
02357          reloadtime,
02358          ast_active_channels()
02359          );
02360    return 0;
02361 }
02362 
02363 static int process_message(struct mansession *s, const struct message *m)
02364 {
02365    char action[80] = "";
02366    struct manager_action *tmp;
02367    const char *id = astman_get_header(m,"ActionID");
02368    char idText[256] = "";
02369    int ret = 0;
02370 
02371    ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
02372    if (option_debug)
02373       ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
02374 
02375    if (ast_strlen_zero(action)) {
02376       astman_send_error(s, m, "Missing action in request");
02377       return 0;
02378    }
02379    if (!ast_strlen_zero(id)) {
02380       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02381    }
02382    if (!s->session->authenticated) {
02383       if (!strcasecmp(action, "Challenge")) {
02384          const char *authtype = astman_get_header(m, "AuthType");
02385 
02386          if (!strcasecmp(authtype, "MD5")) {
02387             if (ast_strlen_zero(s->session->challenge))
02388                snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
02389             astman_append(s, "Response: Success\r\n"
02390                   "%s"
02391                   "Challenge: %s\r\n\r\n",
02392                   idText, s->session->challenge);
02393             return 0;
02394          } else {
02395             astman_send_error(s, m, "Must specify AuthType");
02396             return 0;
02397          }
02398       } else if (!strcasecmp(action, "Login")) {
02399          if (authenticate(s, m)) {
02400             sleep(1);
02401             astman_send_error(s, m, "Authentication failed");
02402             return -1;
02403          } else {
02404             s->session->authenticated = 1;
02405             if (option_verbose > 1) {
02406                if (displayconnects) {
02407                   ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", 
02408                      (s->session->sessiontimeout ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
02409                }
02410             }
02411             ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", 
02412                (s->session->sessiontimeout ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
02413             astman_send_ack(s, m, "Authentication accepted");
02414          }
02415       } else if (!strcasecmp(action, "Logoff")) {
02416          astman_send_ack(s, m, "See ya");
02417          return -1;
02418       } else
02419          astman_send_error(s, m, "Authentication Required");
02420    } else {
02421       if (!strcasecmp(action, "Login"))
02422          astman_send_ack(s, m, "Already logged in");
02423       else {
02424          ast_rwlock_rdlock(&actionlock);
02425          for (tmp = first_action; tmp; tmp = tmp->next) {      
02426             if (strcasecmp(action, tmp->action))
02427                continue;
02428             if ((s->session->writeperm & tmp->authority) == tmp->authority) {
02429                if (tmp->func(s, m))
02430                   ret = -1;
02431             } else
02432                astman_send_error(s, m, "Permission denied");
02433             break;
02434          }
02435          ast_rwlock_unlock(&actionlock);
02436          if (!tmp)
02437             astman_send_error(s, m, "Invalid/unknown command");
02438       }
02439    }
02440    if (ret)
02441       return ret;
02442    return process_events(s);
02443 }
02444 
02445 static int get_input(struct mansession_session *s, char *output)
02446 {
02447    /* output must have at least sizeof(s->inbuf) space */
02448    int res;
02449    int x;
02450    struct pollfd fds[1];
02451    for (x = 1; x < s->inlen; x++) {
02452       if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
02453          /* Copy output data up to and including \r\n */
02454          memcpy(output, s->inbuf, x + 1);
02455          /* Add trailing \0 */
02456          output[x+1] = '\0';
02457          /* Move remaining data back to the front */
02458          memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
02459          s->inlen -= (x + 1);
02460          return 1;
02461       }
02462    } 
02463    if (s->inlen >= sizeof(s->inbuf) - 1) {
02464       ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
02465       s->inlen = 0;
02466    }
02467    fds[0].fd = s->fd;
02468    fds[0].events = POLLIN;
02469    do {
02470       ast_mutex_lock(&s->__lock);
02471       if (s->pending_event) {
02472          s->pending_event = 0;
02473          ast_mutex_unlock(&s->__lock);
02474          return 0;
02475       }
02476       s->waiting_thread = pthread_self();
02477       ast_mutex_unlock(&s->__lock);
02478 
02479       res = ast_poll(fds, 1, -1);
02480 
02481       ast_mutex_lock(&s->__lock);
02482       s->waiting_thread = AST_PTHREADT_NULL;
02483       ast_mutex_unlock(&s->__lock);
02484       if (res < 0) {
02485          if (errno == EINTR || errno == EAGAIN) {
02486             return 0;
02487          }
02488          ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
02489          return -1;
02490       } else if (res > 0) {
02491          ast_mutex_lock(&s->__lock);
02492          res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
02493          ast_mutex_unlock(&s->__lock);
02494          if (res < 1)
02495             return -1;
02496          break;
02497       }
02498    } while(1);
02499    s->inlen += res;
02500    s->inbuf[s->inlen] = '\0';
02501    return 0;
02502 }
02503 
02504 static int do_message(struct mansession *s)
02505 {
02506    struct message m = { 0 };
02507    char header_buf[sizeof(s->session->inbuf)] = { '\0' };
02508    int res;
02509 
02510    for (;;) {
02511       /* Check if any events are pending and do them if needed */
02512       if (s->session->eventq->next) {
02513          if (process_events(s))
02514             return -1;
02515       }
02516       res = get_input(s->session, header_buf);
02517       if (res == 0) {
02518          continue;
02519       } else if (res > 0) {
02520          /* Strip trailing \r\n */
02521          if (strlen(header_buf) < 2)
02522             continue;
02523          header_buf[strlen(header_buf) - 2] = '\0';
02524          if (ast_strlen_zero(header_buf))
02525             return process_message(s, &m) ? -1 : 0;
02526          else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
02527             m.headers[m.hdrcount++] = ast_strdupa(header_buf);
02528       } else {
02529          return res;
02530       }
02531    }
02532 }
02533 
02534 static void *session_do(void *data)
02535 {
02536    struct mansession_session *session = data;
02537    int res;
02538    struct mansession s = { .session = session, .fd = session->fd };
02539 
02540    astman_append(&s, "Asterisk Call Manager/1.0\r\n");
02541    for (;;) {
02542       if ((res = do_message(&s)) < 0)
02543          break;
02544    }
02545    if (session->authenticated) {
02546       if (option_verbose > 1) {
02547          if (displayconnects) 
02548             ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
02549       }
02550       ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
02551    } else {
02552       if (option_verbose > 1) {
02553          if (displayconnects)
02554             ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
02555       }
02556       ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
02557    }
02558 
02559    /* It is possible under certain circumstances for this session thread
02560       to complete its work and exit *before* the thread that created it
02561       has finished executing the ast_pthread_create_background() function.
02562       If this occurs, some versions of glibc appear to act in a buggy
02563       fashion and attempt to write data into memory that it thinks belongs
02564       to the thread but is in fact not owned by the thread (or may have
02565       been freed completely).
02566 
02567       Causing this thread to yield to other threads at least one time
02568       appears to work around this bug.
02569    */
02570    usleep(1);
02571 
02572    destroy_session(session);
02573    return NULL;
02574 }
02575 
02576 static void *accept_thread(void *ignore)
02577 {
02578    int as;
02579    struct sockaddr_in sin;
02580    socklen_t sinlen;
02581    struct eventqent *eqe;
02582    struct mansession_session *s;
02583    struct protoent *p;
02584    int arg = 1;
02585    int flags;
02586    pthread_attr_t attr;
02587    time_t now;
02588    struct pollfd pfds[1];
02589 
02590    pthread_attr_init(&attr);
02591    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02592 
02593    for (;;) {
02594       time(&now);
02595       AST_LIST_LOCK(&sessions);
02596       AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
02597          if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
02598             AST_LIST_REMOVE_CURRENT(&sessions, list);
02599             num_sessions--;
02600             if (s->authenticated && (option_verbose > 1) && displayconnects) {
02601                ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
02602                   s->username, ast_inet_ntoa(s->sin.sin_addr));
02603             }
02604             free_session(s);
02605             break;   
02606          }
02607       }
02608       AST_LIST_TRAVERSE_SAFE_END
02609       /* Purge master event queue of old, unused events, but make sure we
02610          always keep at least one in the queue */
02611       eqe = master_eventq;
02612       while (master_eventq->next && !master_eventq->usecount) {
02613          eqe = master_eventq;
02614          master_eventq = master_eventq->next;
02615          free(eqe);
02616       }
02617       AST_LIST_UNLOCK(&sessions);
02618 
02619       sinlen = sizeof(sin);
02620       pfds[0].fd = asock;
02621       pfds[0].events = POLLIN;
02622       /* Wait for something to happen, but timeout every few seconds so
02623          we can ditch any old manager sessions */
02624       if (ast_poll(pfds, 1, 5000) < 1)
02625          continue;
02626       as = accept(asock, (struct sockaddr *)&sin, &sinlen);
02627       if (as < 0) {
02628          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
02629          continue;
02630       }
02631       p = getprotobyname("tcp");
02632       if (p) {
02633          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
02634             ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
02635          }
02636       }
02637       if (!(s = ast_calloc(1, sizeof(*s))))
02638          continue;
02639 
02640       memcpy(&s->sin, &sin, sizeof(sin));
02641       s->writetimeout = 100;
02642       s->waiting_thread = AST_PTHREADT_NULL;
02643 
02644       if (!block_sockets) {
02645          /* For safety, make sure socket is non-blocking */
02646          flags = fcntl(as, F_GETFL);
02647          fcntl(as, F_SETFL, flags | O_NONBLOCK);
02648       } else {
02649          flags = fcntl(as, F_GETFL);
02650          fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
02651       }
02652       ast_mutex_init(&s->__lock);
02653       s->fd = as;
02654       s->send_events = -1;
02655       AST_LIST_LOCK(&sessions);
02656       AST_LIST_INSERT_HEAD(&sessions, s, list);
02657       num_sessions++;
02658       /* Find the last place in the master event queue and hook ourselves
02659          in there */
02660       s->eventq = master_eventq;
02661       while(s->eventq->next)
02662          s->eventq = s->eventq->next;
02663       ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
02664       AST_LIST_UNLOCK(&sessions);
02665       if (ast_pthread_create_background(&s->t, &attr, session_do, s))
02666          destroy_session(s);
02667    }
02668    pthread_attr_destroy(&attr);
02669    return NULL;
02670 }
02671 
02672 static int append_event(const char *str, int category)
02673 {
02674    struct eventqent *tmp, *prev = NULL;
02675    tmp = ast_malloc(sizeof(*tmp) + strlen(str));
02676 
02677    if (!tmp)
02678       return -1;
02679 
02680    tmp->next = NULL;
02681    tmp->category = category;
02682    strcpy(tmp->eventdata, str);
02683    
02684    if (master_eventq) {
02685       prev = master_eventq;
02686       while (prev->next) 
02687          prev = prev->next;
02688       prev->next = tmp;
02689    } else {
02690       master_eventq = tmp;
02691    }
02692    
02693    tmp->usecount = num_sessions;
02694    
02695    return 0;
02696 }
02697 
02698 /*! \brief  manager_event: Send AMI event to client */
02699 int manager_event(int category, const char *event, const char *fmt, ...)
02700 {
02701    struct mansession_session *s;
02702    char auth[80];
02703    va_list ap;
02704    struct timeval now;
02705    struct ast_dynamic_str *buf;
02706 
02707    /* Abort if there aren't any manager sessions */
02708    if (!num_sessions)
02709       return 0;
02710 
02711    if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
02712       return -1;
02713 
02714    ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
02715          "Event: %s\r\nPrivilege: %s\r\n",
02716           event, authority_to_str(category, auth, sizeof(auth)));
02717 
02718    if (timestampevents) {
02719       now = ast_tvnow();
02720       ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
02721             "Timestamp: %ld.%06lu\r\n",
02722              now.tv_sec, (unsigned long) now.tv_usec);
02723    }
02724 
02725    va_start(ap, fmt);
02726    ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
02727    va_end(ap);
02728    
02729    ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");  
02730    
02731    /* Append event to master list and wake up any sleeping sessions */
02732    AST_LIST_LOCK(&sessions);
02733    append_event(buf->str, category);
02734    AST_LIST_TRAVERSE(&sessions, s, list) {
02735       ast_mutex_lock(&s->__lock);
02736       if (s->waiting_thread != AST_PTHREADT_NULL)
02737          pthread_kill(s->waiting_thread, SIGURG);
02738       else
02739          /* We have an event to process, but the mansession is
02740           * not waiting for it. We still need to indicate that there
02741           * is an event waiting so that get_input processes the pending
02742           * event instead of polling.
02743           */
02744          s->pending_event = 1;
02745       ast_mutex_unlock(&s->__lock);
02746    }
02747    AST_LIST_UNLOCK(&sessions);
02748 
02749    return 0;
02750 }
02751 
02752 int ast_manager_unregister(char *action) 
02753 {
02754    struct manager_action *cur, *prev;
02755    struct timespec tv = { 5, };
02756 
02757    if (ast_rwlock_timedwrlock(&actionlock, &tv)) {
02758       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
02759       return -1;
02760    }
02761    cur = prev = first_action;
02762    while (cur) {
02763       if (!strcasecmp(action, cur->action)) {
02764          prev->next = cur->next;
02765          free(cur);
02766          if (option_verbose > 1) 
02767             ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
02768          ast_rwlock_unlock(&actionlock);
02769          return 0;
02770       }
02771       prev = cur;
02772       cur = cur->next;
02773    }
02774    ast_rwlock_unlock(&actionlock);
02775    return 0;
02776 }
02777 
02778 static int manager_state_cb(char *context, char *exten, int state, void *data)
02779 {
02780    /* Notify managers of change */
02781    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
02782    return 0;
02783 }
02784 
02785 static int ast_manager_register_struct(struct manager_action *act)
02786 {
02787    struct manager_action *cur, *prev = NULL;
02788    int ret;
02789    struct timespec tv = { 5, };
02790 
02791    if (ast_rwlock_timedwrlock(&actionlock, &tv)) {
02792       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
02793       return -1;
02794    }
02795    cur = first_action;
02796    while (cur) { /* Walk the list of actions */
02797       ret = strcasecmp(cur->action, act->action);
02798       if (ret == 0) {
02799          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
02800          ast_rwlock_unlock(&actionlock);
02801          return -1;
02802       } else if (ret > 0) {
02803          /* Insert these alphabetically */
02804          if (prev) {
02805             act->next = prev->next;
02806             prev->next = act;
02807          } else {
02808             act->next = first_action;
02809             first_action = act;
02810          }
02811          break;
02812       }
02813       prev = cur; 
02814       cur = cur->next;
02815    }
02816    
02817    if (!cur) {
02818       if (prev)
02819          prev->next = act;
02820       else
02821          first_action = act;
02822       act->next = NULL;
02823    }
02824 
02825    if (option_verbose > 1) 
02826       ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
02827    ast_rwlock_unlock(&actionlock);
02828    return 0;
02829 }
02830 
02831 /*! \brief register a new command with manager, including online help. This is 
02832    the preferred way to register a manager command */
02833 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
02834 {
02835    struct manager_action *cur;
02836 
02837    cur = ast_malloc(sizeof(*cur));
02838    if (!cur)
02839       return -1;
02840    
02841    cur->action = action;
02842    cur->authority = auth;
02843    cur->func = func;
02844    cur->synopsis = synopsis;
02845    cur->description = description;
02846    cur->next = NULL;
02847 
02848    if (ast_manager_register_struct(cur)) {
02849       ast_free(cur);
02850       return -1;
02851    }
02852 
02853    return 0;
02854 }
02855 /*! @}
02856  END Doxygen group */
02857 
02858 static struct mansession_session *find_session(uint32_t ident)
02859 {
02860    struct mansession_session *s;
02861 
02862    AST_LIST_LOCK(&sessions);
02863    AST_LIST_TRAVERSE(&sessions, s, list) {
02864       ast_mutex_lock(&s->__lock);
02865       if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
02866          s->inuse++;
02867          break;
02868       }
02869       ast_mutex_unlock(&s->__lock);
02870    }
02871    AST_LIST_UNLOCK(&sessions);
02872 
02873    return s;
02874 }
02875 
02876 int astman_verify_session_readpermissions(uint32_t ident, int perm)
02877 {
02878    int result = 0;
02879    struct mansession_session *s;
02880 
02881    AST_LIST_LOCK(&sessions);
02882    AST_LIST_TRAVERSE(&sessions, s, list) {
02883       ast_mutex_lock(&s->__lock);
02884       if ((s->managerid == ident) && (s->readperm & perm)) {
02885          result = 1;
02886          ast_mutex_unlock(&s->__lock);
02887          break;
02888       }
02889       ast_mutex_unlock(&s->__lock);
02890    }
02891    AST_LIST_UNLOCK(&sessions);
02892    return result;
02893 }
02894 
02895 int astman_verify_session_writepermissions(uint32_t ident, int perm)
02896 {
02897    int result = 0;
02898    struct mansession_session *s;
02899 
02900    AST_LIST_LOCK(&sessions);
02901    AST_LIST_TRAVERSE(&sessions, s, list) {
02902       ast_mutex_lock(&s->__lock);
02903       if ((s->managerid == ident) && (s->writeperm & perm)) {
02904          result = 1;
02905          ast_mutex_unlock(&s->__lock);
02906          break;
02907       }
02908       ast_mutex_unlock(&s->__lock);
02909    }
02910    AST_LIST_UNLOCK(&sessions);
02911    return result;
02912 }
02913 
02914 enum {
02915    FORMAT_RAW,
02916    FORMAT_HTML,
02917    FORMAT_XML,
02918 };
02919 static char *contenttype[] = { "plain", "html", "xml" };
02920 
02921 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
02922 {
02923    struct mansession_session *s = NULL;
02924    struct mansession ss = { .session = NULL, };
02925    uint32_t ident = 0;
02926    char workspace[512];
02927    char cookie[128];
02928    size_t len = sizeof(workspace);
02929    int blastaway = 0;
02930    char *c = workspace;
02931    char *retval = NULL;
02932    struct ast_variable *v;
02933 
02934    for (v = params; v; v = v->next) {
02935       if (!strcasecmp(v->name, "mansession_id")) {
02936          sscanf(v->value, "%30x", &ident);
02937          break;
02938       }
02939    }
02940    
02941    if (!(s = find_session(ident))) {
02942       /* Create new session */
02943       if (!(s = ast_calloc(1, sizeof(*s)))) {
02944          *status = 500;
02945          goto generic_callback_out;
02946       }
02947       memcpy(&s->sin, requestor, sizeof(s->sin));
02948       s->fd = -1;
02949       s->waiting_thread = AST_PTHREADT_NULL;
02950       s->send_events = 0;
02951       ast_mutex_init(&s->__lock);
02952       ast_mutex_lock(&s->__lock);
02953       s->inuse = 1;
02954       /*!\note There is approximately a 1 in 1.8E19 chance that the following
02955        * calculation will produce 0, which is an invalid ID, but due to the
02956        * properties of the rand() function (and the constantcy of s), that
02957        * won't happen twice in a row.
02958        */
02959       while ((s->managerid = rand() ^ (unsigned long) s) == 0);
02960       AST_LIST_LOCK(&sessions);
02961       AST_LIST_INSERT_HEAD(&sessions, s, list);
02962       /* Hook into the last spot in the event queue */
02963       s->eventq = master_eventq;
02964       while (s->eventq->next)
02965          s->eventq = s->eventq->next;
02966       ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
02967       ast_atomic_fetchadd_int(&num_sessions, 1);
02968       AST_LIST_UNLOCK(&sessions);
02969    }
02970 
02971    /* Reset HTTP timeout.  If we're not yet authenticated, keep it extremely short */
02972    time(&s->sessiontimeout);
02973    if (!s->authenticated && (httptimeout > 5))
02974       s->sessiontimeout += 5;
02975    else
02976       s->sessiontimeout += httptimeout;
02977    ss.session = s;
02978    ast_mutex_unlock(&s->__lock);
02979 
02980    ss.f = tmpfile();
02981    ss.fd = fileno(ss.f);
02982 
02983    if (s) {
02984       struct message m = { 0 };
02985       char tmp[80];
02986       unsigned int x;
02987       size_t hdrlen;
02988 
02989       for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
02990          hdrlen = strlen(v->name) + strlen(v->value) + 3;
02991          m.headers[m.hdrcount] = alloca(hdrlen);
02992          snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
02993          m.hdrcount = x + 1;
02994       }
02995 
02996       if (process_message(&ss, &m)) {
02997          if (s->authenticated) {
02998             if (option_verbose > 1) {
02999                if (displayconnects) 
03000                   ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));    
03001             }
03002             ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
03003          } else {
03004             if (option_verbose > 1) {
03005                if (displayconnects)
03006                   ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
03007             }
03008             ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
03009          }
03010          s->needdestroy = 1;
03011       }
03012       ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
03013       sprintf(tmp, "%08x", s->managerid);
03014       ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
03015       if (format == FORMAT_HTML)
03016          ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Interface</title>");
03017       if (format == FORMAT_XML) {
03018          ast_build_string(&c, &len, "<ajax-response>\n");
03019       } else if (format == FORMAT_HTML) {
03020          ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
03021          ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
03022       }
03023       ast_mutex_lock(&s->__lock);
03024       if (ss.fd > -1) {
03025          char *buf;
03026          size_t l;
03027 
03028          /* Ensure buffer is NULL-terminated */
03029          fprintf(ss.f, "%c", 0);
03030 
03031          if ((l = lseek(ss.fd, 0, SEEK_END)) > 0) {
03032             if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, ss.fd, 0))) {
03033                ast_log(LOG_WARNING, "mmap failed.  Manager request output was not processed\n");
03034             } else {
03035                char *tmpbuf;
03036                if (format == FORMAT_XML)
03037                   tmpbuf = xml_translate(buf, params);
03038                else if (format == FORMAT_HTML)
03039                   tmpbuf = html_translate(buf);
03040                else
03041                   tmpbuf = buf;
03042                if (tmpbuf) {
03043                   size_t wlen, tlen;
03044                   if ((retval = malloc((wlen = strlen(workspace)) + (tlen = strlen(tmpbuf)) + 128))) {
03045                      strcpy(retval, workspace);
03046                      strcpy(retval + wlen, tmpbuf);
03047                      c = retval + wlen + tlen;
03048                      /* Leftover space for footer, if any */
03049                      len = 120;
03050                   }
03051                }
03052                if (tmpbuf != buf)
03053                   free(tmpbuf);
03054                free(s->outputstr);
03055                s->outputstr = NULL;
03056                munmap(buf, l);
03057             }
03058          }
03059          fclose(ss.f);
03060          ss.f = NULL;
03061          ss.fd = -1;
03062       } else if (s->outputstr) {
03063          char *tmp;
03064          if (format == FORMAT_XML)
03065             tmp = xml_translate(s->outputstr->str, params);
03066          else if (format == FORMAT_HTML)
03067             tmp = html_translate(s->outputstr->str);
03068          else
03069             tmp = s->outputstr->str;
03070          if (tmp) {
03071             retval = malloc(strlen(workspace) + strlen(tmp) + 128);
03072             if (retval) {
03073                strcpy(retval, workspace);
03074                strcpy(retval + strlen(retval), tmp);
03075                c = retval + strlen(retval);
03076                len = 120;
03077             }
03078          }
03079          if (tmp != s->outputstr->str)
03080             free(tmp);
03081          free(s->outputstr);
03082          s->outputstr = NULL;
03083       }
03084       ast_mutex_unlock(&s->__lock);
03085       /* Still okay because c would safely be pointing to workspace even
03086          if retval failed to allocate above */
03087       if (format == FORMAT_XML) {
03088          ast_build_string(&c, &len, "</ajax-response>\n");
03089       } else if (format == FORMAT_HTML)
03090          ast_build_string(&c, &len, "</table></body>\r\n");
03091    } else {
03092       *status = 500;
03093       *title = strdup("Server Error");
03094    }
03095    ast_mutex_lock(&s->__lock);
03096    if (s->needdestroy) {
03097       if (s->inuse == 1) {
03098          ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
03099          blastaway = 1;
03100       } else {
03101          ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
03102          if (s->waiting_thread != AST_PTHREADT_NULL)
03103             pthread_kill(s->waiting_thread, SIGURG);
03104          s->inuse--;
03105       }
03106    } else
03107       s->inuse--;
03108    ast_mutex_unlock(&s->__lock);
03109    
03110    if (blastaway)
03111       destroy_session(s);
03112 generic_callback_out:
03113    if (*status != 200)
03114       return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); 
03115    return retval;
03116 }
03117 
03118 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03119 {
03120    return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
03121 }
03122 
03123 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03124 {
03125    return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
03126 }
03127 
03128 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03129 {
03130    return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
03131 }
03132 
03133 struct ast_http_uri rawmanuri = {
03134    .description = "Raw HTTP Manager Event Interface",
03135    .uri = "rawman",
03136    .has_subtree = 0,
03137    .callback = rawman_http_callback,
03138 };
03139 
03140 struct ast_http_uri manageruri = {
03141    .description = "HTML Manager Event Interface",
03142    .uri = "manager",
03143    .has_subtree = 0,
03144    .callback = manager_http_callback,
03145 };
03146 
03147 struct ast_http_uri managerxmluri = {
03148    .description = "XML Manager Event Interface",
03149    .uri = "mxml",
03150    .has_subtree = 0,
03151    .callback = mxml_http_callback,
03152 };
03153 
03154 static int registered = 0;
03155 static int webregged = 0;
03156 
03157 int init_manager(void)
03158 {
03159    struct ast_config *cfg = NULL, *ucfg = NULL;
03160    const char *val;
03161    char *cat = NULL;
03162    int oldportno = portno;
03163    static struct sockaddr_in ba;
03164    int x = 1;
03165    int flags;
03166    int newhttptimeout = 60;
03167    struct ast_manager_user *user = NULL;
03168 
03169    if (!registered) {
03170       /* Register default actions */
03171       ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
03172       ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
03173       ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
03174       ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
03175       ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
03176       ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
03177       ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
03178       ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
03179       ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
03180       ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
03181       ast_manager_register2("Atxfer", EVENT_FLAG_CALL, action_atxfer, "Attended transfer", mandescr_atxfer );
03182       ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
03183       ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
03184       ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
03185       ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
03186       ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
03187       ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
03188       ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
03189       ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
03190       ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
03191       ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
03192       ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
03193 
03194       ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
03195       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
03196       registered = 1;
03197       /* Append placeholder event so master_eventq never runs dry */
03198       append_event("Event: Placeholder\r\n\r\n", 0);
03199    }
03200    portno = DEFAULT_MANAGER_PORT;
03201    displayconnects = 1;
03202    cfg = ast_config_load("manager.conf");
03203    if (!cfg) {
03204       ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
03205       return 0;
03206    }
03207    if ((val = ast_variable_retrieve(cfg, "general", "enabled"))) {
03208       manager_enabled = ast_true(val);
03209    }
03210    if ((val = ast_variable_retrieve(cfg, "general", "block-sockets"))) {
03211       block_sockets = ast_true(val);
03212    }
03213    if((val = ast_variable_retrieve(cfg, "general", "webenabled"))) {
03214       webmanager_enabled = ast_true(val);
03215    }
03216    if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
03217       if (sscanf(val, "%5d", &portno) != 1) {
03218          ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
03219          portno = DEFAULT_MANAGER_PORT;
03220       }
03221    }
03222 
03223    if ((val = ast_variable_retrieve(cfg, "general", "displayconnects"))) {
03224       displayconnects = ast_true(val);
03225    }
03226    if ((val = ast_variable_retrieve(cfg, "general", "timestampevents"))) {
03227       timestampevents = ast_true(val);
03228    }
03229    if ((val = ast_variable_retrieve(cfg, "general", "httptimeout"))) {
03230       newhttptimeout = atoi(val);
03231    }
03232 
03233    memset(&ba, 0, sizeof(ba));
03234    ba.sin_family = AF_INET;
03235    ba.sin_port = htons(portno);
03236 
03237    if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
03238       if (!inet_aton(val, &ba.sin_addr)) { 
03239          ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
03240          memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
03241       }
03242    }
03243    
03244 
03245    if ((asock > -1) && ((portno != oldportno) || !manager_enabled)) {
03246 #if 0
03247       /* Can't be done yet */
03248       close(asock);
03249       asock = -1;
03250 #else
03251       ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
03252 #endif
03253    }
03254 
03255    AST_LIST_LOCK(&users);
03256 
03257    if ((ucfg = ast_config_load("users.conf"))) {
03258       while ((cat = ast_category_browse(ucfg, cat))) {
03259          int hasmanager = 0;
03260          struct ast_variable *var = NULL;
03261 
03262          if (!strcasecmp(cat, "general")) {
03263             continue;
03264          }
03265 
03266          if (!(hasmanager = ast_true(ast_variable_retrieve(ucfg, cat, "hasmanager")))) {
03267             continue;
03268          }
03269 
03270          /* Look for an existing entry, if none found - create one and add it to the list */
03271          if (!(user = ast_get_manager_by_name_locked(cat))) {
03272             if (!(user = ast_calloc(1, sizeof(*user)))) {
03273                break;
03274             }
03275             /* Copy name over */
03276             ast_copy_string(user->username, cat, sizeof(user->username));
03277             /* Insert into list */
03278             AST_LIST_INSERT_TAIL(&users, user, list);
03279          }
03280 
03281          /* Make sure we keep this user and don't destroy it during cleanup */
03282          user->keep = 1;
03283 
03284          for (var = ast_variable_browse(ucfg, cat); var; var = var->next) {
03285             if (!strcasecmp(var->name, "secret")) {
03286                if (user->secret) {
03287                   free(user->secret);
03288                }
03289                user->secret = ast_strdup(var->value);
03290             } else if (!strcasecmp(var->name, "deny") ) {
03291                if (user->deny) {
03292                   free(user->deny);
03293                }
03294                user->deny = ast_strdup(var->value);
03295             } else if (!strcasecmp(var->name, "permit") ) {
03296                if (user->permit) {
03297                   free(user->permit);
03298                }
03299                user->permit = ast_strdup(var->value);
03300             } else if (!strcasecmp(var->name, "read") ) {
03301                if (user->read) {
03302                   free(user->read);
03303                }
03304                user->read = ast_strdup(var->value);
03305             } else if (!strcasecmp(var->name, "write") ) {
03306                if (user->write) {
03307                   free(user->write);
03308                }
03309                user->write = ast_strdup(var->value);
03310             } else if (!strcasecmp(var->name, "displayconnects") ) {
03311                user->displayconnects = ast_true(var->value);
03312             } else if (!strcasecmp(var->name, "hasmanager")) {
03313                /* already handled */
03314             } else {
03315                ast_log(LOG_DEBUG, "%s is an unknown option (to the manager module).\n", var->name);
03316             }
03317          }
03318       }
03319       ast_config_destroy(ucfg);
03320    }
03321 
03322    while ((cat = ast_category_browse(cfg, cat))) {
03323       struct ast_variable *var = NULL;
03324 
03325       if (!strcasecmp(cat, "general"))
03326          continue;
03327 
03328       /* Look for an existing entry, if none found - create one and add it to the list */
03329       if (!(user = ast_get_manager_by_name_locked(cat))) {
03330          if (!(user = ast_calloc(1, sizeof(*user))))
03331             break;
03332          /* Copy name over */
03333          ast_copy_string(user->username, cat, sizeof(user->username));
03334          /* Insert into list */
03335          AST_LIST_INSERT_TAIL(&users, user, list);
03336       }
03337 
03338       /* Make sure we keep this user and don't destroy it during cleanup */
03339       user->keep = 1;
03340 
03341       var = ast_variable_browse(cfg, cat);
03342       while (var) {
03343          if (!strcasecmp(var->name, "secret")) {
03344             if (user->secret)
03345                free(user->secret);
03346             user->secret = ast_strdup(var->value);
03347          } else if (!strcasecmp(var->name, "deny") ) {
03348             if (user->deny)
03349                free(user->deny);
03350             user->deny = ast_strdup(var->value);
03351          } else if (!strcasecmp(var->name, "permit") ) {
03352             if (user->permit)
03353                free(user->permit);
03354             user->permit = ast_strdup(var->value);
03355          }  else if (!strcasecmp(var->name, "read") ) {
03356             if (user->read)
03357                free(user->read);
03358             user->read = ast_strdup(var->value);
03359          }  else if (!strcasecmp(var->name, "write") ) {
03360             if (user->write)
03361                free(user->write);
03362             user->write = ast_strdup(var->value);
03363          }  else if (!strcasecmp(var->name, "displayconnects") )
03364             user->displayconnects = ast_true(var->value);
03365          else
03366             ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
03367          var = var->next;
03368       }
03369    }
03370 
03371    /* Perform cleanup - essentially prune out old users that no longer exist */
03372    AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
03373       if (user->keep) {
03374          user->keep = 0;
03375          continue;
03376       }
03377       /* We do not need to keep this user so take them out of the list */
03378       AST_LIST_REMOVE_CURRENT(&users, list);
03379       /* Free their memory now */
03380       if (user->secret)
03381          free(user->secret);
03382       if (user->deny)
03383          free(user->deny);
03384       if (user->permit)
03385          free(user->permit);
03386       if (user->read)
03387          free(user->read);
03388       if (user->write)
03389          free(user->write);
03390       free(user);
03391    }
03392    AST_LIST_TRAVERSE_SAFE_END
03393 
03394    AST_LIST_UNLOCK(&users);
03395 
03396    ast_config_destroy(cfg);
03397    
03398    if (webmanager_enabled && manager_enabled) {
03399       if (!webregged) {
03400          ast_http_uri_link(&rawmanuri);
03401          ast_http_uri_link(&manageruri);
03402          ast_http_uri_link(&managerxmluri);
03403          webregged = 1;
03404       }
03405    } else {
03406       if (webregged) {
03407          ast_http_uri_unlink(&rawmanuri);
03408          ast_http_uri_unlink(&manageruri);
03409          ast_http_uri_unlink(&managerxmluri);
03410          webregged = 0;
03411       }
03412    }
03413 
03414    if (newhttptimeout > 0)
03415       httptimeout = newhttptimeout;
03416 
03417    /* If not enabled, do nothing */
03418    if (!manager_enabled)
03419       return 0;
03420 
03421    if (asock < 0) {
03422       asock = socket(AF_INET, SOCK_STREAM, 0);
03423       if (asock < 0) {
03424          ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
03425          return -1;
03426       }
03427       setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
03428       if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
03429          ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
03430          close(asock);
03431          asock = -1;
03432          return -1;
03433       }
03434       if (listen(asock, 2)) {
03435          ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
03436          close(asock);
03437          asock = -1;
03438          return -1;
03439       }
03440       flags = fcntl(asock, F_GETFL);
03441       fcntl(asock, F_SETFL, flags | O_NONBLOCK);
03442       if (option_verbose)
03443          ast_verbose("Asterisk Management interface listening on port %d\n", portno);
03444       ast_pthread_create_background(&t, NULL, accept_thread, NULL);
03445    }
03446    return 0;
03447 }
03448 
03449 int reload_manager(void)
03450 {
03451    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
03452    return init_manager();
03453 }

Generated on Fri Jan 29 14:25:17 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7