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