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

Generated on Mon Nov 24 15:34:19 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7