Thu Feb 5 16:25:48 2009

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

Generated on Thu Feb 5 16:25:48 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7