Mon Jul 14 17:24:58 2008

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

Generated on Mon Jul 14 17:24:59 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1