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

Generated on Tue Nov 4 13:20:20 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7