Mon Mar 31 07:38:03 2008

Asterisk developer's documentation


manager.c

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

Generated on Mon Mar 31 07:38:03 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1