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

Generated on Mon Apr 30 07:36:35 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1