Thu May 24 14:21:11 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 - 2005, 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  * Channel Management and more
00024  * 
00025  * \ref amiconf
00026  */
00027 
00028 /*! \addtogroup Group_AMI AMI functions 
00029 */
00030 /*! @{ 
00031  Doxygen group */
00032 
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include <sys/time.h>
00037 #include <sys/types.h>
00038 #include <netdb.h>
00039 #include <sys/socket.h>
00040 #include <netinet/in.h>
00041 #include <netinet/tcp.h>
00042 #include <arpa/inet.h>
00043 #include <signal.h>
00044 #include <errno.h>
00045 #include <unistd.h>
00046 
00047 #include "asterisk.h"
00048 
00049 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 9581 $")
00050 
00051 #include "asterisk/channel.h"
00052 #include "asterisk/file.h"
00053 #include "asterisk/manager.h"
00054 #include "asterisk/config.h"
00055 #include "asterisk/callerid.h"
00056 #include "asterisk/lock.h"
00057 #include "asterisk/logger.h"
00058 #include "asterisk/options.h"
00059 #include "asterisk/cli.h"
00060 #include "asterisk/app.h"
00061 #include "asterisk/pbx.h"
00062 #include "asterisk/md5.h"
00063 #include "asterisk/acl.h"
00064 #include "asterisk/utils.h"
00065 
00066 struct fast_originate_helper {
00067    char tech[AST_MAX_MANHEADER_LEN];
00068    char data[AST_MAX_MANHEADER_LEN];
00069    int timeout;
00070    char app[AST_MAX_APP];
00071    char appdata[AST_MAX_MANHEADER_LEN];
00072    char cid_name[AST_MAX_MANHEADER_LEN];
00073    char cid_num[AST_MAX_MANHEADER_LEN];
00074    char context[AST_MAX_CONTEXT];
00075    char exten[AST_MAX_EXTENSION];
00076    char idtext[AST_MAX_MANHEADER_LEN];
00077    char account[AST_MAX_ACCOUNT_CODE];
00078    int priority;
00079    struct ast_variable *vars;
00080 };
00081 
00082 static int enabled = 0;
00083 static int portno = DEFAULT_MANAGER_PORT;
00084 static int asock = -1;
00085 static int displayconnects = 1;
00086 
00087 static pthread_t t;
00088 AST_MUTEX_DEFINE_STATIC(sessionlock);
00089 static int block_sockets = 0;
00090 
00091 static struct permalias {
00092    int num;
00093    char *label;
00094 } perms[] = {
00095    { EVENT_FLAG_SYSTEM, "system" },
00096    { EVENT_FLAG_CALL, "call" },
00097    { EVENT_FLAG_LOG, "log" },
00098    { EVENT_FLAG_VERBOSE, "verbose" },
00099    { EVENT_FLAG_COMMAND, "command" },
00100    { EVENT_FLAG_AGENT, "agent" },
00101    { EVENT_FLAG_USER, "user" },
00102    { -1, "all" },
00103    { 0, "none" },
00104 };
00105 
00106 static struct mansession *sessions = NULL;
00107 static struct manager_action *first_action = NULL;
00108 AST_MUTEX_DEFINE_STATIC(actionlock);
00109 
00110 /*! If you are calling ast_carefulwrite, it is assumed that you are calling
00111    it on a file descriptor that _DOES_ have NONBLOCK set.  This way,
00112    there is only one system call made to do a write, unless we actually
00113    have a need to wait.  This way, we get better performance. */
00114 int ast_carefulwrite(int fd, char *s, int len, int timeoutms) 
00115 {
00116    /* Try to write string, but wait no more than ms milliseconds
00117       before timing out */
00118    int res=0;
00119    struct pollfd fds[1];
00120    while(len) {
00121       res = write(fd, s, len);
00122       if ((res < 0) && (errno != EAGAIN)) {
00123          return -1;
00124       }
00125       if (res < 0) res = 0;
00126       len -= res;
00127       s += res;
00128       res = 0;
00129       if (len) {
00130          fds[0].fd = fd;
00131          fds[0].events = POLLOUT;
00132          /* Wait until writable again */
00133          res = poll(fds, 1, timeoutms);
00134          if (res < 1)
00135             return -1;
00136       }
00137    }
00138    return res;
00139 }
00140 
00141 /*! authority_to_str: Convert authority code to string with serveral options */
00142 static char *authority_to_str(int authority, char *res, int reslen)
00143 {
00144    int running_total = 0, i;
00145    memset(res, 0, reslen);
00146    for (i=0; i<sizeof(perms) / sizeof(perms[0]) - 1; i++) {
00147       if (authority & perms[i].num) {
00148          if (*res) {
00149             strncat(res, ",", (reslen > running_total) ? reslen - running_total : 0);
00150             running_total++;
00151          }
00152          strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total : 0);
00153          running_total += strlen(perms[i].label);
00154       }
00155    }
00156    if (ast_strlen_zero(res)) {
00157       ast_copy_string(res, "<none>", reslen);
00158    }
00159    return res;
00160 }
00161 
00162 static char *complete_show_mancmd(char *line, char *word, int pos, int state)
00163 {
00164    struct manager_action *cur = first_action;
00165    int which = 0;
00166 
00167    ast_mutex_lock(&actionlock);
00168    while (cur) { /* Walk the list of actions */
00169       if (!strncasecmp(word, cur->action, strlen(word))) {
00170          if (++which > state) {
00171             char *ret = strdup(cur->action);
00172             ast_mutex_unlock(&actionlock);
00173             return ret;
00174          }
00175       }
00176       cur = cur->next;
00177    }
00178    ast_mutex_unlock(&actionlock);
00179    return NULL;
00180 }
00181 
00182 static int handle_showmancmd(int fd, int argc, char *argv[])
00183 {
00184    struct manager_action *cur = first_action;
00185    char authority[80];
00186    int num;
00187 
00188    if (argc != 4)
00189       return RESULT_SHOWUSAGE;
00190    ast_mutex_lock(&actionlock);
00191    while (cur) { /* Walk the list of actions */
00192       for (num = 3; num < argc; num++) {
00193          if (!strcasecmp(cur->action, argv[num])) {
00194             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 : "");
00195          }
00196       }
00197       cur = cur->next;
00198    }
00199 
00200    ast_mutex_unlock(&actionlock);
00201    return RESULT_SUCCESS;
00202 }
00203 
00204 /*! \brief  handle_showmancmds: CLI command */
00205 /* Should change to "manager show commands" */
00206 static int handle_showmancmds(int fd, int argc, char *argv[])
00207 {
00208    struct manager_action *cur = first_action;
00209    char authority[80];
00210    char *format = "  %-15.15s  %-15.15s  %-55.55s\n";
00211 
00212    ast_mutex_lock(&actionlock);
00213    ast_cli(fd, format, "Action", "Privilege", "Synopsis");
00214    ast_cli(fd, format, "------", "---------", "--------");
00215    while (cur) { /* Walk the list of actions */
00216       ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
00217       cur = cur->next;
00218    }
00219 
00220    ast_mutex_unlock(&actionlock);
00221    return RESULT_SUCCESS;
00222 }
00223 
00224 /*! \brief  handle_showmanconn: CLI command show manager connected */
00225 /* Should change to "manager show connected" */
00226 static int handle_showmanconn(int fd, int argc, char *argv[])
00227 {
00228    struct mansession *s;
00229    char iabuf[INET_ADDRSTRLEN];
00230    char *format = "  %-15.15s  %-15.15s\n";
00231    ast_mutex_lock(&sessionlock);
00232    s = sessions;
00233    ast_cli(fd, format, "Username", "IP Address");
00234    while (s) {
00235       ast_cli(fd, format,s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
00236       s = s->next;
00237    }
00238 
00239    ast_mutex_unlock(&sessionlock);
00240    return RESULT_SUCCESS;
00241 }
00242 
00243 static char showmancmd_help[] = 
00244 "Usage: show manager command <actionname>\n"
00245 "  Shows the detailed description for a specific Asterisk manager interface command.\n";
00246 
00247 static char showmancmds_help[] = 
00248 "Usage: show manager commands\n"
00249 "  Prints a listing of all the available Asterisk manager interface commands.\n";
00250 
00251 static char showmanconn_help[] = 
00252 "Usage: show manager connected\n"
00253 "  Prints a listing of the users that are currently connected to the\n"
00254 "Asterisk manager interface.\n";
00255 
00256 static struct ast_cli_entry show_mancmd_cli =
00257    { { "show", "manager", "command", NULL },
00258    handle_showmancmd, "Show a manager interface command", showmancmd_help, complete_show_mancmd };
00259 
00260 static struct ast_cli_entry show_mancmds_cli =
00261    { { "show", "manager", "commands", NULL },
00262    handle_showmancmds, "List manager interface commands", showmancmds_help };
00263 
00264 static struct ast_cli_entry show_manconn_cli =
00265    { { "show", "manager", "connected", NULL },
00266    handle_showmanconn, "Show connected manager interface users", showmanconn_help };
00267 
00268 static void free_session(struct mansession *s)
00269 {
00270    struct eventqent *eqe;
00271    if (s->fd > -1)
00272       close(s->fd);
00273    ast_mutex_destroy(&s->__lock);
00274    while(s->eventq) {
00275       eqe = s->eventq;
00276       s->eventq = s->eventq->next;
00277       free(eqe);
00278    }
00279    free(s);
00280 }
00281 
00282 static void destroy_session(struct mansession *s)
00283 {
00284    struct mansession *cur, *prev = NULL;
00285    ast_mutex_lock(&sessionlock);
00286    cur = sessions;
00287    while(cur) {
00288       if (cur == s)
00289          break;
00290       prev = cur;
00291       cur = cur->next;
00292    }
00293    if (cur) {
00294       if (prev)
00295          prev->next = cur->next;
00296       else
00297          sessions = cur->next;
00298       free_session(s);
00299    } else
00300       ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
00301    ast_mutex_unlock(&sessionlock);
00302    
00303 }
00304 
00305 char *astman_get_header(struct message *m, char *var)
00306 {
00307    char cmp[80];
00308    int x;
00309    snprintf(cmp, sizeof(cmp), "%s: ", var);
00310    for (x=0;x<m->hdrcount;x++)
00311       if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
00312          return m->headers[x] + strlen(cmp);
00313    return "";
00314 }
00315 
00316 struct ast_variable *astman_get_variables(struct message *m)
00317 {
00318    int varlen, x, y;
00319    struct ast_variable *head = NULL, *cur;
00320    char *var, *val;
00321    unsigned int var_count;
00322         char *vars[32];
00323    
00324    varlen = strlen("Variable: ");   
00325 
00326    for (x = 0; x < m->hdrcount; x++) {
00327       if (strncasecmp("Variable: ", m->headers[x], varlen))
00328          continue;
00329 
00330       if (!(var = ast_strdupa(m->headers[x] + varlen)))
00331          return head;
00332 
00333       if ((var_count = ast_app_separate_args(var, '|', vars, sizeof(vars) / sizeof(vars[0])))) {
00334          for (y = 0; y < var_count; y++) {
00335             if (!vars[y])
00336                continue;
00337             var = val = ast_strdupa(vars[y]);
00338             strsep(&val, "=");
00339             if (!val || ast_strlen_zero(var))
00340                continue;
00341             cur = ast_variable_new(var, val);
00342             if (head) {
00343                cur->next = head;
00344                head = cur;
00345             } else
00346                head = cur;
00347          }
00348       }
00349    }
00350 
00351    return head;
00352 }
00353 
00354 /*! NOTE:
00355    Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
00356    hold the session lock _or_ be running in an action callback (in which case s->busy will
00357    be non-zero). In either of these cases, there is no need to lock-protect the session's
00358    fd, since no other output will be sent (events will be queued), and no input will
00359    be read until either the current action finishes or get_input() obtains the session
00360    lock.
00361  */
00362 void astman_send_error(struct mansession *s, struct message *m, char *error)
00363 {
00364    char *id = astman_get_header(m,"ActionID");
00365 
00366    ast_cli(s->fd, "Response: Error\r\n");
00367    if (!ast_strlen_zero(id))
00368       ast_cli(s->fd, "ActionID: %s\r\n",id);
00369    ast_cli(s->fd, "Message: %s\r\n\r\n", error);
00370 }
00371 
00372 void astman_send_response(struct mansession *s, struct message *m, char *resp, char *msg)
00373 {
00374    char *id = astman_get_header(m,"ActionID");
00375 
00376    ast_cli(s->fd, "Response: %s\r\n", resp);
00377    if (!ast_strlen_zero(id))
00378       ast_cli(s->fd, "ActionID: %s\r\n",id);
00379    if (msg)
00380       ast_cli(s->fd, "Message: %s\r\n\r\n", msg);
00381    else
00382       ast_cli(s->fd, "\r\n");
00383 }
00384 
00385 void astman_send_ack(struct mansession *s, struct message *m, char *msg)
00386 {
00387    astman_send_response(s, m, "Success", msg);
00388 }
00389 
00390 /*! Tells you if smallstr exists inside bigstr
00391    which is delim by delim and uses no buf or stringsep
00392    ast_instring("this|that|more","this",',') == 1;
00393 
00394    feel free to move this to app.c -anthm */
00395 static int ast_instring(char *bigstr, char *smallstr, char delim) 
00396 {
00397    char *val = bigstr, *next;
00398 
00399    do {
00400       if ((next = strchr(val, delim))) {
00401          if (!strncmp(val, smallstr, (next - val)))
00402             return 1;
00403          else
00404             continue;
00405       } else
00406          return !strcmp(smallstr, val);
00407 
00408    } while (*(val = (next + 1)));
00409 
00410    return 0;
00411 }
00412 
00413 static int get_perm(char *instr)
00414 {
00415    int x = 0, ret = 0;
00416 
00417    if (!instr)
00418       return 0;
00419 
00420    for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
00421       if (ast_instring(instr, perms[x].label, ','))
00422          ret |= perms[x].num;
00423    
00424    return ret;
00425 }
00426 
00427 static int ast_is_number(char *string) 
00428 {
00429    int ret = 1, x = 0;
00430 
00431    if (!string)
00432       return 0;
00433 
00434    for (x=0; x < strlen(string); x++) {
00435       if (!(string[x] >= 48 && string[x] <= 57)) {
00436          ret = 0;
00437          break;
00438       }
00439    }
00440    
00441    return ret ? atoi(string) : 0;
00442 }
00443 
00444 static int ast_strings_to_mask(char *string) 
00445 {
00446    int x, ret = -1;
00447    
00448    x = ast_is_number(string);
00449 
00450    if (x) {
00451       ret = x;
00452    } else if (ast_strlen_zero(string)) {
00453       ret = -1;
00454    } else if (ast_false(string)) {
00455       ret = 0;
00456    } else if (ast_true(string)) {
00457       ret = 0;
00458       for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
00459          ret |= perms[x].num;    
00460    } else {
00461       ret = 0;
00462       for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
00463          if (ast_instring(string, perms[x].label, ',')) 
00464             ret |= perms[x].num;    
00465       }
00466    }
00467 
00468    return ret;
00469 }
00470 
00471 /*! 
00472    Rather than braindead on,off this now can also accept a specific int mask value 
00473    or a ',' delim list of mask strings (the same as manager.conf) -anthm
00474 */
00475 
00476 static int set_eventmask(struct mansession *s, char *eventmask)
00477 {
00478    int maskint = ast_strings_to_mask(eventmask);
00479 
00480    ast_mutex_lock(&s->__lock);
00481    if (maskint >= 0) 
00482       s->send_events = maskint;
00483    ast_mutex_unlock(&s->__lock);
00484    
00485    return maskint;
00486 }
00487 
00488 static int authenticate(struct mansession *s, struct message *m)
00489 {
00490    struct ast_config *cfg;
00491    char iabuf[INET_ADDRSTRLEN];
00492    char *cat;
00493    char *user = astman_get_header(m, "Username");
00494    char *pass = astman_get_header(m, "Secret");
00495    char *authtype = astman_get_header(m, "AuthType");
00496    char *key = astman_get_header(m, "Key");
00497    char *events = astman_get_header(m, "Events");
00498    
00499    cfg = ast_config_load("manager.conf");
00500    if (!cfg)
00501       return -1;
00502    cat = ast_category_browse(cfg, NULL);
00503    while(cat) {
00504       if (strcasecmp(cat, "general")) {
00505          /* This is a user */
00506          if (!strcasecmp(cat, user)) {
00507             struct ast_variable *v;
00508             struct ast_ha *ha = NULL;
00509             char *password = NULL;
00510             v = ast_variable_browse(cfg, cat);
00511             while (v) {
00512                if (!strcasecmp(v->name, "secret")) {
00513                   password = v->value;
00514                } else if (!strcasecmp(v->name, "permit") ||
00515                      !strcasecmp(v->name, "deny")) {
00516                   ha = ast_append_ha(v->name, v->value, ha);
00517                } else if (!strcasecmp(v->name, "writetimeout")) {
00518                   int val = atoi(v->value);
00519 
00520                   if (val < 100)
00521                      ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
00522                   else
00523                      s->writetimeout = val;
00524                }
00525                      
00526                v = v->next;
00527             }
00528             if (ha && !ast_apply_ha(ha, &(s->sin))) {
00529                ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
00530                ast_free_ha(ha);
00531                ast_config_destroy(cfg);
00532                return -1;
00533             } else if (ha)
00534                ast_free_ha(ha);
00535             if (!strcasecmp(authtype, "MD5")) {
00536                if (!ast_strlen_zero(key) && 
00537                    !ast_strlen_zero(s->challenge) && !ast_strlen_zero(password)) {
00538                   int x;
00539                   int len=0;
00540                   char md5key[256] = "";
00541                   struct MD5Context md5;
00542                   unsigned char digest[16];
00543                   MD5Init(&md5);
00544                   MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
00545                   MD5Update(&md5, (unsigned char *) password, strlen(password));
00546                   MD5Final(digest, &md5);
00547                   for (x=0;x<16;x++)
00548                      len += sprintf(md5key + len, "%2.2x", digest[x]);
00549                   if (!strcmp(md5key, key))
00550                      break;
00551                   else {
00552                      ast_config_destroy(cfg);
00553                      return -1;
00554                   }
00555                }
00556             } else if (password && !strcasecmp(password, pass)) {
00557                break;
00558             } else {
00559                ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
00560                ast_config_destroy(cfg);
00561                return -1;
00562             }  
00563          }
00564       }
00565       cat = ast_category_browse(cfg, cat);
00566    }
00567    if (cat) {
00568       ast_copy_string(s->username, cat, sizeof(s->username));
00569       s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
00570       s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
00571       ast_config_destroy(cfg);
00572       if (events)
00573          set_eventmask(s, events);
00574       return 0;
00575    }
00576    ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
00577    ast_config_destroy(cfg);
00578    return -1;
00579 }
00580 
00581 /*! \brief PING: Manager PING */
00582 static char mandescr_ping[] = 
00583 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the "
00584 "  manager connection open.\n"
00585 "Variables: NONE\n";
00586 
00587 static int action_ping(struct mansession *s, struct message *m)
00588 {
00589    astman_send_response(s, m, "Pong", NULL);
00590    return 0;
00591 }
00592 
00593 static char mandescr_listcommands[] = 
00594 "Description: Returns the action name and synopsis for every\n"
00595 "  action that is available to the user\n"
00596 "Variables: NONE\n";
00597 
00598 static int action_listcommands(struct mansession *s, struct message *m)
00599 {
00600    struct manager_action *cur = first_action;
00601    char idText[256] = "";
00602    char temp[BUFSIZ];
00603    char *id = astman_get_header(m,"ActionID");
00604 
00605    if (!ast_strlen_zero(id))
00606       snprintf(idText,256,"ActionID: %s\r\n",id);
00607    ast_cli(s->fd, "Response: Success\r\n%s", idText);
00608    ast_mutex_lock(&actionlock);
00609    while (cur) { /* Walk the list of actions */
00610       if ((s->writeperm & cur->authority) == cur->authority)
00611          ast_cli(s->fd, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)) );
00612       cur = cur->next;
00613    }
00614    ast_mutex_unlock(&actionlock);
00615    ast_cli(s->fd, "\r\n");
00616 
00617    return 0;
00618 }
00619 
00620 static char mandescr_events[] = 
00621 "Description: Enable/Disable sending of events to this manager\n"
00622 "  client.\n"
00623 "Variables:\n"
00624 "  EventMask: 'on' if all events should be sent,\n"
00625 "     'off' if no events should be sent,\n"
00626 "     'system,call,log' to select which flags events should have to be sent.\n";
00627 
00628 static int action_events(struct mansession *s, struct message *m)
00629 {
00630    char *mask = astman_get_header(m, "EventMask");
00631    int res;
00632 
00633    res = set_eventmask(s, mask);
00634    if (res > 0)
00635       astman_send_response(s, m, "Events On", NULL);
00636    else if (res == 0)
00637       astman_send_response(s, m, "Events Off", NULL);
00638 
00639    return 0;
00640 }
00641 
00642 static char mandescr_logoff[] = 
00643 "Description: Logoff this manager session\n"
00644 "Variables: NONE\n";
00645 
00646 static int action_logoff(struct mansession *s, struct message *m)
00647 {
00648    astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
00649    return -1;
00650 }
00651 
00652 static char mandescr_hangup[] = 
00653 "Description: Hangup a channel\n"
00654 "Variables: \n"
00655 "  Channel: The channel name to be hungup\n";
00656 
00657 static int action_hangup(struct mansession *s, struct message *m)
00658 {
00659    struct ast_channel *c = NULL;
00660    char *name = astman_get_header(m, "Channel");
00661    if (ast_strlen_zero(name)) {
00662       astman_send_error(s, m, "No channel specified");
00663       return 0;
00664    }
00665    c = ast_get_channel_by_name_locked(name);
00666    if (!c) {
00667       astman_send_error(s, m, "No such channel");
00668       return 0;
00669    }
00670    ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
00671    ast_mutex_unlock(&c->lock);
00672    astman_send_ack(s, m, "Channel Hungup");
00673    return 0;
00674 }
00675 
00676 static char mandescr_setvar[] = 
00677 "Description: Set a global or local channel variable.\n"
00678 "Variables: (Names marked with * are required)\n"
00679 "  Channel: Channel to set variable for\n"
00680 "  *Variable: Variable name\n"
00681 "  *Value: Value\n";
00682 
00683 static int action_setvar(struct mansession *s, struct message *m)
00684 {
00685         struct ast_channel *c = NULL;
00686         char *name = astman_get_header(m, "Channel");
00687         char *varname = astman_get_header(m, "Variable");
00688         char *varval = astman_get_header(m, "Value");
00689    
00690    if (ast_strlen_zero(varname)) {
00691       astman_send_error(s, m, "No variable specified");
00692       return 0;
00693    }
00694    
00695    if (ast_strlen_zero(varval)) {
00696       astman_send_error(s, m, "No value specified");
00697       return 0;
00698    }
00699 
00700    if (!ast_strlen_zero(name)) {
00701       c = ast_get_channel_by_name_locked(name);
00702       if (!c) {
00703          astman_send_error(s, m, "No such channel");
00704          return 0;
00705       }
00706    }
00707    
00708    pbx_builtin_setvar_helper(c, varname, varval);
00709      
00710    if (c)
00711       ast_mutex_unlock(&c->lock);
00712 
00713    astman_send_ack(s, m, "Variable Set"); 
00714 
00715    return 0;
00716 }
00717 
00718 static char mandescr_getvar[] = 
00719 "Description: Get the value of a global or local channel variable.\n"
00720 "Variables: (Names marked with * are required)\n"
00721 "  Channel: Channel to read variable from\n"
00722 "  *Variable: Variable name\n"
00723 "  ActionID: Optional Action id for message matching.\n";
00724 
00725 static int action_getvar(struct mansession *s, struct message *m)
00726 {
00727         struct ast_channel *c = NULL;
00728         char *name = astman_get_header(m, "Channel");
00729         char *varname = astman_get_header(m, "Variable");
00730    char *id = astman_get_header(m,"ActionID");
00731    char *varval;
00732    char *varval2=NULL;
00733 
00734    if (!strlen(varname)) {
00735       astman_send_error(s, m, "No variable specified");
00736       return 0;
00737    }
00738 
00739    if (strlen(name)) {
00740       c = ast_get_channel_by_name_locked(name);
00741       if (!c) {
00742          astman_send_error(s, m, "No such channel");
00743          return 0;
00744       }
00745    }
00746    
00747    varval=pbx_builtin_getvar_helper(c,varname);
00748    if (varval)
00749       varval2 = ast_strdupa(varval);
00750    if (!varval2)
00751       varval2 = "";
00752    if (c)
00753       ast_mutex_unlock(&c->lock);
00754    ast_cli(s->fd, "Response: Success\r\n"
00755       "Variable: %s\r\nValue: %s\r\n" ,varname,varval2);
00756    if (!ast_strlen_zero(id))
00757       ast_cli(s->fd, "ActionID: %s\r\n",id);
00758    ast_cli(s->fd, "\r\n");
00759 
00760    return 0;
00761 }
00762 
00763 
00764 /*! \brief  action_status: Manager "status" command to show channels */
00765 /* Needs documentation... */
00766 static int action_status(struct mansession *s, struct message *m)
00767 {
00768    char *id = astman_get_header(m,"ActionID");
00769       char *name = astman_get_header(m,"Channel");
00770    char idText[256] = "";
00771    struct ast_channel *c;
00772    char bridge[256];
00773    struct timeval now = ast_tvnow();
00774    long elapsed_seconds=0;
00775    int all = ast_strlen_zero(name); /* set if we want all channels */
00776 
00777    astman_send_ack(s, m, "Channel status will follow");
00778         if (!ast_strlen_zero(id))
00779                 snprintf(idText,256,"ActionID: %s\r\n",id);
00780    if (all)
00781       c = ast_channel_walk_locked(NULL);
00782    else {
00783       c = ast_get_channel_by_name_locked(name);
00784       if (!c) {
00785          astman_send_error(s, m, "No such channel");
00786          return 0;
00787       }
00788    }
00789    /* if we look by name, we break after the first iteration */
00790    while(c) {
00791       if (c->_bridge)
00792          snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
00793       else
00794          bridge[0] = '\0';
00795       if (c->pbx) {
00796          if (c->cdr) {
00797             elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
00798          }
00799          ast_cli(s->fd,
00800          "Event: Status\r\n"
00801          "Privilege: Call\r\n"
00802          "Channel: %s\r\n"
00803          "CallerID: %s\r\n"
00804          "CallerIDName: %s\r\n"
00805          "Account: %s\r\n"
00806          "State: %s\r\n"
00807          "Context: %s\r\n"
00808          "Extension: %s\r\n"
00809          "Priority: %d\r\n"
00810          "Seconds: %ld\r\n"
00811          "%s"
00812          "Uniqueid: %s\r\n"
00813          "%s"
00814          "\r\n",
00815          c->name, 
00816          c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
00817          c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
00818          c->accountcode,
00819          ast_state2str(c->_state), c->context,
00820          c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
00821       } else {
00822          ast_cli(s->fd,
00823          "Event: Status\r\n"
00824          "Privilege: Call\r\n"
00825          "Channel: %s\r\n"
00826          "CallerID: %s\r\n"
00827          "CallerIDName: %s\r\n"
00828          "Account: %s\r\n"
00829          "State: %s\r\n"
00830          "%s"
00831          "Uniqueid: %s\r\n"
00832          "%s"
00833          "\r\n",
00834          c->name, 
00835          c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
00836          c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
00837          c->accountcode,
00838          ast_state2str(c->_state), bridge, c->uniqueid, idText);
00839       }
00840       ast_mutex_unlock(&c->lock);
00841       if (!all)
00842          break;
00843       c = ast_channel_walk_locked(c);
00844    }
00845    ast_cli(s->fd,
00846    "Event: StatusComplete\r\n"
00847    "%s"
00848    "\r\n",idText);
00849    return 0;
00850 }
00851 
00852 static char mandescr_redirect[] = 
00853 "Description: Redirect (transfer) a call.\n"
00854 "Variables: (Names marked with * are required)\n"
00855 "  *Channel: Channel to redirect\n"
00856 "  ExtraChannel: Second call leg to transfer (optional)\n"
00857 "  *Exten: Extension to transfer to\n"
00858 "  *Context: Context to transfer to\n"
00859 "  *Priority: Priority to transfer to\n"
00860 "  ActionID: Optional Action id for message matching.\n";
00861 
00862 /*! \brief  action_redirect: The redirect manager command */
00863 static int action_redirect(struct mansession *s, struct message *m)
00864 {
00865    char *name = astman_get_header(m, "Channel");
00866    char *name2 = astman_get_header(m, "ExtraChannel");
00867    char *exten = astman_get_header(m, "Exten");
00868    char *context = astman_get_header(m, "Context");
00869    char *priority = astman_get_header(m, "Priority");
00870    struct ast_channel *chan, *chan2 = NULL;
00871    int pi = 0;
00872    int res;
00873 
00874    if (ast_strlen_zero(name)) {
00875       astman_send_error(s, m, "Channel not specified");
00876       return 0;
00877    }
00878    if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
00879       astman_send_error(s, m, "Invalid priority\n");
00880       return 0;
00881    }
00882    chan = ast_get_channel_by_name_locked(name);
00883    if (!chan) {
00884       char buf[BUFSIZ];
00885       snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
00886       astman_send_error(s, m, buf);
00887       return 0;
00888    }
00889    if (!ast_strlen_zero(name2))
00890       chan2 = ast_get_channel_by_name_locked(name2);
00891    res = ast_async_goto(chan, context, exten, pi);
00892    if (!res) {
00893       if (!ast_strlen_zero(name2)) {
00894          if (chan2)
00895             res = ast_async_goto(chan2, context, exten, pi);
00896          else
00897             res = -1;
00898          if (!res)
00899             astman_send_ack(s, m, "Dual Redirect successful");
00900          else
00901             astman_send_error(s, m, "Secondary redirect failed");
00902       } else
00903          astman_send_ack(s, m, "Redirect successful");
00904    } else
00905       astman_send_error(s, m, "Redirect failed");
00906    if (chan)
00907       ast_mutex_unlock(&chan->lock);
00908    if (chan2)
00909       ast_mutex_unlock(&chan2->lock);
00910    return 0;
00911 }
00912 
00913 static char mandescr_command[] = 
00914 "Description: Run a CLI command.\n"
00915 "Variables: (Names marked with * are required)\n"
00916 "  *Command: Asterisk CLI command to run\n"
00917 "  ActionID: Optional Action id for message matching.\n";
00918 
00919 /*! \brief  action_command: Manager command "command" - execute CLI command */
00920 static int action_command(struct mansession *s, struct message *m)
00921 {
00922    char *cmd = astman_get_header(m, "Command");
00923    char *id = astman_get_header(m, "ActionID");
00924    ast_cli(s->fd, "Response: Follows\r\nPrivilege: Command\r\n");
00925    if (!ast_strlen_zero(id))
00926       ast_cli(s->fd, "ActionID: %s\r\n", id);
00927    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
00928    ast_cli_command(s->fd, cmd);
00929    ast_cli(s->fd, "--END COMMAND--\r\n\r\n");
00930    return 0;
00931 }
00932 
00933 static void *fast_originate(void *data)
00934 {
00935    struct fast_originate_helper *in = data;
00936    int res;
00937    int reason = 0;
00938    struct ast_channel *chan = NULL;
00939 
00940    if (!ast_strlen_zero(in->app)) {
00941       res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
00942          !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, 
00943          !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL,
00944          in->vars, in->account, &chan);
00945    } else {
00946       res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
00947          !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, 
00948          !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL,
00949          in->vars, in->account, &chan);
00950    }   
00951    if (!res)
00952       manager_event(EVENT_FLAG_CALL,
00953          "OriginateSuccess",
00954          "%s"
00955          "Channel: %s/%s\r\n"
00956          "Context: %s\r\n"
00957          "Exten: %s\r\n"
00958          "Reason: %d\r\n"
00959          "Uniqueid: %s\r\n",
00960          in->idtext, in->tech, in->data, in->context, in->exten, reason, chan ? chan->uniqueid : "<null>");
00961    else
00962       manager_event(EVENT_FLAG_CALL,
00963          "OriginateFailure",
00964          "%s"
00965          "Channel: %s/%s\r\n"
00966          "Context: %s\r\n"
00967          "Exten: %s\r\n"
00968          "Reason: %d\r\n"
00969          "Uniqueid: %s\r\n",
00970          in->idtext, in->tech, in->data, in->context, in->exten, reason, chan ? chan->uniqueid : "<null>");
00971 
00972    /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
00973    if (chan)
00974       ast_mutex_unlock(&chan->lock);
00975    free(in);
00976    return NULL;
00977 }
00978 
00979 static char mandescr_originate[] = 
00980 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
00981 "  Application/Data\n"
00982 "Variables: (Names marked with * are required)\n"
00983 "  *Channel: Channel name to call\n"
00984 "  Exten: Extension to use (requires 'Context' and 'Priority')\n"
00985 "  Context: Context to use (requires 'Exten' and 'Priority')\n"
00986 "  Priority: Priority to use (requires 'Exten' and 'Context')\n"
00987 "  Application: Application to use\n"
00988 "  Data: Data to use (requires 'Application')\n"
00989 "  Timeout: How long to wait for call to be answered (in ms)\n"
00990 "  CallerID: Caller ID to be set on the outgoing channel\n"
00991 "  Variable: Channel variable to set, multiple Variable: headers are allowed\n"
00992 "  Account: Account code\n"
00993 "  Async: Set to 'true' for fast origination\n";
00994 
00995 static int action_originate(struct mansession *s, struct message *m)
00996 {
00997    char *name = astman_get_header(m, "Channel");
00998    char *exten = astman_get_header(m, "Exten");
00999    char *context = astman_get_header(m, "Context");
01000    char *priority = astman_get_header(m, "Priority");
01001    char *timeout = astman_get_header(m, "Timeout");
01002    char *callerid = astman_get_header(m, "CallerID");
01003    char *account = astman_get_header(m, "Account");
01004    char *app = astman_get_header(m, "Application");
01005    char *appdata = astman_get_header(m, "Data");
01006    char *async = astman_get_header(m, "Async");
01007    char *id = astman_get_header(m, "ActionID");
01008    struct ast_variable *vars = astman_get_variables(m);
01009    char *tech, *data;
01010    char *l=NULL, *n=NULL;
01011    int pi = 0;
01012    int res;
01013    int to = 30000;
01014    int reason = 0;
01015    char tmp[256];
01016    char tmp2[256];
01017    
01018    pthread_t th;
01019    pthread_attr_t attr;
01020    if (!name) {
01021       astman_send_error(s, m, "Channel not specified");
01022       return 0;
01023    }
01024    if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
01025       astman_send_error(s, m, "Invalid priority\n");
01026       return 0;
01027    }
01028    if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
01029       astman_send_error(s, m, "Invalid timeout\n");
01030       return 0;
01031    }
01032    ast_copy_string(tmp, name, sizeof(tmp));
01033    tech = tmp;
01034    data = strchr(tmp, '/');
01035    if (!data) {
01036       astman_send_error(s, m, "Invalid channel\n");
01037       return 0;
01038    }
01039    *data = '\0';
01040    data++;
01041    ast_copy_string(tmp2, callerid, sizeof(tmp2));
01042    ast_callerid_parse(tmp2, &n, &l);
01043    if (n) {
01044       if (ast_strlen_zero(n))
01045          n = NULL;
01046    }
01047    if (l) {
01048       ast_shrink_phone_number(l);
01049       if (ast_strlen_zero(l))
01050          l = NULL;
01051    }
01052    if (ast_true(async)) {
01053       struct fast_originate_helper *fast = malloc(sizeof(struct fast_originate_helper));
01054       if (!fast) {
01055          res = -1;
01056       } else {
01057          memset(fast, 0, sizeof(struct fast_originate_helper));
01058          if (!ast_strlen_zero(id))
01059             snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
01060          ast_copy_string(fast->tech, tech, sizeof(fast->tech));
01061             ast_copy_string(fast->data, data, sizeof(fast->data));
01062          ast_copy_string(fast->app, app, sizeof(fast->app));
01063          ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
01064          if (l)
01065             ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
01066          if (n)
01067             ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
01068          fast->vars = vars;   
01069          ast_copy_string(fast->context, context, sizeof(fast->context));
01070          ast_copy_string(fast->exten, exten, sizeof(fast->exten));
01071          ast_copy_string(fast->account, account, sizeof(fast->account));
01072          fast->timeout = to;
01073          fast->priority = pi;
01074          pthread_attr_init(&attr);
01075          pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01076          if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
01077             res = -1;
01078          } else {
01079             res = 0;
01080          }
01081       }
01082    } else if (!ast_strlen_zero(app)) {
01083          res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
01084       } else {
01085       if (exten && context && pi)
01086             res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
01087       else {
01088          astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
01089          return 0;
01090       }
01091    }   
01092    if (!res)
01093       astman_send_ack(s, m, "Originate successfully queued");
01094    else
01095       astman_send_error(s, m, "Originate failed");
01096    return 0;
01097 }
01098 
01099 /*!   \brief Help text for manager command mailboxstatus
01100  */
01101 static char mandescr_mailboxstatus[] = 
01102 "Description: Checks a voicemail account for status.\n"
01103 "Variables: (Names marked with * are required)\n"
01104 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
01105 "  ActionID: Optional ActionID for message matching.\n"
01106 "Returns number of messages.\n"
01107 "  Message: Mailbox Status\n"
01108 "  Mailbox: <mailboxid>\n"
01109 "  Waiting: <count>\n"
01110 "\n";
01111 
01112 static int action_mailboxstatus(struct mansession *s, struct message *m)
01113 {
01114    char *mailbox = astman_get_header(m, "Mailbox");
01115    char *id = astman_get_header(m,"ActionID");
01116    char idText[256] = "";
01117    int ret;
01118    if (ast_strlen_zero(mailbox)) {
01119       astman_send_error(s, m, "Mailbox not specified");
01120       return 0;
01121    }
01122         if (!ast_strlen_zero(id))
01123                 snprintf(idText,256,"ActionID: %s\r\n",id);
01124    ret = ast_app_has_voicemail(mailbox, NULL);
01125    ast_cli(s->fd, "Response: Success\r\n"
01126                "%s"
01127                "Message: Mailbox Status\r\n"
01128                "Mailbox: %s\r\n"
01129                "Waiting: %d\r\n\r\n", idText, mailbox, ret);
01130    return 0;
01131 }
01132 
01133 static char mandescr_mailboxcount[] = 
01134 "Description: Checks a voicemail account for new messages.\n"
01135 "Variables: (Names marked with * are required)\n"
01136 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
01137 "  ActionID: Optional ActionID for message matching.\n"
01138 "Returns number of new and old messages.\n"
01139 "  Message: Mailbox Message Count\n"
01140 "  Mailbox: <mailboxid>\n"
01141 "  NewMessages: <count>\n"
01142 "  OldMessages: <count>\n"
01143 "\n";
01144 static int action_mailboxcount(struct mansession *s, struct message *m)
01145 {
01146    char *mailbox = astman_get_header(m, "Mailbox");
01147    char *id = astman_get_header(m,"ActionID");
01148    char idText[256] = "";
01149    int newmsgs = 0, oldmsgs = 0;
01150    if (ast_strlen_zero(mailbox)) {
01151       astman_send_error(s, m, "Mailbox not specified");
01152       return 0;
01153    }
01154    ast_app_messagecount(mailbox, &newmsgs, &oldmsgs);
01155    if (!ast_strlen_zero(id)) {
01156       snprintf(idText,256,"ActionID: %s\r\n",id);
01157    }
01158    ast_cli(s->fd, "Response: Success\r\n"
01159                "%s"
01160                "Message: Mailbox Message Count\r\n"
01161                "Mailbox: %s\r\n"
01162                "NewMessages: %d\r\n"
01163                "OldMessages: %d\r\n" 
01164                "\r\n",
01165                 idText,mailbox, newmsgs, oldmsgs);
01166    return 0;
01167 }
01168 
01169 static char mandescr_extensionstate[] = 
01170 "Description: Report the extension state for given extension.\n"
01171 "  If the extension has a hint, will use devicestate to check\n"
01172 "  the status of the device connected to the extension.\n"
01173 "Variables: (Names marked with * are required)\n"
01174 "  *Exten: Extension to check state on\n"
01175 "  *Context: Context for extension\n"
01176 "  ActionId: Optional ID for this transaction\n"
01177 "Will return an \"Extension Status\" message.\n"
01178 "The response will include the hint for the extension and the status.\n";
01179 
01180 static int action_extensionstate(struct mansession *s, struct message *m)
01181 {
01182    char *exten = astman_get_header(m, "Exten");
01183    char *context = astman_get_header(m, "Context");
01184    char *id = astman_get_header(m,"ActionID");
01185    char idText[256] = "";
01186    char hint[256] = "";
01187    int status;
01188    if (ast_strlen_zero(exten)) {
01189       astman_send_error(s, m, "Extension not specified");
01190       return 0;
01191    }
01192    if (ast_strlen_zero(context))
01193       context = "default";
01194    status = ast_extension_state(NULL, context, exten);
01195    ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
01196         if (!ast_strlen_zero(id)) {
01197                 snprintf(idText,256,"ActionID: %s\r\n",id);
01198         }
01199    ast_cli(s->fd, "Response: Success\r\n"
01200                     "%s"
01201                "Message: Extension Status\r\n"
01202                "Exten: %s\r\n"
01203                "Context: %s\r\n"
01204                "Hint: %s\r\n"
01205                "Status: %d\r\n\r\n",
01206                idText,exten, context, hint, status);
01207    return 0;
01208 }
01209 
01210 static char mandescr_timeout[] = 
01211 "Description: Hangup a channel after a certain time.\n"
01212 "Variables: (Names marked with * are required)\n"
01213 "  *Channel: Channel name to hangup\n"
01214 "  *Timeout: Maximum duration of the call (sec)\n"
01215 "Acknowledges set time with 'Timeout Set' message\n";
01216 
01217 static int action_timeout(struct mansession *s, struct message *m)
01218 {
01219    struct ast_channel *c = NULL;
01220    char *name = astman_get_header(m, "Channel");
01221    int timeout = atoi(astman_get_header(m, "Timeout"));
01222    if (ast_strlen_zero(name)) {
01223       astman_send_error(s, m, "No channel specified");
01224       return 0;
01225    }
01226    if (!timeout) {
01227       astman_send_error(s, m, "No timeout specified");
01228       return 0;
01229    }
01230    c = ast_get_channel_by_name_locked(name);
01231    if (!c) {
01232       astman_send_error(s, m, "No such channel");
01233       return 0;
01234    }
01235    ast_channel_setwhentohangup(c, timeout);
01236    ast_mutex_unlock(&c->lock);
01237    astman_send_ack(s, m, "Timeout Set");
01238    return 0;
01239 }
01240 
01241 static int process_message(struct mansession *s, struct message *m)
01242 {
01243    char action[80] = "";
01244    struct manager_action *tmp = first_action;
01245    char *id = astman_get_header(m,"ActionID");
01246    char idText[256] = "";
01247    char iabuf[INET_ADDRSTRLEN];
01248 
01249    ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
01250    ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
01251 
01252    if (ast_strlen_zero(action)) {
01253       astman_send_error(s, m, "Missing action in request");
01254       return 0;
01255    }
01256         if (!ast_strlen_zero(id)) {
01257                 snprintf(idText,256,"ActionID: %s\r\n",id);
01258         }
01259    if (!s->authenticated) {
01260       if (!strcasecmp(action, "Challenge")) {
01261          char *authtype;
01262          authtype = astman_get_header(m, "AuthType");
01263          if (!strcasecmp(authtype, "MD5")) {
01264             if (ast_strlen_zero(s->challenge))
01265                snprintf(s->challenge, sizeof(s->challenge), "%d", rand());
01266             ast_mutex_lock(&s->__lock);
01267             ast_cli(s->fd, "Response: Success\r\n"
01268                   "%s"
01269                   "Challenge: %s\r\n\r\n",
01270                   idText,s->challenge);
01271             ast_mutex_unlock(&s->__lock);
01272             return 0;
01273          } else {
01274             astman_send_error(s, m, "Must specify AuthType");
01275             return 0;
01276          }
01277       } else if (!strcasecmp(action, "Login")) {
01278          if (authenticate(s, m)) {
01279             sleep(1);
01280             astman_send_error(s, m, "Authentication failed");
01281             return -1;
01282          } else {
01283             s->authenticated = 1;
01284             if (option_verbose > 1) {
01285                if ( displayconnects ) {
01286                   ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
01287                }
01288             }
01289             ast_log(LOG_EVENT, "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
01290             astman_send_ack(s, m, "Authentication accepted");
01291          }
01292       } else if (!strcasecmp(action, "Logoff")) {
01293          astman_send_ack(s, m, "See ya");
01294          return -1;
01295       } else
01296          astman_send_error(s, m, "Authentication Required");
01297    } else {
01298       int ret=0;
01299       struct eventqent *eqe;
01300       ast_mutex_lock(&s->__lock);
01301       s->busy = 1;
01302       ast_mutex_unlock(&s->__lock);
01303       while( tmp ) {       
01304          if (!strcasecmp(action, tmp->action)) {
01305             if ((s->writeperm & tmp->authority) == tmp->authority) {
01306                if (tmp->func(s, m))
01307                   ret = -1;
01308             } else {
01309                astman_send_error(s, m, "Permission denied");
01310             }
01311             break;
01312          }
01313          tmp = tmp->next;
01314       }
01315       if (!tmp)
01316          astman_send_error(s, m, "Invalid/unknown command");
01317       ast_mutex_lock(&s->__lock);
01318       s->busy = 0;
01319       while(s->eventq) {
01320          if (ast_carefulwrite(s->fd, s->eventq->eventdata, strlen(s->eventq->eventdata), s->writetimeout) < 0) {
01321             ret = -1;
01322             break;
01323          }
01324          eqe = s->eventq;
01325          s->eventq = s->eventq->next;
01326          free(eqe);
01327       }
01328       ast_mutex_unlock(&s->__lock);
01329       return ret;
01330    }
01331    return 0;
01332 }
01333 
01334 static int get_input(struct mansession *s, char *output)
01335 {
01336    /* output must have at least sizeof(s->inbuf) space */
01337    int res;
01338    int x;
01339    struct pollfd fds[1];
01340    char iabuf[INET_ADDRSTRLEN];
01341    for (x=1;x<s->inlen;x++) {
01342       if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
01343          /* Copy output data up to and including \r\n */
01344          memcpy(output, s->inbuf, x + 1);
01345          /* Add trailing \0 */
01346          output[x+1] = '\0';
01347          /* Move remaining data back to the front */
01348          memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
01349          s->inlen -= (x + 1);
01350          return 1;
01351       }
01352    } 
01353    if (s->inlen >= sizeof(s->inbuf) - 1) {
01354       ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf);
01355       s->inlen = 0;
01356    }
01357    fds[0].fd = s->fd;
01358    fds[0].events = POLLIN;
01359    do {
01360       res = poll(fds, 1, -1);
01361       if (res < 0) {
01362          if (errno == EINTR) {
01363             if (s->dead)
01364                return -1;
01365             continue;
01366          }
01367          ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
01368          return -1;
01369       } else if (res > 0) {
01370          ast_mutex_lock(&s->__lock);
01371          res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
01372          ast_mutex_unlock(&s->__lock);
01373          if (res < 1)
01374             return -1;
01375          break;
01376       }
01377    } while(1);
01378    s->inlen += res;
01379    s->inbuf[s->inlen] = '\0';
01380    return 0;
01381 }
01382 
01383 static void *session_do(void *data)
01384 {
01385    struct mansession *s = data;
01386    struct message m;
01387    char iabuf[INET_ADDRSTRLEN];
01388    int res;
01389    
01390    ast_mutex_lock(&s->__lock);
01391    ast_cli(s->fd, "Asterisk Call Manager/1.0\r\n");
01392    ast_mutex_unlock(&s->__lock);
01393    memset(&m, 0, sizeof(m));
01394    for (;;) {
01395       res = get_input(s, m.headers[m.hdrcount]);
01396       if (res > 0) {
01397          /* Strip trailing \r\n */
01398          if (strlen(m.headers[m.hdrcount]) < 2)
01399             continue;
01400          m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
01401          if (ast_strlen_zero(m.headers[m.hdrcount])) {
01402             if (process_message(s, &m))
01403                break;
01404             memset(&m, 0, sizeof(m));
01405          } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
01406             m.hdrcount++;
01407       } else if (res < 0)
01408          break;
01409    }
01410    if (s->authenticated) {
01411       if (option_verbose > 1) {
01412          if (displayconnects) 
01413             ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));    
01414       }
01415       ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
01416    } else {
01417       if (option_verbose > 1) {
01418          if ( displayconnects )
01419             ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
01420       }
01421       ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
01422    }
01423    destroy_session(s);
01424    return NULL;
01425 }
01426 
01427 static void *accept_thread(void *ignore)
01428 {
01429    int as;
01430    struct sockaddr_in sin;
01431    socklen_t sinlen;
01432    struct mansession *s;
01433    struct protoent *p;
01434    int arg = 1;
01435    int flags;
01436    pthread_attr_t attr;
01437 
01438    pthread_attr_init(&attr);
01439    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01440 
01441    for (;;) {
01442       sinlen = sizeof(sin);
01443       as = accept(asock, (struct sockaddr *)&sin, &sinlen);
01444       if (as < 0) {
01445          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
01446          continue;
01447       }
01448       p = getprotobyname("tcp");
01449       if (p) {
01450          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
01451             ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
01452          }
01453       }
01454       s = malloc(sizeof(struct mansession));
01455       if (!s) {
01456          ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
01457          continue;
01458       } 
01459       memset(s, 0, sizeof(struct mansession));
01460       memcpy(&s->sin, &sin, sizeof(sin));
01461       s->writetimeout = 100;
01462 
01463       if(! block_sockets) {
01464          /* For safety, make sure socket is non-blocking */
01465          flags = fcntl(as, F_GETFL);
01466          fcntl(as, F_SETFL, flags | O_NONBLOCK);
01467       }
01468       ast_mutex_init(&s->__lock);
01469       s->fd = as;
01470       s->send_events = -1;
01471       ast_mutex_lock(&sessionlock);
01472       s->next = sessions;
01473       sessions = s;
01474       ast_mutex_unlock(&sessionlock);
01475       if (ast_pthread_create(&s->t, &attr, session_do, s))
01476          destroy_session(s);
01477    }
01478    pthread_attr_destroy(&attr);
01479    return NULL;
01480 }
01481 
01482 static int append_event(struct mansession *s, const char *str)
01483 {
01484    struct eventqent *tmp, *prev=NULL;
01485    tmp = malloc(sizeof(struct eventqent) + strlen(str));
01486    if (tmp) {
01487       tmp->next = NULL;
01488       strcpy(tmp->eventdata, str);
01489       if (s->eventq) {
01490          prev = s->eventq;
01491          while(prev->next) 
01492             prev = prev->next;
01493          prev->next = tmp;
01494       } else {
01495          s->eventq = tmp;
01496       }
01497       return 0;
01498    }
01499    return -1;
01500 }
01501 
01502 /*! \brief  manager_event: Send AMI event to client */
01503 int manager_event(int category, char *event, char *fmt, ...)
01504 {
01505    struct mansession *s;
01506    char auth[80];
01507    char tmp[4096] = "";
01508    char *tmp_next = tmp;
01509    size_t tmp_left = sizeof(tmp) - 2;
01510    va_list ap;
01511 
01512    ast_mutex_lock(&sessionlock);
01513    for (s = sessions; s; s = s->next) {
01514       if ((s->readperm & category) != category)
01515          continue;
01516 
01517       if ((s->send_events & category) != category)
01518          continue;
01519 
01520       if (ast_strlen_zero(tmp)) {
01521          ast_build_string(&tmp_next, &tmp_left, "Event: %s\r\nPrivilege: %s\r\n",
01522                 event, authority_to_str(category, auth, sizeof(auth)));
01523          va_start(ap, fmt);
01524          ast_build_string_va(&tmp_next, &tmp_left, fmt, ap);
01525          va_end(ap);
01526          *tmp_next++ = '\r';
01527          *tmp_next++ = '\n';
01528          *tmp_next = '\0';
01529       }
01530 
01531       ast_mutex_lock(&s->__lock);
01532       if (s->busy) {
01533          append_event(s, tmp);
01534       } else if (!s->dead) {
01535          if (ast_carefulwrite(s->fd, tmp, tmp_next - tmp, s->writetimeout) < 0) {
01536             ast_log(LOG_WARNING, "Disconnecting slow (or gone) manager session!\n");
01537             s->dead = 1;
01538             pthread_kill(s->t, SIGURG);
01539          }
01540       }
01541       ast_mutex_unlock(&s->__lock);
01542    }
01543    ast_mutex_unlock(&sessionlock);
01544 
01545    return 0;
01546 }
01547 
01548 int ast_manager_unregister( char *action ) 
01549 {
01550    struct manager_action *cur = first_action, *prev = first_action;
01551 
01552    ast_mutex_lock(&actionlock);
01553    while( cur ) {       
01554       if (!strcasecmp(action, cur->action)) {
01555          prev->next = cur->next;
01556          free(cur);
01557          if (option_verbose > 1) 
01558             ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
01559          ast_mutex_unlock(&actionlock);
01560          return 0;
01561       }
01562       prev = cur;
01563       cur = cur->next;
01564    }
01565    ast_mutex_unlock(&actionlock);
01566    return 0;
01567 }
01568 
01569 static int manager_state_cb(char *context, char *exten, int state, void *data)
01570 {
01571    /* Notify managers of change */
01572    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
01573    return 0;
01574 }
01575 
01576 static int ast_manager_register_struct(struct manager_action *act)
01577 {
01578    struct manager_action *cur = first_action, *prev = NULL;
01579    int ret;
01580 
01581    ast_mutex_lock(&actionlock);
01582    while(cur) { /* Walk the list of actions */
01583       ret = strcasecmp(cur->action, act->action);
01584       if (ret == 0) {
01585          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
01586          ast_mutex_unlock(&actionlock);
01587          return -1;
01588       } else if (ret > 0) {
01589          /* Insert these alphabetically */
01590          if (prev) {
01591             act->next = prev->next;
01592             prev->next = act;
01593          } else {
01594             act->next = first_action;
01595             first_action = act;
01596          }
01597          break;
01598       }
01599       prev = cur; 
01600       cur = cur->next;
01601    }
01602    
01603    if (!cur) {
01604       if (prev)
01605          prev->next = act;
01606       else
01607          first_action = act;
01608       act->next = NULL;
01609    }
01610 
01611    if (option_verbose > 1) 
01612       ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
01613    ast_mutex_unlock(&actionlock);
01614    return 0;
01615 }
01616 
01617 /*! \brief register a new command with manager, including online help. This is 
01618    the preferred way to register a manager command */
01619 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
01620 {
01621    struct manager_action *cur;
01622 
01623    cur = malloc(sizeof(struct manager_action));
01624    if (!cur) {
01625       ast_log(LOG_WARNING, "Manager: out of memory trying to register action\n");
01626       return -1;
01627    }
01628    cur->action = action;
01629    cur->authority = auth;
01630    cur->func = func;
01631    cur->synopsis = synopsis;
01632    cur->description = description;
01633    cur->next = NULL;
01634 
01635    ast_manager_register_struct(cur);
01636 
01637    return 0;
01638 }
01639 /*! @}
01640  END Doxygen group */
01641 
01642 static int registered = 0;
01643 
01644 int init_manager(void)
01645 {
01646    struct ast_config *cfg;
01647    char *val;
01648    int oldportno = portno;
01649    static struct sockaddr_in ba;
01650    int x = 1;
01651    if (!registered) {
01652       /* Register default actions */
01653       ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
01654       ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
01655       ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
01656       ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
01657       ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
01658       ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
01659       ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
01660       ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
01661       ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
01662       ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
01663       ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
01664       ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
01665       ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
01666       ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
01667       ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
01668 
01669       ast_cli_register(&show_mancmd_cli);
01670       ast_cli_register(&show_mancmds_cli);
01671       ast_cli_register(&show_manconn_cli);
01672       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
01673       registered = 1;
01674    }
01675    portno = DEFAULT_MANAGER_PORT;
01676    displayconnects = 1;
01677    cfg = ast_config_load("manager.conf");
01678    if (!cfg) {
01679       ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
01680       return 0;
01681    }
01682    memset(&ba, 0, sizeof(ba));
01683    val = ast_variable_retrieve(cfg, "general", "enabled");
01684    if (val)
01685       enabled = ast_true(val);
01686 
01687    val = ast_variable_retrieve(cfg, "general", "block-sockets");
01688    if(val)
01689       block_sockets = ast_true(val);
01690 
01691    if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
01692       if (sscanf(val, "%d", &portno) != 1) {
01693          ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
01694          portno = DEFAULT_MANAGER_PORT;
01695       }
01696    } else if ((val = ast_variable_retrieve(cfg, "general", "portno"))) {
01697       if (sscanf(val, "%d", &portno) != 1) {
01698          ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
01699          portno = DEFAULT_MANAGER_PORT;
01700       }
01701       ast_log(LOG_NOTICE, "Use of portno in manager.conf deprecated.  Please use 'port=%s' instead.\n", val);
01702    }
01703    /* Parsing the displayconnects */
01704    if ((val = ast_variable_retrieve(cfg, "general", "displayconnects"))) {
01705          displayconnects = ast_true(val);;
01706    }
01707             
01708    
01709    ba.sin_family = AF_INET;
01710    ba.sin_port = htons(portno);
01711    memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
01712    
01713    if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
01714       if (!inet_aton(val, &ba.sin_addr)) { 
01715          ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
01716          memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
01717       }
01718    }
01719    
01720    if ((asock > -1) && ((portno != oldportno) || !enabled)) {
01721 #if 0
01722       /* Can't be done yet */
01723       close(asock);
01724       asock = -1;
01725 #else
01726       ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
01727 #endif
01728    }
01729    ast_config_destroy(cfg);
01730    
01731    /* If not enabled, do nothing */
01732    if (!enabled) {
01733       return 0;
01734    }
01735    if (asock < 0) {
01736       asock = socket(AF_INET, SOCK_STREAM, 0);
01737       if (asock < 0) {
01738          ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
01739          return -1;
01740       }
01741       setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
01742       if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
01743          ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
01744          close(asock);
01745          asock = -1;
01746          return -1;
01747       }
01748       if (listen(asock, 2)) {
01749          ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
01750          close(asock);
01751          asock = -1;
01752          return -1;
01753       }
01754       if (option_verbose)
01755          ast_verbose("Asterisk Management interface listening on port %d\n", portno);
01756       ast_pthread_create(&t, NULL, accept_thread, NULL);
01757    }
01758    return 0;
01759 }
01760 
01761 int reload_manager(void)
01762 {
01763    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
01764    return init_manager();
01765 }

Generated on Thu May 24 14:21:11 2007 for Asterisk - the Open Source PBX by  doxygen 1.4.7