Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

manager.c

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

Generated on Wed Mar 16 20:08:35 2005 for Asterisk by  doxygen 1.4.0