Fri Aug 29 06:38:15 2008

Asterisk developer's documentation


manager.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 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: 71288 $")
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(name)) {
00696       c = ast_get_channel_by_name_locked(name);
00697       if (!c) {
00698          astman_send_error(s, m, "No such channel");
00699          return 0;
00700       }
00701    }
00702    
00703    pbx_builtin_setvar_helper(c, varname, varval ? varval : "");
00704      
00705    if (c)
00706       ast_mutex_unlock(&c->lock);
00707 
00708    astman_send_ack(s, m, "Variable Set"); 
00709 
00710    return 0;
00711 }
00712 
00713 static char mandescr_getvar[] = 
00714 "Description: Get the value of a global or local channel variable.\n"
00715 "Variables: (Names marked with * are required)\n"
00716 "  Channel: Channel to read variable from\n"
00717 "  *Variable: Variable name\n"
00718 "  ActionID: Optional Action id for message matching.\n";
00719 
00720 static int action_getvar(struct mansession *s, struct message *m)
00721 {
00722         struct ast_channel *c = NULL;
00723         char *name = astman_get_header(m, "Channel");
00724         char *varname = astman_get_header(m, "Variable");
00725    char *id = astman_get_header(m,"ActionID");
00726    char *varval;
00727    char *varval2=NULL;
00728 
00729    if (!strlen(varname)) {
00730       astman_send_error(s, m, "No variable specified");
00731       return 0;
00732    }
00733 
00734    if (strlen(name)) {
00735       c = ast_get_channel_by_name_locked(name);
00736       if (!c) {
00737          astman_send_error(s, m, "No such channel");
00738          return 0;
00739       }
00740    }
00741    
00742    varval=pbx_builtin_getvar_helper(c,varname);
00743    if (varval)
00744       varval2 = ast_strdupa(varval);
00745    if (!varval2)
00746       varval2 = "";
00747    if (c)
00748       ast_mutex_unlock(&c->lock);
00749    ast_cli(s->fd, "Response: Success\r\n"
00750       "Variable: %s\r\nValue: %s\r\n" ,varname,varval2);
00751    if (!ast_strlen_zero(id))
00752       ast_cli(s->fd, "ActionID: %s\r\n",id);
00753    ast_cli(s->fd, "\r\n");
00754 
00755    return 0;
00756 }
00757 
00758 
00759 /*! \brief  action_status: Manager "status" command to show channels */
00760 /* Needs documentation... */
00761 static int action_status(struct mansession *s, struct message *m)
00762 {
00763    char *id = astman_get_header(m,"ActionID");
00764       char *name = astman_get_header(m,"Channel");
00765    char idText[256] = "";
00766    struct ast_channel *c;
00767    char bridge[256];
00768    struct timeval now = ast_tvnow();
00769    long elapsed_seconds=0;
00770    int all = ast_strlen_zero(name); /* set if we want all channels */
00771 
00772         if (!ast_strlen_zero(id))
00773                 snprintf(idText,256,"ActionID: %s\r\n",id);
00774    if (all)
00775       c = ast_channel_walk_locked(NULL);
00776    else {
00777       c = ast_get_channel_by_name_locked(name);
00778       if (!c) {
00779          astman_send_error(s, m, "No such channel");
00780          return 0;
00781       }
00782    }
00783    astman_send_ack(s, m, "Channel status will follow");
00784    /* if we look by name, we break after the first iteration */
00785    while(c) {
00786       if (c->_bridge)
00787          snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
00788       else
00789          bridge[0] = '\0';
00790       if (c->pbx) {
00791          if (c->cdr) {
00792             elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
00793          }
00794          ast_cli(s->fd,
00795          "Event: Status\r\n"
00796          "Privilege: Call\r\n"
00797          "Channel: %s\r\n"
00798          "CallerID: %s\r\n"
00799          "CallerIDName: %s\r\n"
00800          "Account: %s\r\n"
00801          "State: %s\r\n"
00802          "Context: %s\r\n"
00803          "Extension: %s\r\n"
00804          "Priority: %d\r\n"
00805          "Seconds: %ld\r\n"
00806          "%s"
00807          "Uniqueid: %s\r\n"
00808          "%s"
00809          "\r\n",
00810          c->name, 
00811          c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
00812          c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
00813          c->accountcode,
00814          ast_state2str(c->_state), c->context,
00815          c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
00816       } else {
00817          ast_cli(s->fd,
00818          "Event: Status\r\n"
00819          "Privilege: Call\r\n"
00820          "Channel: %s\r\n"
00821          "CallerID: %s\r\n"
00822          "CallerIDName: %s\r\n"
00823          "Account: %s\r\n"
00824          "State: %s\r\n"
00825          "%s"
00826          "Uniqueid: %s\r\n"
00827          "%s"
00828          "\r\n",
00829          c->name, 
00830          c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
00831          c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
00832          c->accountcode,
00833          ast_state2str(c->_state), bridge, c->uniqueid, idText);
00834       }
00835       ast_mutex_unlock(&c->lock);
00836       if (!all)
00837          break;
00838       c = ast_channel_walk_locked(c);
00839    }
00840    ast_cli(s->fd,
00841    "Event: StatusComplete\r\n"
00842    "%s"
00843    "\r\n",idText);
00844    return 0;
00845 }
00846 
00847 static char mandescr_redirect[] = 
00848 "Description: Redirect (transfer) a call.\n"
00849 "Variables: (Names marked with * are required)\n"
00850 "  *Channel: Channel to redirect\n"
00851 "  ExtraChannel: Second call leg to transfer (optional)\n"
00852 "  *Exten: Extension to transfer to\n"
00853 "  *Context: Context to transfer to\n"
00854 "  *Priority: Priority to transfer to\n"
00855 "  ActionID: Optional Action id for message matching.\n";
00856 
00857 /*! \brief  action_redirect: The redirect manager command */
00858 static int action_redirect(struct mansession *s, struct message *m)
00859 {
00860    char *name = astman_get_header(m, "Channel");
00861    char *name2 = astman_get_header(m, "ExtraChannel");
00862    char *exten = astman_get_header(m, "Exten");
00863    char *context = astman_get_header(m, "Context");
00864    char *priority = astman_get_header(m, "Priority");
00865    struct ast_channel *chan, *chan2 = NULL;
00866    int pi = 0;
00867    int res;
00868 
00869    if (ast_strlen_zero(name)) {
00870       astman_send_error(s, m, "Channel not specified");
00871       return 0;
00872    }
00873    if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
00874       astman_send_error(s, m, "Invalid priority\n");
00875       return 0;
00876    }
00877    chan = ast_get_channel_by_name_locked(name);
00878    if (!chan) {
00879       char buf[BUFSIZ];
00880       snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
00881       astman_send_error(s, m, buf);
00882       return 0;
00883    }
00884    if (ast_check_hangup(chan)) {
00885       astman_send_error(s, m, "Redirect failed, channel hung up.\n");
00886       ast_mutex_unlock(&chan->lock);
00887       return 0;
00888    }
00889    if (!ast_strlen_zero(name2))
00890       chan2 = ast_get_channel_by_name_locked(name2);
00891    if (chan2 && ast_check_hangup(chan2)) {
00892       astman_send_error(s, m, "Redirect failed, extra channel hung up.\n");
00893       ast_mutex_unlock(&chan->lock);
00894       ast_mutex_unlock(&chan2->lock);
00895       return 0;
00896    }
00897    res = ast_async_goto(chan, context, exten, pi);
00898    if (!res) {
00899       if (!ast_strlen_zero(name2)) {
00900          if (chan2)
00901             res = ast_async_goto(chan2, context, exten, pi);
00902          else
00903             res = -1;
00904          if (!res)
00905             astman_send_ack(s, m, "Dual Redirect successful");
00906          else
00907             astman_send_error(s, m, "Secondary redirect failed");
00908       } else
00909          astman_send_ack(s, m, "Redirect successful");
00910    } else
00911       astman_send_error(s, m, "Redirect failed");
00912    if (chan)
00913       ast_mutex_unlock(&chan->lock);
00914    if (chan2)
00915       ast_mutex_unlock(&chan2->lock);
00916    return 0;
00917 }
00918 
00919 static char mandescr_command[] = 
00920 "Description: Run a CLI command.\n"
00921 "Variables: (Names marked with * are required)\n"
00922 "  *Command: Asterisk CLI command to run\n"
00923 "  ActionID: Optional Action id for message matching.\n";
00924 
00925 /*! \brief  action_command: Manager command "comma