Fri Aug 24 02:22:16 2007

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

Generated on Fri Aug 24 02:22:16 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1