Sat Sep 16 05:47:46 2006

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) && s->challenge) {
00537                   int x;
00538                   int len=0;
00539                   char md5key[256] = "";
00540                   struct MD5Context md5;
00541                   unsigned char digest[16];
00542                   MD5Init(&md5);
00543                   MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
00544                   MD5Update(&md5, (unsigned char *) password, strlen(password));
00545                   MD5Final(digest, &md5);
00546                   for (x=0;x<16;x++)
00547                      len += sprintf(md5key + len, "%2.2x", digest[x]);
00548                   if (!strcmp(md5key, key))
00549                      break;
00550                   else {
00551                      ast_config_destroy(cfg);
00552                      return -1;
00553                   }
00554                }
00555             } else if (password && !strcasecmp(password, pass)) {
00556                break;
00557             } else {
00558                ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
00559                ast_config_destroy(cfg);
00560                return -1;
00561             }  
00562          }
00563       }
00564       cat = ast_category_browse(cfg, cat);
00565    }
00566    if (cat) {
00567       ast_copy_string(s->username, cat, sizeof(s->username));
00568       s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
00569       s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
00570       ast_config_destroy(cfg);
00571       if (events)
00572          set_eventmask(s, events);
00573       return 0;
00574    }
00575    ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
00576    ast_config_destroy(cfg);
00577    return -1;
00578 }
00579 
00580 /*! \brief PING: Manager PING */
00581 static char mandescr_ping[] = 
00582 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the "
00583 "  manager connection open.\n"
00584 "Variables: NONE\n";
00585 
00586 static int action_ping(struct mansession *s, struct message *m)
00587 {
00588    astman_send_response(s, m, "Pong", NULL);
00589    return 0;
00590 }
00591 
00592 static char mandescr_listcommands[] = 
00593 "Description: Returns the action name and synopsis for every\n"
00594 "  action that is available to the user\n"
00595 "Variables: NONE\n";
00596 
00597 static int action_listcommands(struct mansession *s, struct message *m)
00598 {
00599    struct manager_action *cur = first_action;
00600    char idText[256] = "";
00601    char temp[BUFSIZ];
00602    char *id = astman_get_header(m,"ActionID");
00603 
00604    if (!ast_strlen_zero(id))
00605       snprintf(idText,256,"ActionID: %s\r\n",id);
00606    ast_cli(s->fd, "Response: Success\r\n%s", idText);
00607    ast_mutex_lock(&actionlock);
00608    while (cur) { /* Walk the list of actions */
00609       if ((s->writeperm & cur->authority) == cur->authority)
00610          ast_cli(s->fd, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)) );
00611       cur = cur->next;
00612    }
00613    ast_mutex_unlock(&actionlock);
00614    ast_cli(s->fd, "\r\n");
00615 
00616    return 0;
00617 }
00618 
00619 static char mandescr_events[] = 
00620 "Description: Enable/Disable sending of events to this manager\n"
00621 "  client.\n"
00622 "Variables:\n"
00623 "  EventMask: 'on' if all events should be sent,\n"
00624 "     'off' if no events should be sent,\n"
00625 "     'system,call,log' to select which flags events should have to be sent.\n";
00626 
00627 static int action_events(struct mansession *s, struct message *m)
00628 {
00629    char *mask = astman_get_header(m, "EventMask");
00630    int res;
00631 
00632    res = set_eventmask(s, mask);
00633    if (res > 0)
00634       astman_send_response(s, m, "Events On", NULL);
00635    else if (res == 0)
00636       astman_send_response(s, m, "Events Off", NULL);
00637 
00638    return 0;
00639 }
00640 
00641 static char mandescr_logoff[] = 
00642 "Description: Logoff this manager session\n"
00643 "Variables: NONE\n";
00644 
00645 static int action_logoff(struct mansession *s, struct message *m)
00646 {
00647    astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
00648    return -1;
00649 }
00650 
00651 static char mandescr_hangup[] = 
00652 "Description: Hangup a channel\n"
00653 "Variables: \n"
00654 "  Channel: The channel name to be hungup\n";
00655 
00656 static int action_hangup(struct mansession *s, struct message *m)
00657 {
00658    struct ast_channel *c = NULL;
00659    char *name = astman_get_header(m, "Channel");
00660    if (ast_strlen_zero(name)) {
00661       astman_send_error(s, m, "No channel specified");
00662       return 0;
00663    }
00664    c = ast_get_channel_by_name_locked(name);
00665    if (!c) {
00666       astman_send_error(s, m, "No such channel");
00667       return 0;
00668    }
00669    ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
00670    ast_mutex_unlock(&c->lock);
00671    astman_send_ack(s, m, "Channel Hungup");
00672    return 0;
00673 }
00674 
00675 static char mandescr_setvar[] = 
00676 "Description: Set a global or local channel variable.\n"
00677 "Variables: (Names marked with * are required)\n"
00678 "  Channel: Channel to set variable for\n"
00679 "  *Variable: Variable name\n"
00680 "  *Value: Value\n";
00681 
00682 static int action_setvar(struct mansession *s, struct message *m)
00683 {
00684         struct ast_channel *c = NULL;
00685         char *name = astman_get_header(m, "Channel");
00686         char *varname = astman_get_header(m, "Variable");
00687         char *varval = astman_get_header(m, "Value");
00688    
00689    if (ast_strlen_zero(varname)) {
00690       astman_send_error(s, m, "No variable specified");
00691       return 0;
00692    }
00693    
00694    if (ast_strlen_zero(varval)) {
00695       astman_send_error(s, m, "No value specified");
00696       return 0;
00697    }
00698 
00699    if (!ast_strlen_zero(name)) {
00700       c = ast_get_channel_by_name_locked(name);
00701       if (!c) {
00702          astman_send_error(s, m, "No such channel");
00703          return 0;
00704       }
00705    }
00706    
00707    pbx_builtin_setvar_helper(c, varname, varval);
00708      
00709    if (c)
00710       ast_mutex_unlock(&c->lock);
00711 
00712    astman_send_ack(s, m, "Variable Set"); 
00713 
00714    return 0;
00715 }
00716 
00717 static char mandescr_getvar[] = 
00718 "Description: Get the value of a global or local channel variable.\n"
00719 "Variables: (Names marked with * are required)\n"
00720 "  Channel: Channel to read variable from\n"
00721 "  *Variable: Variable name\n"
00722 "  ActionID: Optional Action id for message matching.\n";
00723 
00724 static int action_getvar(struct mansession *s, struct message *m)
00725 {
00726         struct ast_channel *c = NULL;
00727         char *name = astman_get_header(m, "Channel");
00728         char *varname = astman_get_header(m, "Variable");
00729    char *id = astman_get_header(m,"ActionID");
00730    char *varval;
00731    char *varval2=NULL;
00732 
00733    if (!strlen(varname)) {
00734       astman_send_error(s, m, "No variable specified");
00735       return 0;
00736    }
00737 
00738    if (strlen(name)) {
00739       c = ast_get_channel_by_name_locked(name);
00740       if (!c) {
00741          astman_send_error(s, m, "No such channel");
00742          return 0;
00743       }
00744    }
00745    
00746    varval=pbx_builtin_getvar_helper(c,varname);
00747    if (varval)
00748       varval2 = ast_strdupa(varval);
00749    if (!varval2)
00750       varval2 = "";
00751    if (c)
00752       ast_mutex_unlock(&c->lock);
00753    ast_cli(s->fd, "Response: Success\r\n"
00754       "Variable: %s\r\nValue: %s\r\n" ,varname,varval2);
00755    if (!ast_strlen_zero(id))
00756       ast_cli(s->fd, "ActionID: %s\r\n",id);
00757    ast_cli(s->fd, "\r\n");
00758 
00759    return 0;
00760 }
00761 
00762 
00763 /*! \brief  action_status: Manager "status" command to show channels */
00764 /* Needs documentation... */
00765 static int action_status(struct mansession *s, struct message *m)
00766 {
00767    char *id = astman_get_header(m,"ActionID");
00768       char *name = astman_get_header(m,"Channel");
00769    char idText[256] = "";
00770    struct ast_channel *c;
00771    char bridge[256];
00772    struct timeval now = ast_tvnow();
00773    long elapsed_seconds=0;
00774    int all = ast_strlen_zero(name); /* set if we want all channels */
00775 
00776    astman_send_ack(s, m, "Channel status will follow");
00777         if (!ast_strlen_zero(id))
00778                 snprintf(idText,256,"ActionID: %s\r\n",id);
00779    if (all)
00780       c = ast_channel_walk_locked(NULL);
00781    else {
00782       c = ast_get_channel_by_name_locked(name);
00783       if (!c) {
00784          astman_send_error(s, m, "No such channel");
00785          return 0;
00786       }
00787    }
00788    /* if we look by name, we break after the first iteration */
00789    while(c) {
00790       if (c->_bridge)
00791          snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
00792       else
00793          bridge[0] = '\0';
00794       if (c->pbx) {
00795          if (c->cdr) {
00796             elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
00797          }
00798          ast_cli(s->fd,
00799          "Event: Status\r\n"
00800          "Privilege: Call\r\n"
00801          "Channel: %s\r\n"
00802          "CallerID: %s\r\n"
00803          "CallerIDName: %s\r\n"
00804          "Account: %s\r\n"
00805          "State: %s\r\n"
00806          "Context: %s\r\n"
00807          "Extension: %s\r\n"
00808          "Priority: %d\r\n"
00809          "Seconds: %ld\r\n"
00810          "%s"
00811          "Uniqueid: %s\r\n"
00812          "%s"
00813          "\r\n",
00814          c->name, 
00815          c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
00816          c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
00817          c->accountcode,
00818          ast_state2str(c->_state), c->context,
00819          c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
00820       } else {
00821          ast_cli(s->fd,
00822          "Event: Status\r\n"
00823          "Privilege: Call\r\n"
00824          "Channel: %s\r\n"
00825          "CallerID: %s\r\n"
00826          "CallerIDName: %s\r\n"
00827          "Account: %s\r\n"
00828          "State: %s\r\n"
00829          "%s"
00830          "Uniqueid: %s\r\n"
00831          "%s"
00832          "\r\n",
00833          c->name, 
00834          c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
00835          c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
00836          c->accountcode,
00837          ast_state2str(c->_state), bridge, c->uniqueid, idText);
00838       }
00839       ast_mutex_unlock(&c->lock);
00840       if (!all)
00841          break;
00842       c = ast_channel_walk_locked(c);
00843    }
00844    ast_cli(s->fd,
00845    "Event: StatusComplete\r\n"
00846    "%s"
00847    "\r\n",idText);
00848    return 0;
00849 }
00850 
00851 static char mandescr_redirect[] = 
00852 "Description: Redirect (transfer) a call.\n"
00853 "Variables: (Names marked with * are required)\n"
00854 "  *Channel: Channel to redirect\n"
00855 "  ExtraChannel: Second call leg to transfer (optional)\n"
00856 "  *Exten: Extension to transfer to\n"
00857 "  *Context: Context to transfer to\n"
00858 "  *Priority: Priority to transfer to\n"
00859 "  ActionID: Optional Action id for message matching.\n";
00860 
00861 /*! \brief  action_redirect: The redirect manager command */
00862 static int action_redirect(struct mansession *s, struct message *m)
00863 {
00864    char *name = astman_get_header(m, "Channel");
00865    char *name2 = astman_get_header(m, "ExtraChannel");
00866    char *exten = astman_get_header(m, "Exten");
00867    char *context = astman_get_header(m, "Context");
00868    char *priority = astman_get_header(m, "Priority");
00869    struct ast_channel *chan, *chan2 = NULL;
00870    int pi = 0;
00871    int res;
00872 
00873    if (ast_strlen_zero(name)) {
00874       astman_send_error(s, m, "Channel not specified");
00875       return 0;
00876    }
00877    if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
00878       astman_send_error(s, m, "Invalid priority\n");
00879       return 0;
00880    }
00881    chan = ast_get_channel_by_name_locked(name);
00882    if (!chan) {
00883       char buf[BUFSIZ];
00884       snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
00885       astman_send_error(s, m, buf);
00886       return 0;
00887    }
00888    if (!ast_strlen_zero(name2))
00889       chan2 = ast_get_channel_by_name_locked(name2);
00890    res = ast_async_goto(chan, context, exten, pi);
00891    if (!res) {
00892       if (!ast_strlen_zero(name2)) {
00893          if (chan2)
00894             res = ast_async_goto(chan2, context, exten, pi);
00895          else
00896             res = -1;
00897          if (!res)
00898             astman_send_ack(s, m, "Dual Redirect successful");
00899          else
00900             astman_send_error(s, m, "Secondary redirect failed");
00901       } else
00902          astman_send_ack(s, m, "Redirect successful");
00903    } else
00904       astman_send_error(s, m, "Redirect failed");
00905    if (chan)
00906       ast_mutex_unlock(&chan->lock);
00907    if (chan2)
00908       ast_mutex_unlock(&chan2->lock);
00909    return 0;
00910 }
00911 
00912 static char mandescr_command[] = 
00913 "Description: Run a CLI command.\n"
00914 "Variables: (Names marked with * are required)\n"
00915 "  *Command: Asterisk CLI command to run\n"
00916 "  ActionID: Optional Action id for message matching.\n";
00917 
00918 /*! \brief  action_command: Manager command "command" - execute CLI command */
00919 static int action_command(struct mansession *s, struct message *m)
00920 {
00921    char *cmd = astman_get_header(m, "Command");
00922    char *id = astman_get_header(m, "ActionID");
00923    ast_cli(s->fd, "Response: Follows\r\nPrivilege: Command\r\n");
00924    if (!ast_strlen_zero(id))
00925       ast_cli(s->fd, "ActionID: %s\r\n", id);
00926    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
00927    ast_cli_command(s->fd, cmd);
00928    ast_cli(s->fd, "--END COMMAND--\r\n\r\n");
00929    return 0;
00930 }
00931 
00932 static void *fast_originate(void *data)
00933 {
00934    struct fast_originate_helper *in = data;
00935    int res;
00936    int reason = 0;
00937    struct ast_channel *chan = NULL;
00938 
00939    if (!ast_strlen_zero(in->app)) {
00940       res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
00941          !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, 
00942          !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL,
00943          in->vars, in->account, &chan);
00944    } else {
00945       res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
00946          !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, 
00947          !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL,
00948          in->vars, in->account, &chan);
00949    }   
00950    if (!res)
00951       manager_event(EVENT_FLAG_CALL,
00952          "OriginateSuccess",
00953          "%s"
00954          "Channel: %s/%s\r\n"
00955          "Context: %s\r\n"
00956          "Exten: %s\r\n"
00957          "Reason: %d\r\n"
00958          "Uniqueid: %s\r\n",
00959          in->idtext, in->tech, in->data, in->context, in->exten, reason, chan ? chan->uniqueid : "<null>");
00960    else
00961       manager_event(EVENT_FLAG_CALL,
00962          "OriginateFailure",
00963          "%s"
00964          "Channel: %s/%s\r\n"
00965          "Context: %s\r\n"
00966          "Exten: %s\r\n"
00967          "Reason: %d\r\n"
00968          "Uniqueid: %s\r\n",
00969          in->idtext, in->tech, in->data, in->context, in->exten, reason, chan ? chan->uniqueid : "<null>");
00970 
00971    /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
00972    if (chan)
00973       ast_mutex_unlock(&chan->lock);
00974    free(in);
00975    return NULL;
00976 }
00977 
00978 static char mandescr_originate[] = 
00979 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
00980 "  Application/Data\n"
00981 "Variables: (Names marked with * are required)\n"
00982 "  *Channel: Channel name to call\n"
00983 "  Exten: Extension to use (requires 'Context' and 'Priority')\n"
00984 "  Context: Context to use (requires 'Exten' and 'Priority')\n"
00985 "  Priority: Priority to use (requires 'Exten' and 'Context')\n"
00986 "  Application: Application to use\n"
00987 "  Data: Data to use (requires 'Application')\n"
00988 "  Timeout: How long to wait for call to be answered (in ms)\n"
00989 "  CallerID: Caller ID to be set on the outgoing channel\n"
00990 "  Variable: Channel variable to set, multiple Variable: headers are allowed\n"
00991 "  Account: Account code\n"
00992 "  Async: Set to 'true' for fast origination\n";
00993 
00994 static int action_originate(struct mansession *s, struct message *m)
00995 {
00996    char *name = astman_get_header(m, "Channel");
00997    char *exten = astman_get_header(m, "Exten");
00998    char *context = astman_get_header(m, "Context");
00999    char *priority = astman_get_header(m, "Priority");
01000    char *timeout = astman_get_header(m, "Timeout");
01001    char *callerid = astman_get_header(m, "CallerID");
01002    char *account = astman_get_header(m, "Account");
01003    char *app = astman_get_header(m, "Application");
01004    char *appdata = astman_get_header(m, "Data");
01005    char *async = astman_get_header(m, "Async");
01006    char *id = astman_get_header(m, "ActionID");
01007    struct ast_variable *vars = astman_get_variables(m);
01008    char *tech, *data;
01009    char *l=NULL, *n=NULL;
01010    int pi = 0;
01011    int res;
01012    int to = 30000;
01013    int reason = 0;
01014    char tmp[256];
01015    char tmp2[256];
01016    
01017    pthread_t th;
01018    pthread_attr_t attr;
01019    if (!name) {
01020       astman_send_error(s, m, "Channel not specified");
01021       return 0;
01022    }
01023    if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
01024       astman_send_error(s, m, "Invalid priority\n");
01025       return 0;
01026    }
01027    if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
01028       astman_send_error(s, m, "Invalid timeout\n");
01029       return 0;
01030    }
01031    ast_copy_string(tmp, name, sizeof(tmp));
01032    tech = tmp;
01033    data = strchr(tmp, '/');
01034    if (!data) {
01035       astman_send_error(s, m, "Invalid channel\n");
01036       return 0;
01037    }
01038    *data = '\0';
01039    data++;
01040    ast_copy_string(tmp2, callerid, sizeof(tmp2));
01041    ast_callerid_parse(tmp2, &n, &l);
01042    if (n) {
01043       if (ast_strlen_zero(n))
01044          n = NULL;
01045    }
01046    if (l) {
01047       ast_shrink_phone_number(l);
01048       if (ast_strlen_zero(l))
01049          l = NULL;
01050    }
01051    if (ast_true(async)) {
01052       struct fast_originate_helper *fast = malloc(sizeof(struct fast_originate_helper));
01053       if (!fast) {
01054          res = -1;
01055       } else {
01056          memset(fast, 0, sizeof(struct fast_originate_helper));
01057          if (!ast_strlen_zero(id))
01058             snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
01059          ast_copy_string(fast->tech, tech, sizeof(fast->tech));
01060             ast_copy_string(fast->data, data, sizeof(fast->data));
01061          ast_copy_string(fast->app, app, sizeof(fast->app));
01062          ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
01063          if (l)
01064             ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
01065          if (n)
01066             ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
01067          fast->vars = vars;   
01068          ast_copy_string(fast->context, context, sizeof(fast->context));
01069          ast_copy_string(fast->exten, exten, sizeof(fast->exten));
01070          ast_copy_string(fast->account, account, sizeof(fast->account));
01071          fast->timeout = to;
01072          fast->priority = pi;
01073          pthread_attr_init(&attr);
01074          pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01075          if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
01076             res = -1;
01077          } else {
01078             res = 0;
01079          }
01080       }
01081    } else if (!ast_strlen_zero(app)) {
01082          res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
01083       } else {
01084       if (exten && context && pi)
01085             res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
01086       else {
01087          astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
01088          return 0;
01089       }
01090    }   
01091    if (!res)
01092       astman_send_ack(s, m, "Originate successfully queued");
01093    else
01094       astman_send_error(s, m, "Originate failed");
01095    return 0;
01096 }
01097 
01098 /*!   \brief Help text for manager command mailboxstatus
01099  */
01100 static char mandescr_mailboxstatus[] = 
01101 "Description: Checks a voicemail account for status.\n"
01102 "Variables: (Names marked with * are required)\n"
01103 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
01104 "  ActionID: Optional ActionID for message matching.\n"
01105 "Returns number of messages.\n"
01106 "  Message: Mailbox Status\n"
01107 "  Mailbox: <mailboxid>\n"
01108 "  Waiting: <count>\n"
01109 "\n";
01110 
01111 static int action_mailboxstatus(struct mansession *s, struct message *m)
01112 {
01113    char *mailbox = astman_get_header(m, "Mailbox");
01114    char *id = astman_get_header(m,"ActionID");
01115    char idText[256] = "";
01116    int ret;
01117    if (ast_strlen_zero(mailbox)) {
01118       astman_send_error(s, m, "Mailbox not specified");
01119       return 0;
01120    }
01121         if (!ast_strlen_zero(id))
01122                 snprintf(idText,256,"ActionID: %s\r\n",id);
01123    ret = ast_app_has_voicemail(mailbox, NULL);
01124    ast_cli(s->fd, "Response: Success\r\n"
01125                "%s"
01126                "Message: Mailbox Status\r\n"
01127                "Mailbox: %s\r\n"
01128                "Waiting: %d\r\n\r\n", idText, mailbox, ret);
01129    return 0;
01130 }
01131 
01132 static char mandescr_mailboxcount[] = 
01133 "Description: Checks a voicemail account for new messages.\n"
01134 "Variables: (Names marked with * are required)\n"
01135 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
01136 "  ActionID: Optional ActionID for message matching.\n"
01137 "Returns number of new and old messages.\n"
01138 "  Message: Mailbox Message Count\n"
01139 "  Mailbox: <mailboxid>\n"
01140 "  NewMessages: <count>\n"
01141 "  OldMessages: <count>\n"
01142 "\n";
01143 static int action_mailboxcount(struct mansession *s, struct message *m)
01144 {
01145    char *mailbox = astman_get_header(m, "Mailbox");
01146    char *id = astman_get_header(m,"ActionID");
01147    char idText[256] = "";
01148    int newmsgs = 0, oldmsgs = 0;
01149    if (ast_strlen_zero(mailbox)) {
01150       astman_send_error(s, m, "Mailbox not specified");
01151       return 0;
01152    }
01153    ast_app_messagecount(mailbox, &newmsgs, &oldmsgs);
01154    if (!ast_strlen_zero(id)) {
01155       snprintf(idText,256,"ActionID: %s\r\n",id);
01156    }
01157    ast_cli(s->fd, "Response: Success\r\n"
01158                "%s"
01159                "Message: Mailbox Message Count\r\n"
01160                "Mailbox: %s\r\n"
01161                "NewMessages: %d\r\n"
01162                "OldMessages: %d\r\n" 
01163                "\r\n",
01164                 idText,mailbox, newmsgs, oldmsgs);
01165    return 0;
01166 }
01167 
01168 static char mandescr_extensionstate[] = 
01169 "Description: Report the extension state for given extension.\n"
01170 "  If the extension has a hint, will use devicestate to check\n"
01171 "  the status of the device connected to the extension.\n"
01172 "Variables: (Names marked with * are required)\n"
01173 "  *Exten: Extension to check state on\n"
01174 "  *Context: Context for extension\n"
01175 "  ActionId: Optional ID for this transaction\n"
01176 "Will return an \"Extension Status\" message.\n"
01177 "The response will include the hint for the extension and the status.\n";
01178 
01179 static int action_extensionstate(struct mansession *s, struct message *m)
01180 {
01181    char *exten = astman_get_header(m, "Exten");
01182    char *context = astman_get_header(m, "Context");
01183    char *id = astman_get_header(m,"ActionID");
01184    char idText[256] = "";
01185    char hint[256] = "";
01186    int status;
01187    if (ast_strlen_zero(exten)) {
01188       astman_send_error(s, m, "Extension not specified");
01189       return 0;
01190    }
01191    if (ast_strlen_zero(context))
01192       context = "default";
01193    status = ast_extension_state(NULL, context, exten);
01194    ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
01195         if (!ast_strlen_zero(id)) {
01196                 snprintf(idText,256,"ActionID: %s\r\n",id);
01197         }
01198    ast_cli(s->fd, "Response: Success\r\n"
01199                     "%s"
01200                "Message: Extension Status\r\n"
01201                "Exten: %s\r\n"
01202                "Context: %s\r\n"
01203                "Hint: %s\r\n"
01204                "Status: %d\r\n\r\n",
01205                idText,exten, context, hint, status);
01206    return 0;
01207 }
01208 
01209 static char mandescr_timeout[] = 
01210 "Description: Hangup a channel after a certain time.\n"
01211 "Variables: (Names marked with * are required)\n"
01212 "  *Channel: Channel name to hangup\n"
01213 "  *Timeout: Maximum duration of the call (sec)\n"
01214 "Acknowledges set time with 'Timeout Set' message\n";
01215 
01216 static int action_timeout(struct mansession *s, struct message *m)
01217 {
01218    struct ast_channel *c = NULL;
01219    char *name = astman_get_header(m, "Channel");
01220    int timeout = atoi(astman_get_header(m, "Timeout"));
01221    if (ast_strlen_zero(name)) {
01222       astman_send_error(s, m, "No channel specified");
01223       return 0;
01224    }
01225    if (!timeout) {
01226       astman_send_error(s, m, "No timeout specified");
01227       return 0;
01228    }
01229    c = ast_get_channel_by_name_locked(name);
01230    if (!c) {
01231       astman_send_error(s, m, "No such channel");
01232       return 0;
01233    }
01234    ast_channel_setwhentohangup(c, timeout);
01235    ast_mutex_unlock(&c->lock);
01236    astman_send_ack(s, m, "Timeout Set");
01237    return 0;
01238 }
01239 
01240 static int process_message(struct mansession *s, struct message *m)
01241 {
01242    char action[80] = "";
01243    struct manager_action *tmp = first_action;
01244    char *id = astman_get_header(m,"ActionID");
01245    char idText[256] = "";
01246    char iabuf[INET_ADDRSTRLEN];
01247 
01248    ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
01249    ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
01250 
01251    if (ast_strlen_zero(action)) {
01252       astman_send_error(s, m, "Missing action in request");
01253       return 0;
01254    }
01255         if (!ast_strlen_zero(id)) {
01256                 snprintf(idText,256,"ActionID: %s\r\n",id);
01257         }
01258    if (!s->authenticated) {
01259       if (!strcasecmp(action, "Challenge")) {
01260          char *authtype;
01261          authtype = astman_get_header(m, "AuthType");
01262          if (!strcasecmp(authtype, "MD5")) {
01263             if (ast_strlen_zero(s->challenge))
01264                snprintf(s->challenge, sizeof(s->challenge), "%d", rand());
01265             ast_mutex_lock(&s->__lock);
01266             ast_cli(s->fd, "Response: Success\r\n"
01267                   "%s"
01268                   "Challenge: %s\r\n\r\n",
01269                   idText,s->challenge);
01270             ast_mutex_unlock(&s->__lock);
01271             return 0;
01272          } else {
01273             astman_send_error(s, m, "Must specify AuthType");
01274             return 0;
01275          }
01276       } else if (!strcasecmp(action, "Login")) {
01277          if (authenticate(s, m)) {
01278             sleep(1);
01279             astman_send_error(s, m, "Authentication failed");
01280             return -1;
01281          } else {
01282             s->authenticated = 1;
01283             if (option_verbose > 1) {
01284                if ( displayconnects ) {
01285                   ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
01286                }
01287             }
01288             ast_log(LOG_EVENT, "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
01289             astman_send_ack(s, m, "Authentication accepted");
01290          }
01291       } else if (!strcasecmp(action, "Logoff")) {
01292          astman_send_ack(s, m, "See ya");
01293          return -1;
01294       } else
01295          astman_send_error(s, m, "Authentication Required");
01296    } else {
01297       int ret=0;
01298       struct eventqent *eqe;
01299       ast_mutex_lock(&s->__lock);
01300       s->busy = 1;
01301       ast_mutex_unlock(&s->__lock);
01302       while( tmp ) {       
01303          if (!strcasecmp(action, tmp->action)) {
01304             if ((s->writeperm & tmp->authority) == tmp->authority) {
01305                if (tmp->func(s, m))
01306                   ret = -1;
01307             } else {
01308                astman_send_error(s, m, "Permission denied");
01309             }
01310             break;
01311          }
01312          tmp = tmp->next;
01313       }
01314       if (!tmp)
01315          astman_send_error(s, m, "Invalid/unknown command");
01316       ast_mutex_lock(&s->__lock);
01317       s->busy = 0;
01318       while(s->eventq) {
01319          if (ast_carefulwrite(s->fd, s->eventq->eventdata, strlen(s->eventq->eventdata), s->writetimeout) < 0) {
01320             ret = -1;
01321             break;
01322          }
01323          eqe = s->eventq;
01324          s->eventq = s->eventq->next;
01325          free(eqe);
01326       }
01327       ast_mutex_unlock(&s->__lock);
01328       return ret;
01329    }
01330    return 0;
01331 }
01332 
01333 static int get_input(struct mansession *s, char *output)
01334 {
01335    /* output must have at least sizeof(s->inbuf) space */
01336    int res;
01337    int x;
01338    struct pollfd fds[1];
01339    char iabuf[INET_ADDRSTRLEN];
01340    for (x=1;x<s->inlen;x++) {
01341       if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
01342          /* Copy output data up to and including \r\n */
01343          memcpy(output, s->inbuf, x + 1);
01344          /* Add trailing \0 */
01345          output[x+1] = '\0';
01346          /* Move remaining data back to the front */
01347          memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
01348          s->inlen -= (x + 1);
01349          return 1;
01350       }
01351    } 
01352    if (s->inlen >= sizeof(s->inbuf) - 1) {
01353       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);
01354       s->inlen = 0;
01355    }
01356    fds[0].fd = s->fd;
01357    fds[0].events = POLLIN;
01358    do {
01359       res = poll(fds, 1, -1);
01360       if (res < 0) {
01361          if (errno == EINTR) {
01362             if (s->dead)
01363                return -1;
01364             continue;
01365          }
01366          ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
01367          return -1;
01368       } else if (res > 0) {
01369          ast_mutex_lock(&s->__lock);
01370          res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
01371          ast_mutex_unlock(&s->__lock);
01372          if (res < 1)
01373             return -1;
01374          break;
01375       }
01376    } while(1);
01377    s->inlen += res;
01378    s->inbuf[s->inlen] = '\0';
01379    return 0;
01380 }
01381 
01382 static void *session_do(void *data)
01383 {
01384    struct mansession *s = data;
01385    struct message m;
01386    char iabuf[INET_ADDRSTRLEN];
01387    int res;
01388    
01389    ast_mutex_lock(&s->__lock);
01390    ast_cli(s->fd, "Asterisk Call Manager/1.0\r\n");
01391    ast_mutex_unlock(&s->__lock);
01392    memset(&m, 0, sizeof(m));
01393    for (;;) {
01394       res = get_input(s, m.headers[m.hdrcount]);
01395       if (res > 0) {
01396          /* Strip trailing \r\n */
01397          if (strlen(m.headers[m.hdrcount]) < 2)
01398             continue;
01399          m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
01400          if (ast_strlen_zero(m.headers[m.hdrcount])) {
01401             if (process_message(s, &m))
01402                break;
01403             memset(&m, 0, sizeof(m));
01404          } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
01405             m.hdrcount++;
01406       } else if (res < 0)
01407          break;
01408    }
01409    if (s->authenticated) {
01410       if (option_verbose > 1) {
01411          if (displayconnects) 
01412             ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));    
01413       }
01414       ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
01415    } else {
01416       if (option_verbose > 1) {
01417          if ( displayconnects )
01418             ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
01419       }
01420       ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
01421    }
01422    destroy_session(s);
01423    return NULL;
01424 }
01425 
01426 static void *accept_thread(void *ignore)
01427 {
01428    int as;
01429    struct sockaddr_in sin;
01430    socklen_t sinlen;
01431    struct mansession *s;
01432    struct protoent *p;
01433    int arg = 1;
01434    int flags;
01435    pthread_attr_t attr;
01436 
01437    pthread_attr_init(&attr);
01438    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01439 
01440    for (;;) {
01441       sinlen = sizeof(sin);
01442       as = accept(asock, (struct sockaddr *)&sin, &sinlen);
01443       if (as < 0) {
01444          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
01445          continue;
01446       }
01447       p = getprotobyname("tcp");
01448       if (p) {
01449          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
01450             ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
01451          }
01452       }
01453       s = malloc(sizeof(struct mansession));
01454       if (!s) {
01455          ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
01456          continue;
01457       } 
01458       memset(s, 0, sizeof(struct mansession));
01459       memcpy(&s->sin, &sin, sizeof(sin));
01460       s->writetimeout = 100;
01461 
01462       if(! block_sockets) {
01463          /* For safety, make sure socket is non-blocking */
01464          flags = fcntl(as, F_GETFL);
01465          fcntl(as, F_SETFL, flags | O_NONBLOCK);
01466       }
01467       ast_mutex_init(&s->__lock);
01468       s->fd = as;
01469       s->send_events = -1;
01470       ast_mutex_lock(&sessionlock);
01471       s->next = sessions;
01472       sessions = s;
01473       ast_mutex_unlock(&sessionlock);
01474       if (ast_pthread_create(&s->t, &attr, session_do, s))
01475          destroy_session(s);
01476    }
01477    pthread_attr_destroy(&attr);
01478    return NULL;
01479 }
01480 
01481 static int append_event(struct mansession *s, const char *str)
01482 {
01483    struct eventqent *tmp, *prev=NULL;
01484    tmp = malloc(sizeof(struct eventqent) + strlen(str));
01485    if (tmp) {
01486       tmp->next = NULL;
01487       strcpy(tmp->eventdata, str);
01488       if (s->eventq) {
01489          prev = s->eventq;
01490          while(prev->next) 
01491             prev = prev->next;
01492          prev->next = tmp;
01493       } else {
01494          s->eventq = tmp;
01495       }
01496       return 0;
01497    }
01498    return -1;
01499 }
01500 
01501 /*! \brief  manager_event: Send AMI event to client */
01502 int manager_event(int category, char *event, char *fmt, ...)
01503 {
01504    struct mansession *s;
01505    char auth[80];
01506    char tmp[4096] = "";
01507    char *tmp_next = tmp;
01508    size_t tmp_left = sizeof(tmp) - 2;
01509    va_list ap;
01510 
01511    ast_mutex_lock(&sessionlock);
01512    for (s = sessions; s; s = s->next) {
01513       if ((s->readperm & category) != category)
01514          continue;
01515 
01516       if ((s->send_events & category) != category)
01517          continue;
01518 
01519       if (ast_strlen_zero(tmp)) {
01520          ast_build_string(&tmp_next, &tmp_left, "Event: %s\r\nPrivilege: %s\r\n",
01521                 event, authority_to_str(category, auth, sizeof(auth)));
01522          va_start(ap, fmt);
01523          ast_build_string_va(&tmp_next, &tmp_left, fmt, ap);
01524          va_end(ap);
01525          *tmp_next++ = '\r';
01526          *tmp_next++ = '\n';
01527          *tmp_next = '\0';
01528       }
01529 
01530       ast_mutex_lock(&s->__lock);
01531       if (s->busy) {
01532          append_event(s, tmp);
01533       } else if (!s->dead) {
01534          if (ast_carefulwrite(s->fd, tmp, tmp_next - tmp, s->writetimeout) < 0) {
01535             ast_log(LOG_WARNING, "Disconnecting slow (or gone) manager session!\n");
01536             s->dead = 1;
01537             pthread_kill(s->t, SIGURG);
01538          }
01539       }
01540       ast_mutex_unlock(&s->__lock);
01541    }
01542    ast_mutex_unlock(&sessionlock);
01543 
01544    return 0;
01545 }
01546 
01547 int ast_manager_unregister( char *action ) 
01548 {
01549    struct manager_action *cur = first_action, *prev = first_action;
01550 
01551    ast_mutex_lock(&actionlock);
01552    while( cur ) {       
01553       if (!strcasecmp(action, cur->action)) {
01554          prev->next = cur->next;
01555          free(cur);
01556          if (option_verbose > 1) 
01557             ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
01558          ast_mutex_unlock(&actionlock);
01559          return 0;
01560       }
01561       prev = cur;
01562       cur = cur->next;
01563    }
01564    ast_mutex_unlock(&actionlock);
01565    return 0;
01566 }
01567 
01568 static int manager_state_cb(char *context, char *exten, int state, void *data)
01569 {
01570    /* Notify managers of change */
01571    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
01572    return 0;
01573 }
01574 
01575 static int ast_manager_register_struct(struct manager_action *act)
01576 {
01577    struct manager_action *cur = first_action, *prev = NULL;
01578    int ret;
01579 
01580    ast_mutex_lock(&actionlock);
01581    while(cur) { /* Walk the list of actions */
01582       ret = strcasecmp(cur->action, act->action);
01583       if (ret == 0) {
01584          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
01585          ast_mutex_unlock(&actionlock);
01586          return -1;
01587       } else if (ret > 0) {
01588          /* Insert these alphabetically */
01589          if (prev) {
01590             act->next = prev->next;
01591             prev->next = act;
01592          } else {
01593             act->next = first_action;
01594             first_action = act;
01595          }
01596          break;
01597       }
01598       prev = cur; 
01599       cur = cur->next;
01600    }
01601    
01602    if (!cur) {
01603       if (prev)
01604          prev->next = act;
01605       else
01606          first_action = act;
01607       act->next = NULL;
01608    }
01609 
01610    if (option_verbose > 1) 
01611       ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
01612    ast_mutex_unlock(&actionlock);
01613    return 0;
01614 }
01615 
01616 /*! \brief register a new command with manager, including online help. This is 
01617    the preferred way to register a manager command */
01618 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
01619 {
01620    struct manager_action *cur;
01621 
01622    cur = malloc(sizeof(struct manager_action));
01623    if (!cur) {
01624       ast_log(LOG_WARNING, "Manager: out of memory trying to register action\n");
01625       return -1;
01626    }
01627    cur->action = action;
01628    cur->authority = auth;
01629    cur->func = func;
01630    cur->synopsis = synopsis;
01631    cur->description = description;
01632    cur->next = NULL;
01633 
01634    ast_manager_register_struct(cur);
01635 
01636    return 0;
01637 }
01638 /*! @}
01639  END Doxygen group */
01640 
01641 static int registered = 0;
01642 
01643 int init_manager(void)
01644 {
01645    struct ast_config *cfg;
01646    char *val;
01647    int oldportno = portno;
01648    static struct sockaddr_in ba;
01649    int x = 1;
01650    if (!registered) {
01651       /* Register default actions */
01652       ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
01653       ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
01654       ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
01655       ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
01656       ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
01657       ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
01658       ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
01659       ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
01660       ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
01661       ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
01662       ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
01663       ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
01664       ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
01665       ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
01666       ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
01667 
01668       ast_cli_register(&show_mancmd_cli);
01669       ast_cli_register(&show_mancmds_cli);
01670       ast_cli_register(&show_manconn_cli);
01671       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
01672       registered = 1;
01673    }
01674    portno = DEFAULT_MANAGER_PORT;
01675    displayconnects = 1;
01676    cfg = ast_config_load("manager.conf");
01677    if (!cfg) {
01678       ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
01679       return 0;
01680    }
01681    memset(&ba, 0, sizeof(ba));
01682    val = ast_variable_retrieve(cfg, "general", "enabled");
01683    if (val)
01684       enabled = ast_true(val);
01685 
01686    val = ast_variable_retrieve(cfg, "general", "block-sockets");
01687    if(val)
01688       block_sockets = ast_true(val);
01689 
01690    if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
01691       if (sscanf(val, "%d", &portno) != 1) {
01692          ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
01693          portno = DEFAULT_MANAGER_PORT;
01694       }
01695    } else if ((val = ast_variable_retrieve(cfg, "general", "portno"))) {
01696       if (sscanf(val, "%d", &portno) != 1) {
01697          ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
01698          portno = DEFAULT_MANAGER_PORT;
01699       }
01700       ast_log(LOG_NOTICE, "Use of portno in manager.conf deprecated.  Please use 'port=%s' instead.\n", val);
01701    }
01702    /* Parsing the displayconnects */
01703    if ((val = ast_variable_retrieve(cfg, "general", "displayconnects"))) {
01704          displayconnects = ast_true(val);;
01705    }
01706             
01707    
01708    ba.sin_family = AF_INET;
01709    ba.sin_port = htons(portno);
01710    memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
01711    
01712    if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
01713       if (!inet_aton(val, &ba.sin_addr)) { 
01714          ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
01715          memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
01716       }
01717    }
01718    
01719    if ((asock > -1) && ((portno != oldportno) || !enabled)) {
01720 #if 0
01721       /* Can't be done yet */
01722       close(asock);
01723       asock = -1;
01724 #else
01725       ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
01726 #endif
01727    }
01728    ast_config_destroy(cfg);
01729    
01730    /* If not enabled, do nothing */
01731    if (!enabled) {
01732       return 0;
01733    }
01734    if (asock < 0) {
01735       asock = socket(AF_INET, SOCK_STREAM, 0);
01736       if (asock < 0) {
01737          ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
01738          return -1;
01739       }
01740       setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
01741       if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
01742          ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
01743          close(asock);
01744          asock = -1;
01745          return -1;
01746       }
01747       if (listen(asock, 2)) {
01748          ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
01749          close(asock);
01750          asock = -1;
01751          return -1;
01752       }
01753       if (option_verbose)
01754          ast_verbose("Asterisk Management interface listening on port %d\n", portno);
01755       ast_pthread_create(&t, NULL, accept_thread, NULL);
01756    }
01757    return 0;
01758 }
01759 
01760 int reload_manager(void)
01761 {
01762    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
01763    return init_manager();
01764 }

Generated on Sat Sep 16 05:47:46 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7