Mon May 14 04:42:59 2007

Asterisk developer's documentation


manager.c

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

Generated on Mon May 14 04:42:59 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1