Thu May 14 14:49:04 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: 172030 $")
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    if (chan->pbx) {
01693       ast_channel_lock(chan);
01694       ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
01695       ast_channel_unlock(chan);
01696    }
01697    res = ast_async_goto(chan, context, exten, pi);
01698    if (!res) {
01699       if (!ast_strlen_zero(name2)) {
01700          if (chan2) {
01701             if (chan2->pbx) {
01702                ast_channel_lock(chan2);
01703                ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
01704                ast_channel_unlock(chan2);
01705             }
01706             res = ast_async_goto(chan2, context, exten, pi);
01707          } else {
01708             res = -1;
01709          }
01710          if (!res)
01711             astman_send_ack(s, m, "Dual Redirect successful");
01712          else
01713             astman_send_error(s, m, "Secondary redirect failed");
01714       } else
01715          astman_send_ack(s, m, "Redirect successful");
01716    } else
01717       astman_send_error(s, m, "Redirect failed");
01718    if (chan)
01719       ast_channel_unlock(chan);
01720    if (chan2)
01721       ast_channel_unlock(chan2);
01722    return 0;
01723 }
01724 
01725 static int check_blacklist(const char *cmd)
01726 {
01727    char *cmd_copy, *cur_cmd;
01728    char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
01729    int i;
01730 
01731    cmd_copy = ast_strdupa(cmd);
01732    for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
01733       cur_cmd = ast_strip(cur_cmd);
01734       if (ast_strlen_zero(cur_cmd)) {
01735          i--;
01736          continue;
01737       }
01738 
01739       cmd_words[i] = cur_cmd;
01740    }
01741 
01742    for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
01743       int j, match = 1;
01744 
01745       for (j = 0; command_blacklist[i].words[j]; j++) {
01746          if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
01747             match = 0;
01748             break;
01749          }
01750       }
01751 
01752       if (match) {
01753          return 1;
01754       }
01755    }
01756 
01757    return 0;
01758 }
01759 
01760 static char mandescr_atxfer[] =
01761 "Description: do attended transfer.\n"
01762 "Variables: (Names marked with * are required)\n"
01763 "  *Channel: transferer Channel\n"
01764 "  *Exten: Extension to transfer to\n"
01765 "  Context: Context to transfer to\n"
01766 "  ActionID: Optional Action id for message matching.\n";
01767 
01768 static int action_atxfer(struct mansession *s, const struct message *m)
01769 {
01770    struct ast_channel *c;
01771    const char *name = astman_get_header(m, "Channel");
01772    const char *exten = astman_get_header(m, "Exten");
01773    const char *context = astman_get_header(m, "Context");
01774    char *xferto;
01775    int len;
01776 
01777    if (ast_strlen_zero(name)) {
01778       astman_send_error(s, m, "No channel specified");
01779       return 0;
01780    }
01781    if (ast_strlen_zero(exten)) {
01782       astman_send_error(s, m, "No exten specified");
01783       return 0;
01784    }
01785    c = ast_get_channel_by_name_locked(name);
01786    if (!c) {
01787       astman_send_error(s, m, "No such channel");
01788       return 0;
01789    }
01790    len = asprintf(&xferto, "%s@%s", exten, context);
01791    if (len < 0) {
01792       astman_send_error(s, m, "Out of memory!");
01793       goto cleanup;
01794    }
01795    ast_queue_control_data(c, AST_CONTROL_ATXFERCMD, xferto, len+1);
01796    free(xferto);
01797    astman_send_ack(s, m, "Attended transfer started");
01798 cleanup:
01799    ast_channel_unlock(c);
01800    return 0;
01801 }
01802 
01803 static char mandescr_command[] = 
01804 "Description: Run a CLI command.\n"
01805 "Variables: (Names marked with * are required)\n"
01806 "  *Command: Asterisk CLI command to run\n"
01807 "  ActionID: Optional Action id for message matching.\n";
01808 
01809 /*! \brief  action_command: Manager command "command" - execute CLI command */
01810 static int action_command(struct mansession *s, const struct message *m)
01811 {
01812    const char *cmd = astman_get_header(m, "Command");
01813    const char *id = astman_get_header(m, "ActionID");
01814    char *buf, *final_buf;
01815    char template[] = "/tmp/ast-ami-XXXXXX";  /* template for temporary file */
01816    int fd = mkstemp(template);
01817    off_t l;
01818 
01819    if (ast_strlen_zero(cmd)) {
01820       astman_send_error(s, m, "No command provided");
01821       return 0;
01822    }
01823 
01824    if (check_blacklist(cmd)) {
01825       astman_send_error(s, m, "Command blacklisted");
01826       return 0;
01827    }
01828 
01829    astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
01830    if (!ast_strlen_zero(id))
01831       astman_append(s, "ActionID: %s\r\n", id);
01832    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
01833    ast_cli_command(fd, cmd);  /* XXX need to change this to use a FILE * */
01834    l = lseek(fd, 0, SEEK_END);   /* how many chars available */
01835 
01836    /* This has a potential to overflow the stack.  Hence, use the heap. */
01837    buf = ast_calloc(1, l + 1);
01838    final_buf = ast_calloc(1, l + 1);
01839    if (buf) {
01840       lseek(fd, 0, SEEK_SET);
01841       if (read(fd, buf, l) < 0) {
01842          ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01843       }
01844       buf[l] = '\0';
01845       if (final_buf) {
01846          term_strip(final_buf, buf, l);
01847          final_buf[l] = '\0';
01848       }
01849       astman_append(s, "%s", S_OR(final_buf, buf));
01850       ast_free(buf);
01851    }
01852    close(fd);
01853    unlink(template);
01854    astman_append(s, "--END COMMAND--\r\n\r\n");
01855    if (final_buf)
01856       ast_free(final_buf);
01857    return 0;
01858 }
01859 
01860 static void *fast_originate(void *data)
01861 {
01862    struct fast_originate_helper *in = data;
01863    int res;
01864    int reason = 0;
01865    struct ast_channel *chan = NULL;
01866    char requested_channel[AST_CHANNEL_NAME];
01867 
01868    if (!ast_strlen_zero(in->app)) {
01869       res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
01870          S_OR(in->cid_num, NULL), 
01871          S_OR(in->cid_name, NULL),
01872          in->vars, in->account, &chan);
01873    } else {
01874       res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
01875          S_OR(in->cid_num, NULL), 
01876          S_OR(in->cid_name, NULL),
01877          in->vars, in->account, &chan);
01878    }
01879 
01880    if (!chan)
01881       snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);   
01882    /* Tell the manager what happened with the channel */
01883    manager_event(EVENT_FLAG_CALL, "OriginateResponse",
01884       "%s"
01885       "Response: %s\r\n"
01886       "Channel: %s\r\n"
01887       "Context: %s\r\n"
01888       "Exten: %s\r\n"
01889       "Reason: %d\r\n"
01890       "Uniqueid: %s\r\n"
01891       "CallerID: %s\r\n"      /* This parameter is deprecated and will be removed post-1.4 */
01892       "CallerIDNum: %s\r\n"
01893       "CallerIDName: %s\r\n",
01894       in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason, 
01895       chan ? chan->uniqueid : "<null>",
01896       S_OR(in->cid_num, "<unknown>"),
01897       S_OR(in->cid_num, "<unknown>"),
01898       S_OR(in->cid_name, "<unknown>")
01899       );
01900 
01901    /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
01902    if (chan)
01903       ast_channel_unlock(chan);
01904    free(in);
01905    return NULL;
01906 }
01907 
01908 static char mandescr_originate[] = 
01909 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
01910 "  Application/Data\n"
01911 "Variables: (Names marked with * are required)\n"
01912 "  *Channel: Channel name to call\n"
01913 "  Exten: Extension to use (requires 'Context' and 'Priority')\n"
01914 "  Context: Context to use (requires 'Exten' and 'Priority')\n"
01915 "  Priority: Priority to use (requires 'Exten' and 'Context')\n"
01916 "  Application: Application to use\n"
01917 "  Data: Data to use (requires 'Application')\n"
01918 "  Timeout: How long to wait for call to be answered (in ms)\n"
01919 "  CallerID: Caller ID to be set on the outgoing channel\n"
01920 "  Variable: Channel variable to set, multiple Variable: headers are allowed\n"
01921 "  Account: Account code\n"
01922 "  Async: Set to 'true' for fast origination\n";
01923 
01924 static int action_originate(struct mansession *s, const struct message *m)
01925 {
01926    const char *name = astman_get_header(m, "Channel");
01927    const char *exten = astman_get_header(m, "Exten");
01928    const char *context = astman_get_header(m, "Context");
01929    const char *priority = astman_get_header(m, "Priority");
01930    const char *timeout = astman_get_header(m, "Timeout");
01931    const char *callerid = astman_get_header(m, "CallerID");
01932    const char *account = astman_get_header(m, "Account");
01933    const char *app = astman_get_header(m, "Application");
01934    const char *appdata = astman_get_header(m, "Data");
01935    const char *async = astman_get_header(m, "Async");
01936    const char *id = astman_get_header(m, "ActionID");
01937    const char *codecs = astman_get_header(m, "Codecs");
01938    struct ast_variable *vars = astman_get_variables(m);
01939    char *tech, *data;
01940    char *l = NULL, *n = NULL;
01941    int pi = 0;
01942    int res;
01943    int to = 30000;
01944    int reason = 0;
01945    char tmp[256];
01946    char tmp2[256];
01947    int format = AST_FORMAT_SLINEAR;
01948    
01949    pthread_t th;
01950    pthread_attr_t attr;
01951    if (ast_strlen_zero(name)) {
01952       astman_send_error(s, m, "Channel not specified");
01953       return 0;
01954    }
01955    if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
01956       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
01957          astman_send_error(s, m, "Invalid priority");
01958          return 0;
01959       }
01960    }
01961    if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
01962       astman_send_error(s, m, "Invalid timeout");
01963       return 0;
01964    }
01965    ast_copy_string(tmp, name, sizeof(tmp));
01966    tech = tmp;
01967    data = strchr(tmp, '/');
01968    if (!data) {
01969       astman_send_error(s, m, "Invalid channel");
01970       return 0;
01971    }
01972    *data++ = '\0';
01973    ast_copy_string(tmp2, callerid, sizeof(tmp2));
01974    ast_callerid_parse(tmp2, &n, &l);
01975    if (n) {
01976       if (ast_strlen_zero(n))
01977          n = NULL;
01978    }
01979    if (l) {
01980       ast_shrink_phone_number(l);
01981       if (ast_strlen_zero(l))
01982          l = NULL;
01983    }
01984    if (!ast_strlen_zero(codecs)) {
01985       format = 0;
01986       ast_parse_allow_disallow(NULL, &format, codecs, 1);
01987    }
01988    if (ast_true(async)) {
01989       struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
01990       if (!fast) {
01991          res = -1;
01992       } else {
01993          if (!ast_strlen_zero(id))
01994             snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
01995          ast_copy_string(fast->tech, tech, sizeof(fast->tech));
01996             ast_copy_string(fast->data, data, sizeof(fast->data));
01997          ast_copy_string(fast->app, app, sizeof(fast->app));
01998          ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
01999          if (l)
02000             ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
02001          if (n)
02002             ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
02003          fast->vars = vars;   
02004          ast_copy_string(fast->context, context, sizeof(fast->context));
02005          ast_copy_string(fast->exten, exten, sizeof(fast->exten));
02006          ast_copy_string(fast->account, account, sizeof(fast->account));
02007          fast->format = format;
02008          fast->timeout = to;
02009          fast->priority = pi;
02010          pthread_attr_init(&attr);
02011          pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02012          if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
02013             ast_free(fast);
02014             res = -1;
02015          } else {
02016             res = 0;
02017          }
02018          pthread_attr_destroy(&attr);
02019       }
02020    } else if (!ast_strlen_zero(app)) {
02021          res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
02022       } else {
02023       if (exten && context && pi)
02024             res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
02025       else {
02026          astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
02027          return 0;
02028       }
02029    }   
02030    if (!res)
02031       astman_send_ack(s, m, "Originate successfully queued");
02032    else
02033       astman_send_error(s, m, "Originate failed");
02034    return 0;
02035 }
02036 
02037 /*! \brief Help text for manager command mailboxstatus
02038  */
02039 static char mandescr_mailboxstatus[] = 
02040 "Description: Checks a voicemail account for status.\n"
02041 "Variables: (Names marked with * are required)\n"
02042 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02043 "  ActionID: Optional ActionID for message matching.\n"
02044 "Returns number of messages.\n"
02045 "  Message: Mailbox Status\n"
02046 "  Mailbox: <mailboxid>\n"
02047 "  Waiting: <count>\n"
02048 "\n";
02049 
02050 static int action_mailboxstatus(struct mansession *s, const struct message *m)
02051 {
02052    const char *mailbox = astman_get_header(m, "Mailbox");
02053    const char *id = astman_get_header(m,"ActionID");
02054    char idText[256] = "";
02055    int ret;
02056    if (ast_strlen_zero(mailbox)) {
02057       astman_send_error(s, m, "Mailbox not specified");
02058       return 0;
02059    }
02060         if (!ast_strlen_zero(id))
02061                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02062    ret = ast_app_has_voicemail(mailbox, NULL);
02063    astman_append(s, "Response: Success\r\n"
02064                "%s"
02065                "Message: Mailbox Status\r\n"
02066                "Mailbox: %s\r\n"
02067                "Waiting: %d\r\n\r\n", idText, mailbox, ret);
02068    return 0;
02069 }
02070 
02071 static char mandescr_mailboxcount[] = 
02072 "Description: Checks a voicemail account for new messages.\n"
02073 "Variables: (Names marked with * are required)\n"
02074 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02075 "  ActionID: Optional ActionID for message matching.\n"
02076 "Returns number of new and old messages.\n"
02077 "  Message: Mailbox Message Count\n"
02078 "  Mailbox: <mailboxid>\n"
02079 "  NewMessages: <count>\n"
02080 "  OldMessages: <count>\n"
02081 "\n";
02082 static int action_mailboxcount(struct mansession *s, const struct message *m)
02083 {
02084    const char *mailbox = astman_get_header(m, "Mailbox");
02085    const char *id = astman_get_header(m,"ActionID");
02086    char idText[256] = "";
02087    int newmsgs = 0, oldmsgs = 0;
02088    if (ast_strlen_zero(mailbox)) {
02089       astman_send_error(s, m, "Mailbox not specified");
02090       return 0;
02091    }
02092    ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
02093    if (!ast_strlen_zero(id)) {
02094       snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
02095    }
02096    astman_append(s, "Response: Success\r\n"
02097                "%s"
02098                "Message: Mailbox Message Count\r\n"
02099                "Mailbox: %s\r\n"
02100                "NewMessages: %d\r\n"
02101                "OldMessages: %d\r\n" 
02102                "\r\n",
02103                 idText,mailbox, newmsgs, oldmsgs);
02104    return 0;
02105 }
02106 
02107 static char mandescr_extensionstate[] = 
02108 "Description: Report the extension state for given extension.\n"
02109 "  If the extension has a hint, will use devicestate to check\n"
02110 "  the status of the device connected to the extension.\n"
02111 "Variables: (Names marked with * are required)\n"
02112 "  *Exten: Extension to check state on\n"
02113 "  *Context: Context for extension\n"
02114 "  ActionId: Optional ID for this transaction\n"
02115 "Will return an \"Extension Status\" message.\n"
02116 "The response will include the hint for the extension and the status.\n";
02117 
02118 static int action_extensionstate(struct mansession *s, const struct message *m)
02119 {
02120    const char *exten = astman_get_header(m, "Exten");
02121    const char *context = astman_get_header(m, "Context");
02122    const char *id = astman_get_header(m,"ActionID");
02123    char idText[256] = "";
02124    char hint[256] = "";
02125    int status;
02126    if (ast_strlen_zero(exten)) {
02127       astman_send_error(s, m, "Extension not specified");
02128       return 0;
02129    }
02130    if (ast_strlen_zero(context))
02131       context = "default";
02132    status = ast_extension_state(NULL, context, exten);
02133    ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
02134         if (!ast_strlen_zero(id)) {
02135                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02136         }
02137    astman_append(s, "Response: Success\r\n"
02138                     "%s"
02139                "Message: Extension Status\r\n"
02140                "Exten: %s\r\n"
02141                "Context: %s\r\n"
02142                "Hint: %s\r\n"
02143                "Status: %d\r\n\r\n",
02144                idText,exten, context, hint, status);
02145    return 0;
02146 }
02147 
02148 static char mandescr_timeout[] = 
02149 "Description: Hangup a channel after a certain time.\n"
02150 "Variables: (Names marked with * are required)\n"
02151 "  *Channel: Channel name to hangup\n"
02152 "  *Timeout: Maximum duration of the call (sec)\n"
02153 "Acknowledges set time with 'Timeout Set' message\n";
02154 
02155 static int action_timeout(struct mansession *s, const struct message *m)
02156 {
02157    struct ast_channel *c = NULL;
02158    const char *name = astman_get_header(m, "Channel");
02159    int timeout = atoi(astman_get_header(m, "Timeout"));
02160    if (ast_strlen_zero(name)) {
02161       astman_send_error(s, m, "No channel specified");
02162       return 0;
02163    }
02164    if (!timeout) {
02165       astman_send_error(s, m, "No timeout specified");
02166       return 0;
02167    }
02168    c = ast_get_channel_by_name_locked(name);
02169    if (!c) {
02170       astman_send_error(s, m, "No such channel");
02171       return 0;
02172    }
02173    ast_channel_setwhentohangup(c, timeout);
02174    ast_channel_unlock(c);
02175    astman_send_ack(s, m, "Timeout Set");
02176    return 0;
02177 }
02178 
02179 static int process_events(struct mansession *s)
02180 {
02181    struct eventqent *eqe;
02182    int ret = 0;
02183    ast_mutex_lock(&s->__lock);
02184    if (!s->eventq)
02185       s->eventq = master_eventq;
02186    while(s->eventq->next) {
02187       eqe = s->eventq->next;
02188       if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
02189                ((s->send_events & eqe->category) == eqe->category)) {
02190          if (s->fd > -1) {
02191             if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
02192                ret = -1;
02193          } else if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr)))) 
02194             ret = -1;
02195          else 
02196             ast_dynamic_str_append(&s->outputstr, 0, "%s", eqe->eventdata);
02197       }
02198       unuse_eventqent(s->eventq);
02199       s->eventq = eqe;
02200    }
02201    ast_mutex_unlock(&s->__lock);
02202    return ret;
02203 }
02204 
02205 static char mandescr_userevent[] =
02206 "Description: Send an event to manager sessions.\n"
02207 "Variables: (Names marked with * are required)\n"
02208 "       *UserEvent: EventStringToSend\n"
02209 "       Header1: Content1\n"
02210 "       HeaderN: ContentN\n";
02211 
02212 static int action_userevent(struct mansession *s, const struct message *m)
02213 {
02214    const char *event = astman_get_header(m, "UserEvent");
02215    char body[2048] = "";
02216    int x, bodylen = 0, xlen;
02217    for (x = 0; x < m->hdrcount; x++) {
02218       if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
02219          if (sizeof(body) < bodylen + (xlen = strlen(m->headers[x])) + 3) {
02220             ast_log(LOG_WARNING, "UserEvent exceeds our buffer length.  Truncating.\n");
02221             break;
02222          }
02223          ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
02224          bodylen += xlen;
02225          ast_copy_string(body + bodylen, "\r\n", 3);
02226          bodylen += 2;
02227       }
02228    }
02229 
02230    manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
02231    return 0;
02232 }
02233 
02234 static char mandescr_coresettings[] =
02235 "Description: Query for Core PBX settings.\n"
02236 "Variables: (Names marked with * are optional)\n"
02237 "       *ActionID: ActionID of this transaction\n";
02238 
02239 /*! \brief Show PBX core settings information */
02240 static int action_coresettings(struct mansession *s, const struct message *m)
02241 {
02242    const char *actionid = astman_get_header(m, "ActionID");
02243    char idText[150] = "";
02244 
02245    if (!ast_strlen_zero(actionid))
02246       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02247 
02248    astman_append(s, "Response: Success\r\n"
02249          "%s"
02250          "AMIversion: %s\r\n"
02251          "AsteriskVersion: %s\r\n"
02252          "SystemName: %s\r\n"
02253          "CoreMaxCalls: %d\r\n"
02254          "CoreMaxLoadAvg: %f\r\n"
02255          "CoreRunUser: %s\r\n"
02256          "CoreRunGroup: %s\r\n"
02257          "CoreMaxFilehandles: %d\r\n" 
02258          "CoreRealTimeEnabled: %s\r\n"
02259          "CoreCDRenabled: %s\r\n"
02260          "CoreHTTPenabled: %s\r\n"
02261          "\r\n",
02262          idText,
02263          AMI_VERSION,
02264          ASTERISK_VERSION, 
02265          ast_config_AST_SYSTEM_NAME,
02266          option_maxcalls,
02267          option_maxload,
02268          ast_config_AST_RUN_USER,
02269          ast_config_AST_RUN_GROUP,
02270          option_maxfiles,
02271          ast_realtime_enabled() ? "Yes" : "No",
02272          check_cdr_enabled() ? "Yes" : "No",
02273          check_webmanager_enabled() ? "Yes" : "No"
02274          );
02275    return 0;
02276 }
02277 
02278 static char mandescr_corestatus[] =
02279 "Description: Query for Core PBX status.\n"
02280 "Variables: (Names marked with * are optional)\n"
02281 "       *ActionID: ActionID of this transaction\n";
02282 
02283 /*! \brief Show PBX core status information */
02284 static int action_corestatus(struct mansession *s, const struct message *m)
02285 {
02286    const char *actionid = astman_get_header(m, "ActionID");
02287    char idText[150];
02288    char startuptime[150];
02289    char reloadtime[150];
02290    struct tm tm;
02291 
02292    if (!ast_strlen_zero(actionid))
02293       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02294 
02295    ast_localtime(&ast_startuptime, &tm, NULL);
02296    strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
02297    ast_localtime(&ast_lastreloadtime, &tm, NULL);
02298    strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
02299 
02300    astman_append(s, "Response: Success\r\n"
02301          "%s"
02302          "CoreStartupTime: %s\r\n"
02303          "CoreReloadTime: %s\r\n"
02304          "CoreCurrentCalls: %d\r\n"
02305          "\r\n",
02306          idText,
02307          startuptime,
02308          reloadtime,
02309          ast_active_channels()
02310          );
02311    return 0;
02312 }
02313 
02314 static int process_message(struct mansession *s, const struct message *m)
02315 {
02316    char action[80] = "";
02317    struct manager_action *tmp;
02318    const char *id = astman_get_header(m,"ActionID");
02319    char idText[256] = "";
02320    int ret = 0;
02321 
02322    ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
02323    if (option_debug)
02324       ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
02325 
02326    if (ast_strlen_zero(action)) {
02327       astman_send_error(s, m, "Missing action in request");
02328       return 0;
02329    }
02330    if (!ast_strlen_zero(id)) {
02331       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02332    }
02333    if (!s->authenticated) {
02334       if (!strcasecmp(action, "Challenge")) {
02335          const char *authtype = astman_get_header(m, "AuthType");
02336 
02337          if (!strcasecmp(authtype, "MD5")) {
02338             if (ast_strlen_zero(s->challenge))
02339                snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
02340             astman_append(s, "Response: Success\r\n"
02341                   "%s"
02342                   "Challenge: %s\r\n\r\n",
02343                   idText, s->challenge);
02344             return 0;
02345          } else {
02346             astman_send_error(s, m, "Must specify AuthType");
02347             return 0;
02348          }
02349       } else if (!strcasecmp(action, "Login")) {
02350          if (authenticate(s, m)) {
02351             sleep(1);
02352             astman_send_error(s, m, "Authentication failed");
02353             return -1;
02354          } else {
02355             s->authenticated = 1;
02356             if (option_verbose > 1) {
02357                if (displayconnects) {
02358                   ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", 
02359                      (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
02360                }
02361             }
02362             ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", 
02363                (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
02364             astman_send_ack(s, m, "Authentication accepted");
02365          }
02366       } else if (!strcasecmp(action, "Logoff")) {
02367          astman_send_ack(s, m, "See ya");
02368          return -1;
02369       } else
02370          astman_send_error(s, m, "Authentication Required");
02371    } else {
02372       if (!strcasecmp(action, "Login"))
02373          astman_send_ack(s, m, "Already logged in");
02374       else {
02375          ast_rwlock_rdlock(&actionlock);
02376          for (tmp = first_action; tmp; tmp = tmp->next) {      
02377             if (strcasecmp(action, tmp->action))
02378                continue;
02379             if ((s->writeperm & tmp->authority) == tmp->authority) {
02380                if (tmp->func(s, m))
02381                   ret = -1;
02382             } else
02383                astman_send_error(s, m, "Permission denied");
02384             break;
02385          }
02386          ast_rwlock_unlock(&actionlock);
02387          if (!tmp)
02388             astman_send_error(s, m, "Invalid/unknown command");
02389       }
02390    }
02391    if (ret)
02392       return ret;
02393    return process_events(s);
02394 }
02395 
02396 static int get_input(struct mansession *s, char *output)
02397 {
02398    /* output must have at least sizeof(s->inbuf) space */
02399    int res;
02400    int x;
02401    struct pollfd fds[1];
02402    for (x = 1; x < s->inlen; x++) {
02403       if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
02404          /* Copy output data up to and including \r\n */
02405          memcpy(output, s->inbuf, x + 1);
02406          /* Add trailing \0 */
02407          output[x+1] = '\0';
02408          /* Move remaining data back to the front */
02409          memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
02410          s->inlen -= (x + 1);
02411          return 1;
02412       }
02413    } 
02414    if (s->inlen >= sizeof(s->inbuf) - 1) {
02415       ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
02416       s->inlen = 0;
02417    }
02418    fds[0].fd = s->fd;
02419    fds[0].events = POLLIN;
02420    do {
02421       ast_mutex_lock(&s->__lock);
02422       if (s->pending_event) {
02423          s->pending_event = 0;
02424          ast_mutex_unlock(&s->__lock);
02425          return 0;
02426       }
02427       s->waiting_thread = pthread_self();
02428       ast_mutex_unlock(&s->__lock);
02429 
02430       res = poll(fds, 1, -1);
02431 
02432       ast_mutex_lock(&s->__lock);
02433       s->waiting_thread = AST_PTHREADT_NULL;
02434       ast_mutex_unlock(&s->__lock);
02435       if (res < 0) {
02436          if (errno == EINTR || errno == EAGAIN) {
02437             return 0;
02438          }
02439          ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
02440          return -1;
02441       } else if (res > 0) {
02442          ast_mutex_lock(&s->__lock);
02443          res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
02444          ast_mutex_unlock(&s->__lock);
02445          if (res < 1)
02446             return -1;
02447          break;
02448       }
02449    } while(1);
02450    s->inlen += res;
02451    s->inbuf[s->inlen] = '\0';
02452    return 0;
02453 }
02454 
02455 static int do_message(struct mansession *s)
02456 {
02457    struct message m = { 0 };
02458    char header_buf[sizeof(s->inbuf)] = { '\0' };
02459    int res;
02460 
02461    for (;;) {
02462       /* Check if any events are pending and do them if needed */
02463       if (s->eventq->next) {
02464          if (process_events(s))
02465             return -1;
02466       }
02467       res = get_input(s, header_buf);
02468       if (res == 0) {
02469          continue;
02470       } else if (res > 0) {
02471          /* Strip trailing \r\n */
02472          if (strlen(header_buf) < 2)
02473             continue;
02474          header_buf[strlen(header_buf) - 2] = '\0';
02475          if (ast_strlen_zero(header_buf))
02476             return process_message(s, &m) ? -1 : 0;
02477          else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
02478             m.headers[m.hdrcount++] = ast_strdupa(header_buf);
02479       } else {
02480          return res;
02481       }
02482    }
02483 }
02484 
02485 static void *session_do(void *data)
02486 {
02487    struct mansession *s = data;
02488    int res;
02489    
02490    astman_append(s, "Asterisk Call Manager/1.0\r\n");
02491    for (;;) {
02492       if ((res = do_message(s)) < 0)
02493          break;
02494    }
02495    if (s->authenticated) {
02496       if (option_verbose > 1) {
02497          if (displayconnects) 
02498             ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
02499       }
02500       ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
02501    } else {
02502       if (option_verbose > 1) {
02503          if (displayconnects)
02504             ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
02505       }
02506       ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
02507    }
02508 
02509    /* It is possible under certain circumstances for this session thread
02510       to complete its work and exit *before* the thread that created it
02511       has finished executing the ast_pthread_create_background() function.
02512       If this occurs, some versions of glibc appear to act in a buggy
02513       fashion and attempt to write data into memory that it thinks belongs
02514       to the thread but is in fact not owned by the thread (or may have
02515       been freed completely).
02516 
02517       Causing this thread to yield to other threads at least one time
02518       appears to work around this bug.
02519    */
02520    usleep(1);
02521 
02522    destroy_session(s);
02523    return NULL;
02524 }
02525 
02526 static void *accept_thread(void *ignore)
02527 {
02528    int as;
02529    struct sockaddr_in sin;
02530    socklen_t sinlen;
02531    struct eventqent *eqe;
02532    struct mansession *s;
02533    struct protoent *p;
02534    int arg = 1;
02535    int flags;
02536    pthread_attr_t attr;
02537    time_t now;
02538    struct pollfd pfds[1];
02539 
02540    pthread_attr_init(&attr);
02541    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02542 
02543    for (;;) {
02544       time(&now);
02545       AST_LIST_LOCK(&sessions);
02546       AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
02547          if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
02548             AST_LIST_REMOVE_CURRENT(&sessions, list);
02549             num_sessions--;
02550             if (s->authenticated && (option_verbose > 1) && displayconnects) {
02551                ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
02552                   s->username, ast_inet_ntoa(s->sin.sin_addr));
02553             }
02554             free_session(s);
02555             break;   
02556          }
02557       }
02558       AST_LIST_TRAVERSE_SAFE_END
02559       /* Purge master event queue of old, unused events, but make sure we
02560          always keep at least one in the queue */
02561       eqe = master_eventq;
02562       while (master_eventq->next && !master_eventq->usecount) {
02563          eqe = master_eventq;
02564          master_eventq = master_eventq->next;
02565          free(eqe);
02566       }
02567       AST_LIST_UNLOCK(&sessions);
02568 
02569       sinlen = sizeof(sin);
02570       pfds[0].fd = asock;
02571       pfds[0].events = POLLIN;
02572       /* Wait for something to happen, but timeout every few seconds so
02573          we can ditch any old manager sessions */
02574       if (poll(pfds, 1, 5000) < 1)
02575          continue;
02576       as = accept(asock, (struct sockaddr *)&sin, &sinlen);
02577       if (as < 0) {
02578          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
02579          continue;
02580       }
02581       p = getprotobyname("tcp");
02582       if (p) {
02583          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
02584             ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
02585          }
02586       }
02587       if (!(s = ast_calloc(1, sizeof(*s))))
02588          continue;
02589 
02590       memcpy(&s->sin, &sin, sizeof(sin));
02591       s->writetimeout = 100;
02592       s->waiting_thread = AST_PTHREADT_NULL;
02593 
02594       if (!block_sockets) {
02595          /* For safety, make sure socket is non-blocking */
02596          flags = fcntl(as, F_GETFL);
02597          fcntl(as, F_SETFL, flags | O_NONBLOCK);
02598       } else {
02599          flags = fcntl(as, F_GETFL);
02600          fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
02601       }
02602       ast_mutex_init(&s->__lock);
02603       s->fd = as;
02604       s->send_events = -1;
02605       AST_LIST_LOCK(&sessions);
02606       AST_LIST_INSERT_HEAD(&sessions, s, list);
02607       num_sessions++;
02608       /* Find the last place in the master event queue and hook ourselves
02609          in there */
02610       s->eventq = master_eventq;
02611       while(s->eventq->next)
02612          s->eventq = s->eventq->next;
02613       ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
02614       AST_LIST_UNLOCK(&sessions);
02615       if (ast_pthread_create_background(&s->t, &attr, session_do, s))
02616          destroy_session(s);
02617    }
02618    pthread_attr_destroy(&attr);
02619    return NULL;
02620 }
02621 
02622 static int append_event(const char *str, int category)
02623 {
02624    struct eventqent *tmp, *prev = NULL;
02625    tmp = ast_malloc(sizeof(*tmp) + strlen(str));
02626 
02627    if (!tmp)
02628       return -1;
02629 
02630    tmp->next = NULL;
02631    tmp->category = category;
02632    strcpy(tmp->eventdata, str);
02633    
02634    if (master_eventq) {
02635       prev = master_eventq;
02636       while (prev->next) 
02637          prev = prev->next;
02638       prev->next = tmp;
02639    } else {
02640       master_eventq = tmp;
02641    }
02642    
02643    tmp->usecount = num_sessions;
02644    
02645    return 0;
02646 }
02647 
02648 /*! \brief  manager_event: Send AMI event to client */
02649 int manager_event(int category, const char *event, const char *fmt, ...)
02650 {
02651    struct mansession *s;
02652    char auth[80];
02653    va_list ap;
02654    struct timeval now;
02655    struct ast_dynamic_str *buf;
02656 
02657    /* Abort if there aren't any manager sessions */
02658    if (!num_sessions)
02659       return 0;
02660 
02661    if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
02662       return -1;
02663 
02664    ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
02665          "Event: %s\r\nPrivilege: %s\r\n",
02666           event, authority_to_str(category, auth, sizeof(auth)));
02667 
02668    if (timestampevents) {
02669       now = ast_tvnow();
02670       ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
02671             "Timestamp: %ld.%06lu\r\n",
02672              now.tv_sec, (unsigned long) now.tv_usec);
02673    }
02674 
02675    va_start(ap, fmt);
02676    ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
02677    va_end(ap);
02678    
02679    ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");  
02680    
02681    /* Append event to master list and wake up any sleeping sessions */
02682    AST_LIST_LOCK(&sessions);
02683    append_event(buf->str, category);
02684    AST_LIST_TRAVERSE(&sessions, s, list) {
02685       ast_mutex_lock(&s->__lock);
02686       if (s->waiting_thread != AST_PTHREADT_NULL)
02687          pthread_kill(s->waiting_thread, SIGURG);
02688       else
02689          /* We have an event to process, but the mansession is
02690           * not waiting for it. We still need to indicate that there
02691           * is an event waiting so that get_input processes the pending
02692           * event instead of polling.
02693           */
02694          s->pending_event = 1;
02695       ast_mutex_unlock(&s->__lock);
02696    }
02697    AST_LIST_UNLOCK(&sessions);
02698 
02699    return 0;
02700 }
02701 
02702 int ast_manager_unregister(char *action) 
02703 {
02704    struct manager_action *cur, *prev;
02705 
02706    ast_rwlock_wrlock(&actionlock);
02707    cur = prev = first_action;
02708    while (cur) {
02709       if (!strcasecmp(action, cur->action)) {
02710          prev->next = cur->next;
02711          free(cur);
02712          if (option_verbose > 1) 
02713             ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
02714          ast_rwlock_unlock(&actionlock);
02715          return 0;
02716       }
02717       prev = cur;
02718       cur = cur->next;
02719    }
02720    ast_rwlock_unlock(&actionlock);
02721    return 0;
02722 }
02723 
02724 static int manager_state_cb(char *context, char *exten, int state, void *data)
02725 {
02726    /* Notify managers of change */
02727    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
02728    return 0;
02729 }
02730 
02731 static int ast_manager_register_struct(struct manager_action *act)
02732 {
02733    struct manager_action *cur, *prev = NULL;
02734    int ret;
02735 
02736    ast_rwlock_wrlock(&actionlock);
02737    cur = first_action;
02738    while (cur) { /* Walk the list of actions */
02739       ret = strcasecmp(cur->action, act->action);
02740       if (ret == 0) {
02741          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
02742          ast_rwlock_unlock(&actionlock);
02743          return -1;
02744       } else if (ret > 0) {
02745          /* Insert these alphabetically */
02746          if (prev) {
02747             act->next = prev->next;
02748             prev->next = act;
02749          } else {
02750             act->next = first_action;
02751             first_action = act;
02752          }
02753          break;
02754       }
02755       prev = cur; 
02756       cur = cur->next;
02757    }
02758    
02759    if (!cur) {
02760       if (prev)
02761          prev->next = act;
02762       else
02763          first_action = act;
02764       act->next = NULL;
02765    }
02766 
02767    if (option_verbose > 1) 
02768       ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
02769    ast_rwlock_unlock(&actionlock);
02770    return 0;
02771 }
02772 
02773 /*! \brief register a new command with manager, including online help. This is 
02774    the preferred way to register a manager command */
02775 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
02776 {
02777    struct manager_action *cur;
02778 
02779    cur = ast_malloc(sizeof(*cur));
02780    if (!cur)
02781       return -1;
02782    
02783    cur->action = action;
02784    cur->authority = auth;
02785    cur->func = func;
02786    cur->synopsis = synopsis;
02787    cur->description = description;
02788    cur->next = NULL;
02789 
02790    ast_manager_register_struct(cur);
02791 
02792    return 0;
02793 }
02794 /*! @}
02795  END Doxygen group */
02796 
02797 static struct mansession *find_session(uint32_t ident)
02798 {
02799    struct mansession *s;
02800 
02801    AST_LIST_LOCK(&sessions);
02802    AST_LIST_TRAVERSE(&sessions, s, list) {
02803       ast_mutex_lock(&s->__lock);
02804       if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
02805          s->inuse++;
02806          break;
02807       }
02808       ast_mutex_unlock(&s->__lock);
02809    }
02810    AST_LIST_UNLOCK(&sessions);
02811 
02812    return s;
02813 }
02814 
02815 int astman_verify_session_readpermissions(uint32_t ident, int perm)
02816 {
02817    int result = 0;
02818    struct mansession *s;
02819 
02820    AST_LIST_LOCK(&sessions);
02821    AST_LIST_TRAVERSE(&sessions, s, list) {
02822       ast_mutex_lock(&s->__lock);
02823       if ((s->managerid == ident) && (s->readperm & perm)) {
02824          result = 1;
02825          ast_mutex_unlock(&s->__lock);
02826          break;
02827       }
02828       ast_mutex_unlock(&s->__lock);
02829    }
02830    AST_LIST_UNLOCK(&sessions);
02831    return result;
02832 }
02833 
02834 int astman_verify_session_writepermissions(uint32_t ident, int perm)
02835 {
02836    int result = 0;
02837    struct mansession *s;
02838 
02839    AST_LIST_LOCK(&sessions);
02840    AST_LIST_TRAVERSE(&sessions, s, list) {
02841       ast_mutex_lock(&s->__lock);
02842       if ((s->managerid == ident) && (s->writeperm & perm)) {
02843          result = 1;
02844          ast_mutex_unlock(&s->__lock);
02845          break;
02846       }
02847       ast_mutex_unlock(&s->__lock);
02848    }
02849    AST_LIST_UNLOCK(&sessions);
02850    return result;
02851 }
02852 
02853 enum {
02854    FORMAT_RAW,
02855    FORMAT_HTML,
02856    FORMAT_XML,
02857 };
02858 static char *contenttype[] = { "plain", "html", "xml" };
02859 
02860 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
02861 {
02862    struct mansession *s = NULL;
02863    uint32_t ident = 0;
02864    char workspace[512];
02865    char cookie[128];
02866    size_t len = sizeof(workspace);
02867    int blastaway = 0;
02868    char *c = workspace;
02869    char *retval = NULL;
02870    struct ast_variable *v;
02871 
02872    for (v = params; v; v = v->next) {
02873       if (!strcasecmp(v->name, "mansession_id")) {
02874          sscanf(v->value, "%x", &ident);
02875          break;
02876       }
02877    }
02878    
02879    if (!(s = find_session(ident))) {
02880       /* Create new session */
02881       if (!(s = ast_calloc(1, sizeof(*s)))) {
02882          *status = 500;
02883          goto generic_callback_out;
02884       }
02885       memcpy(&s->sin, requestor, sizeof(s->sin));
02886       s->fd = -1;
02887       s->waiting_thread = AST_PTHREADT_NULL;
02888       s->send_events = 0;
02889       ast_mutex_init(&s->__lock);
02890       ast_mutex_lock(&s->__lock);
02891       s->inuse = 1;
02892       /*!\note There is approximately a 1 in 1.8E19 chance that the following
02893        * calculation will produce 0, which is an invalid ID, but due to the
02894        * properties of the rand() function (and the constantcy of s), that
02895        * won't happen twice in a row.
02896        */
02897       while ((s->managerid = rand() ^ (unsigned long) s) == 0);
02898       AST_LIST_LOCK(&sessions);
02899       AST_LIST_INSERT_HEAD(&sessions, s, list);
02900       /* Hook into the last spot in the event queue */
02901       s->eventq = master_eventq;
02902       while (s->eventq->next)
02903          s->eventq = s->eventq->next;
02904       ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
02905       ast_atomic_fetchadd_int(&num_sessions, 1);
02906       AST_LIST_UNLOCK(&sessions);
02907    }
02908 
02909    /* Reset HTTP timeout.  If we're not yet authenticated, keep it extremely short */
02910    time(&s->sessiontimeout);
02911    if (!s->authenticated && (httptimeout > 5))
02912       s->sessiontimeout += 5;
02913    else
02914       s->sessiontimeout += httptimeout;
02915    ast_mutex_unlock(&s->__lock);
02916    
02917    if (s) {
02918       struct message m = { 0 };
02919       char tmp[80];
02920       unsigned int x;
02921       size_t hdrlen;
02922 
02923       for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
02924          hdrlen = strlen(v->name) + strlen(v->value) + 3;
02925          m.headers[m.hdrcount] = alloca(hdrlen);
02926          snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
02927          m.hdrcount = x + 1;
02928       }
02929 
02930       if (process_message(s, &m)) {
02931          if (s->authenticated) {
02932             if (option_verbose > 1) {
02933                if (displayconnects) 
02934                   ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));    
02935             }
02936             ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
02937          } else {
02938             if (option_verbose > 1) {
02939                if (displayconnects)
02940                   ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
02941             }
02942             ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
02943          }
02944          s->needdestroy = 1;
02945       }
02946       ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
02947       sprintf(tmp, "%08x", s->managerid);
02948       ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
02949       if (format == FORMAT_HTML)
02950          ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Interface</title>");
02951       if (format == FORMAT_XML) {
02952          ast_build_string(&c, &len, "<ajax-response>\n");
02953       } else if (format == FORMAT_HTML) {
02954          ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
02955          ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
02956       }
02957       ast_mutex_lock(&s->__lock);
02958       if (s->outputstr) {
02959          char *tmp;
02960          if (format == FORMAT_XML)
02961             tmp = xml_translate(s->outputstr->str, params);
02962          else if (format == FORMAT_HTML)
02963             tmp = html_translate(s->outputstr->str);
02964          else
02965             tmp = s->outputstr->str;
02966          if (tmp) {
02967             retval = malloc(strlen(workspace) + strlen(tmp) + 128);
02968             if (retval) {
02969                strcpy(retval, workspace);
02970                strcpy(retval + strlen(retval), tmp);
02971                c = retval + strlen(retval);
02972                len = 120;
02973             }
02974          }
02975          if (tmp != s->outputstr->str)
02976             free(tmp);
02977          free(s->outputstr);
02978          s->outputstr = NULL;
02979       }
02980       ast_mutex_unlock(&s->__lock);
02981       /* Still okay because c would safely be pointing to workspace even
02982          if retval failed to allocate above */
02983       if (format == FORMAT_XML) {
02984          ast_build_string(&c, &len, "</ajax-response>\n");
02985       } else if (format == FORMAT_HTML)
02986          ast_build_string(&c, &len, "</table></body>\r\n");
02987    } else {
02988       *status = 500;
02989       *title = strdup("Server Error");
02990    }
02991    ast_mutex_lock(&s->__lock);
02992    if (s->needdestroy) {
02993       if (s->inuse == 1) {
02994          ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
02995          blastaway = 1;
02996       } else {
02997          ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
02998          if (s->waiting_thread != AST_PTHREADT_NULL)
02999             pthread_kill(s->waiting_thread, SIGURG);
03000          s->inuse--;
03001       }
03002    } else
03003       s->inuse--;
03004    ast_mutex_unlock(&s->__lock);
03005    
03006    if (blastaway)
03007       destroy_session(s);
03008 generic_callback_out:
03009    if (*status != 200)
03010       return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); 
03011    return retval;
03012 }
03013 
03014 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03015 {
03016    return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
03017 }
03018 
03019 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03020 {
03021    return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
03022 }
03023 
03024 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03025 {
03026    return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
03027 }
03028 
03029 struct ast_http_uri rawmanuri = {
03030    .description = "Raw HTTP Manager Event Interface",
03031    .uri = "rawman",
03032    .has_subtree = 0,
03033    .callback = rawman_http_callback,
03034 };
03035 
03036 struct ast_http_uri manageruri = {
03037    .description = "HTML Manager Event Interface",
03038    .uri = "manager",
03039    .has_subtree = 0,
03040    .callback = manager_http_callback,
03041 };
03042 
03043 struct ast_http_uri managerxmluri = {
03044    .description = "XML Manager Event Interface",
03045    .uri = "mxml",
03046    .has_subtree = 0,
03047    .callback = mxml_http_callback,
03048 };
03049 
03050 static int registered = 0;
03051 static int webregged = 0;
03052 
03053 int init_manager(void)
03054 {
03055    struct ast_config *cfg = NULL, *ucfg = NULL;
03056    const char *val;
03057    char *cat = NULL;
03058    int oldportno = portno;
03059    static struct sockaddr_in ba;
03060    int x = 1;
03061    int flags;
03062    int newhttptimeout = 60;
03063    struct ast_manager_user *user = NULL;
03064 
03065    if (!registered) {
03066       /* Register default actions */
03067       ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
03068       ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
03069       ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
03070       ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
03071       ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
03072       ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
03073       ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
03074       ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
03075       ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
03076       ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
03077       ast_manager_register2("Atxfer", EVENT_FLAG_CALL, action_atxfer, "Attended transfer", mandescr_atxfer );
03078       ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
03079       ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
03080       ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
03081       ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
03082       ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
03083       ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
03084       ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
03085       ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
03086       ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
03087       ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
03088       ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
03089 
03090       ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
03091       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
03092       registered = 1;
03093       /* Append placeholder event so master_eventq never runs dry */
03094       append_event("Event: Placeholder\r\n\r\n", 0);
03095    }
03096    portno = DEFAULT_MANAGER_PORT;
03097    displayconnects = 1;
03098    cfg = ast_config_load("manager.conf");
03099    if (!cfg) {
03100       ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
03101       return 0;
03102    }
03103    if ((val = ast_variable_retrieve(cfg, "general", "enabled"))) {
03104       manager_enabled = ast_true(val);
03105    }
03106    if ((val = ast_variable_retrieve(cfg, "general", "block-sockets"))) {
03107       block_sockets = ast_true(val);
03108    }
03109    if((val = ast_variable_retrieve(cfg, "general", "webenabled"))) {
03110       webmanager_enabled = ast_true(val);
03111    }
03112    if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
03113       if (sscanf(val, "%d", &portno) != 1) {
03114          ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
03115          portno = DEFAULT_MANAGER_PORT;
03116       }
03117    }
03118    if ((val = ast_variable_retrieve(cfg, "general", "displayconnects"))) {
03119       displayconnects = ast_true(val);
03120    }
03121    if ((val = ast_variable_retrieve(cfg, "general", "timestampevents"))) {
03122       timestampevents = ast_true(val);
03123    }
03124    if ((val = ast_variable_retrieve(cfg, "general", "httptimeout"))) {
03125       newhttptimeout = atoi(val);
03126    }
03127 
03128    memset(&ba, 0, sizeof(ba));
03129    ba.sin_family = AF_INET;
03130    ba.sin_port = htons(portno);
03131 
03132    if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
03133       if (!inet_aton(val, &ba.sin_addr)) { 
03134          ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
03135          memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
03136       }
03137    }
03138    
03139 
03140    if ((asock > -1) && ((portno != oldportno) || !manager_enabled)) {
03141 #if 0
03142       /* Can't be done yet */
03143       close(asock);
03144       asock = -1;
03145 #else
03146       ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
03147 #endif
03148    }
03149 
03150    AST_LIST_LOCK(&users);
03151 
03152    if ((ucfg = ast_config_load("users.conf"))) {
03153       while ((cat = ast_category_browse(ucfg, cat))) {
03154          int hasmanager = 0;
03155          struct ast_variable *var = NULL;
03156 
03157          if (!strcasecmp(cat, "general")) {
03158             continue;
03159          }
03160 
03161          if (!(hasmanager = ast_true(ast_variable_retrieve(ucfg, cat, "hasmanager")))) {
03162             continue;
03163          }
03164 
03165          /* Look for an existing entry, if none found - create one and add it to the list */
03166          if (!(user = ast_get_manager_by_name_locked(cat))) {
03167             if (!(user = ast_calloc(1, sizeof(*user)))) {
03168                break;
03169             }
03170             /* Copy name over */
03171             ast_copy_string(user->username, cat, sizeof(user->username));
03172             /* Insert into list */
03173             AST_LIST_INSERT_TAIL(&users, user, list);
03174          }
03175 
03176          /* Make sure we keep this user and don't destroy it during cleanup */
03177          user->keep = 1;
03178 
03179          for (var = ast_variable_browse(ucfg, cat); var; var = var->next) {
03180             if (!strcasecmp(var->name, "secret")) {
03181                if (user->secret) {
03182                   free(user->secret);
03183                }
03184                user->secret = ast_strdup(var->value);
03185             } else if (!strcasecmp(var->name, "deny") ) {
03186                if (user->deny) {
03187                   free(user->deny);
03188                }
03189                user->deny = ast_strdup(var->value);
03190             } else if (!strcasecmp(var->name, "permit") ) {
03191                if (user->permit) {
03192                   free(user->permit);
03193                }
03194                user->permit = ast_strdup(var->value);
03195             } else if (!strcasecmp(var->name, "read") ) {
03196                if (user->read) {
03197                   free(user->read);
03198                }
03199                user->read = ast_strdup(var->value);
03200             } else if (!strcasecmp(var->name, "write") ) {
03201                if (user->write) {
03202                   free(user->write);
03203                }
03204                user->write = ast_strdup(var->value);
03205             } else if (!strcasecmp(var->name, "displayconnects") ) {
03206                user->displayconnects = ast_true(var->value);
03207             } else if (!strcasecmp(var->name, "hasmanager")) {
03208                /* already handled */
03209             } else {
03210                ast_log(LOG_DEBUG, "%s is an unknown option (to the manager module).\n", var->name);
03211             }
03212          }
03213       }
03214       ast_config_destroy(ucfg);
03215    }
03216 
03217    while ((cat = ast_category_browse(cfg, cat))) {
03218       struct ast_variable *var = NULL;
03219 
03220       if (!strcasecmp(cat, "general"))
03221          continue;
03222 
03223       /* Look for an existing entry, if none found - create one and add it to the list */
03224       if (!(user = ast_get_manager_by_name_locked(cat))) {
03225          if (!(user = ast_calloc(1, sizeof(*user))))
03226             break;
03227          /* Copy name over */
03228          ast_copy_string(user->username, cat, sizeof(user->username));
03229          /* Insert into list */
03230          AST_LIST_INSERT_TAIL(&users, user, list);
03231       }
03232 
03233       /* Make sure we keep this user and don't destroy it during cleanup */
03234       user->keep = 1;
03235 
03236       var = ast_variable_browse(cfg, cat);
03237       while (var) {
03238          if (!strcasecmp(var->name, "secret")) {
03239             if (user->secret)
03240                free(user->secret);
03241             user->secret = ast_strdup(var->value);
03242          } else if (!strcasecmp(var->name, "deny") ) {
03243             if (user->deny)
03244                free(user->deny);
03245             user->deny = ast_strdup(var->value);
03246          } else if (!strcasecmp(var->name, "permit") ) {
03247             if (user->permit)
03248                free(user->permit);
03249             user->permit = ast_strdup(var->value);
03250          }  else if (!strcasecmp(var->name, "read") ) {
03251             if (user->read)
03252                free(user->read);
03253             user->read = ast_strdup(var->value);
03254          }  else if (!strcasecmp(var->name, "write") ) {
03255             if (user->write)
03256                free(user->write);
03257             user->write = ast_strdup(var->value);
03258          }  else if (!strcasecmp(var->name, "displayconnects") )
03259             user->displayconnects = ast_true(var->value);
03260          else
03261             ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
03262          var = var->next;
03263       }
03264    }
03265 
03266    /* Perform cleanup - essentially prune out old users that no longer exist */
03267    AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
03268       if (user->keep) {
03269          user->keep = 0;
03270          continue;
03271       }
03272       /* We do not need to keep this user so take them out of the list */
03273       AST_LIST_REMOVE_CURRENT(&users, list);
03274       /* Free their memory now */
03275       if (user->secret)
03276          free(user->secret);
03277       if (user->deny)
03278          free(user->deny);
03279       if (user->permit)
03280          free(user->permit);
03281       if (user->read)
03282          free(user->read);
03283       if (user->write)
03284          free(user->write);
03285       free(user);
03286    }
03287    AST_LIST_TRAVERSE_SAFE_END
03288 
03289    AST_LIST_UNLOCK(&users);
03290 
03291    ast_config_destroy(cfg);
03292    
03293    if (webmanager_enabled && manager_enabled) {
03294       if (!webregged) {
03295          ast_http_uri_link(&rawmanuri);
03296          ast_http_uri_link(&manageruri);
03297          ast_http_uri_link(&managerxmluri);
03298          webregged = 1;
03299       }
03300    } else {
03301       if (webregged) {
03302          ast_http_uri_unlink(&rawmanuri);
03303          ast_http_uri_unlink(&manageruri);
03304          ast_http_uri_unlink(&managerxmluri);
03305          webregged = 0;
03306       }
03307    }
03308 
03309    if (newhttptimeout > 0)
03310       httptimeout = newhttptimeout;
03311 
03312    /* If not enabled, do nothing */
03313    if (!manager_enabled)
03314       return 0;
03315 
03316    if (asock < 0) {
03317       asock = socket(AF_INET, SOCK_STREAM, 0);
03318       if (asock < 0) {
03319          ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
03320          return -1;
03321       }
03322       setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
03323       if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
03324          ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
03325          close(asock);
03326          asock = -1;
03327          return -1;
03328       }
03329       if (listen(asock, 2)) {
03330          ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
03331          close(asock);
03332          asock = -1;
03333          return -1;
03334       }
03335       flags = fcntl(asock, F_GETFL);
03336       fcntl(asock, F_SETFL, flags | O_NONBLOCK);
03337       if (option_verbose)
03338          ast_verbose("Asterisk Management interface listening on port %d\n", portno);
03339       ast_pthread_create_background(&t, NULL, accept_thread, NULL);
03340    }
03341    return 0;
03342 }
03343 
03344 int reload_manager(void)
03345 {
03346    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
03347    return init_manager();
03348 }

Generated on Thu May 14 14:49:05 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7