Sat Apr 12 07:12:25 2008

Asterisk developer's documentation


manager.c

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

Generated on Sat Apr 12 07:12:25 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.5