Sun Dec 18 20:55:40 2011

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

Generated on Sun Dec 18 20:55:40 2011 for Asterisk - the Open Source PBX by  doxygen 1.5.6