--- main/manager.c.orig 2010-06-02 10:36:32.000000000 -0300 +++ main/manager.c 2010-06-02 10:38:47.000000000 -0300 @@ -175,46 +175,29 @@ * data. */ struct mansession_session { - /*! Execution thread */ - pthread_t t; - /*! Thread lock -- don't use in action callbacks, it's already taken care of */ - ast_mutex_t __lock; - /*! socket address */ - struct sockaddr_in sin; - /*! TCP socket */ - int fd; - /*! Whether an HTTP manager is in use */ - int inuse; - /*! Whether an HTTP session should be destroyed */ - int needdestroy; - /*! Whether an HTTP session has someone waiting on events */ - pthread_t waiting_thread; - /*! Unique manager identifer */ - uint32_t managerid; - /*! Session timeout if HTTP */ - time_t sessiontimeout; - /*! Output from manager interface */ - struct ast_dynamic_str *outputstr; - /*! Logged in username */ - char username[80]; - /*! Authentication challenge */ - char challenge[10]; - /*! Authentication status */ - int authenticated; - /*! Authorization for reading */ - int readperm; - /*! Authorization for writing */ - int writeperm; - /*! Buffer */ - char inbuf[1024]; + pthread_t t; /*! Execution thread */ + ast_mutex_t __lock; /*! Thread lock -- don't use in action callbacks, it's already taken care of */ + struct sockaddr_in sin; /*! socket address */ + int fd; /*! TCP socket */ + int inuse; /*! Whether an HTTP manager is in use */ + int needdestroy; /*! Whether an HTTP session should be destroyed */ + pthread_t waiting_thread; /*! Whether an HTTP session has someone waiting on events */ + uint32_t managerid; /*! Unique manager identifer */ + time_t sessiontimeout; /*! Session timeout if HTTP */ + struct ast_dynamic_str *outputstr; /*! Output from manager interface */ + char username[80]; /*! Logged in username */ + char challenge[10]; /*! Authentication challenge */ + int authenticated; /*! Authentication status */ + int readperm; /*! Authorization for reading */ + int writeperm; /*! Authorization for writing */ + char inbuf[1024]; /*! Buffer */ int inlen; int send_events; - int displaysystemname; /*!< Add system name to manager responses and events */ - /* Queued events that we've not had the ability to send yet */ + int displaysystemname; /*!< Add system name to manager responses and events */ + /* Queued events that we've not had the ability to send yet */ struct eventqent *eventq; - /* Timeout for ast_carefulwrite() */ - int writetimeout; - int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */ + int writetimeout; /* Timeout for ast_carefulwrite() */ + int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */ AST_LIST_ENTRY(mansession_session) list; }; @@ -267,7 +250,7 @@ if (ast_strlen_zero(res)) ast_copy_string(res, "", reslen); - + return res; } @@ -334,14 +317,15 @@ static int compress_char(char c) { c &= 0x7f; - if (c < 32) + if (c < 32) { return 0; - else if (c >= 'a' && c <= 'z') + } else if (c >= 'a' && c <= 'z') { return c - 64; - else if (c > 'z') + } else if (c > 'z') { return '_'; - else + } else { return c - 32; + } } static int variable_count_hash_fn(const void *vvc, const int flags) @@ -384,15 +368,18 @@ struct ao2_container *vco = NULL; for (v = vars; v; v = v->next) { - if (!dest && !strcasecmp(v->name, "ajaxdest")) + if (!dest && !strcasecmp(v->name, "ajaxdest")) { dest = v->value; - else if (!objtype && !strcasecmp(v->name, "ajaxobjtype")) + } else if (!objtype && !strcasecmp(v->name, "ajaxobjtype")) { objtype = v->value; + } } + if (!dest) dest = "unknown"; if (!objtype) objtype = "generic"; + for (x = 0; in[x]; x++) { if (in[x] == ':') colons++; @@ -459,6 +446,7 @@ } } } + if (inobj) ast_build_string(&tmp, &len, " />\n"); if (vco) @@ -483,8 +471,10 @@ } len = strlen(in) + colons * 40 + breaks * 40; /* , "
*/ out = ast_malloc(len); + if (!out) return 0; + tmp = out; while (*in) { var = in; @@ -510,6 +500,7 @@ } } } + return out; } @@ -540,10 +531,10 @@ va_start(ap, fmt); ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap); va_end(ap); - - if (s->fd > -1) + + if (s->fd > -1) { ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->session->writetimeout); - else { + } else { if (!s->session->outputstr && !(s->session->outputstr = ast_calloc(1, sizeof(*s->session->outputstr)))) { ast_mutex_unlock(&s->session->__lock); return; @@ -881,15 +872,18 @@ void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg) { - const char *id = astman_get_header(m,"ActionID"); + const char *id = astman_get_header(m, "ActionID"); astman_append(s, "Response: %s\r\n", resp); + if (!ast_strlen_zero(id)) astman_append(s, "ActionID: %s\r\n", id); - if (msg) + + if (msg) { astman_append(s, "Message: %s\r\n\r\n", msg); - else + } else { astman_append(s, "\r\n"); + } } void astman_send_ack(struct mansession *s, const struct message *m, char *msg) @@ -1435,10 +1429,11 @@ int res; res = set_eventmask(s->session, mask); - if (res > 0) + if (res > 0) { astman_send_response(s, m, "Events On", NULL); - else if (res == 0) + } else if (res == 0) { astman_send_response(s, m, "Events Off", NULL); + } return 0; } @@ -1581,38 +1576,52 @@ return 0; } +static char mandescr_status[] = +"Description: Lists channel status along with requested channel vars.\n" +"Variables: (Names marked with * are required)\n" +" *Channel: Name of the channel to query for status\n" +" ActionID: Optional ID for this transaction\n" +"Will return the status information of each channel along with the\n" +"value for the specified channel variables.\n"; /*! \brief Manager "status" command to show channels */ -/* Needs documentation... */ static int action_status(struct mansession *s, const struct message *m) { - const char *id = astman_get_header(m,"ActionID"); - const char *name = astman_get_header(m,"Channel"); + const char *id = astman_get_header(m, "ActionID"); + const char *name = astman_get_header(m, "Channel"); char idText[256] = ""; - struct ast_channel *c; char bridge[256]; + struct ast_channel *c = NULL; struct timeval now = ast_tvnow(); long elapsed_seconds = 0; int all = ast_strlen_zero(name); /* set if we want all channels */ - if (!ast_strlen_zero(id)) + if (!ast_strlen_zero(id)) { snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); - if (all) + } else { + idText[0] = '\0'; + } + + if (all) { c = ast_channel_walk_locked(NULL); - else { + } else { c = ast_get_channel_by_name_locked(name); if (!c) { astman_send_error(s, m, "No such channel"); return 0; } } + astman_send_ack(s, m, "Channel status will follow"); + /* if we look by name, we break after the first iteration */ while (c) { - if (c->_bridge) + if (c->_bridge) { snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name); - else + } else { bridge[0] = '\0'; + } + if (c->pbx) { if (c->cdr) { elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec; @@ -1667,10 +1676,12 @@ break; c = ast_channel_walk_locked(c); } + astman_append(s, "Event: StatusComplete\r\n" "%s" "\r\n",idText); + return 0; } @@ -2178,9 +2189,11 @@ { struct eventqent *eqe; int ret = 0; + ast_mutex_lock(&s->session->__lock); if (!s->session->eventq) s->session->eventq = master_eventq; + while(s->session->eventq->next) { eqe = s->session->eventq->next; if ((s->session->authenticated && (s->session->readperm & eqe->category) == eqe->category) && @@ -2188,15 +2201,17 @@ if (s->fd > -1) { if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->session->writetimeout) < 0) ret = -1; - } else if (!s->session->outputstr && !(s->session->outputstr = ast_calloc(1, sizeof(*s->session->outputstr)))) + } else if (!s->session->outputstr && !(s->session->outputstr = ast_calloc(1, sizeof(*s->session->outputstr)))) { ret = -1; - else + } else { ast_dynamic_str_append(&s->session->outputstr, 0, "%s", eqe->eventdata); + } } unuse_eventqent(s->session->eventq); s->session->eventq = eqe; } ast_mutex_unlock(&s->session->__lock); + return ret; } @@ -2288,18 +2303,20 @@ } else astman_send_error(s, m, "Authentication Required"); } else { - if (!strcasecmp(action, "Login")) + if (!strcasecmp(action, "Login")) { astman_send_ack(s, m, "Already logged in"); - else { + } else { ast_rwlock_rdlock(&actionlock); for (tmp = first_action; tmp; tmp = tmp->next) { if (strcasecmp(action, tmp->action)) continue; + if ((s->session->writeperm & tmp->authority) == tmp->authority) { if (tmp->func(s, m)) ret = -1; - } else + } else { astman_send_error(s, m, "Permission denied"); + } break; } ast_rwlock_unlock(&actionlock); @@ -2307,6 +2324,7 @@ astman_send_error(s, m, "Invalid/unknown command"); } } + if (ret) return ret; return process_events(s); @@ -2788,32 +2806,41 @@ }; static char *contenttype[] = { "plain", "html", "xml" }; -static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength) +static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, + struct ast_variable *params, int *status, char **title, int *contentlength) { struct mansession_session *s = NULL; struct mansession ss = { .session = NULL, }; uint32_t ident = 0; char workspace[512]; char cookie[128]; + char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */ + size_t len = sizeof(workspace); int blastaway = 0; char *c = workspace; char *retval = NULL; struct ast_variable *v; + struct message m = { 0 }; + char tmp[80]; + unsigned int x; + size_t hdrlen; + for (v = params; v; v = v->next) { if (!strcasecmp(v->name, "mansession_id")) { sscanf(v->value, "%30x", &ident); break; } } - + if (!(s = find_session(ident))) { /* Create new session */ if (!(s = ast_calloc(1, sizeof(*s)))) { *status = 500; goto generic_callback_out; } + memcpy(&s->sin, requestor, sizeof(s->sin)); s->fd = -1; s->waiting_thread = AST_PTHREADT_NULL; @@ -2821,6 +2848,7 @@ ast_mutex_init(&s->__lock); ast_mutex_lock(&s->__lock); s->inuse = 1; + /*!\note There is approximately a 1 in 1.8E19 chance that the following * calculation will produce 0, which is an invalid ID, but due to the * properties of the rand() function (and the constantcy of s), that @@ -2838,130 +2866,152 @@ AST_LIST_UNLOCK(&sessions); } - /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */ - time(&s->sessiontimeout); - if (!s->authenticated && (httptimeout > 5)) - s->sessiontimeout += 5; - else - s->sessiontimeout += httptimeout; + /* Reset HTTP timeout. If we're not authenticated, keep it extremely short */ + s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5); + ss.session = s; ast_mutex_unlock(&s->__lock); - ss.f = tmpfile(); - ss.fd = fileno(ss.f); + ss.fd = mkstemp(template); /* create a temporary file for command output */ + unlink(template); + ss.f = fdopen(ss.fd, "w+"); - if (s) { - struct message m = { 0 }; - char tmp[80]; - unsigned int x; - size_t hdrlen; - - for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) { - hdrlen = strlen(v->name) + strlen(v->value) + 3; - m.headers[m.hdrcount] = alloca(hdrlen); - snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value); - m.hdrcount = x + 1; + for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) { + hdrlen = strlen(v->name) + strlen(v->value) + 3; + m.headers[m.hdrcount] = alloca(hdrlen); + snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value); + if (option_verbose > 3) { + ast_verbose(VERBOSE_PREFIX_4 "HTTP Manager add header %s\n", m.headers[m.hdrcount]); } + m.hdrcount = x + 1; + } - if (process_message(&ss, &m)) { - if (s->authenticated) { - if (option_verbose > 1) { - if (displayconnects) - ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr)); - } - ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr)); + if ( process_message(&ss, &m) ) { + if (s->authenticated) { + if (option_verbose > 1) { + if (displayconnects) + ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr)); + } + ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr)); + } else { + if (option_verbose > 1) { + if (displayconnects) + ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr)); + } + ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr)); + } + s->needdestroy = 1; + } + + sprintf(tmp, "%08x", s->managerid); + ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]); + ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie))); + + if (format == FORMAT_XML) { + ast_build_string(&c, &len, "\n"); + } else if (format == FORMAT_HTML) { + ast_build_string(&c, &len, "\r\nAsterisk™ Manager Interface\r\n" + "\r\n\r\n" + "\r\n"); + } + + ast_mutex_lock(&s->__lock); + if (ss.f != NULL && ss.fd > -1) { + char *buf; + size_t l; + + /* Ensure buffer is NULL-terminated */ + fprintf(ss.f, "%c", 0); + + if ((l = lseek(ss.fd, 0, SEEK_END)) > 0) { + /* Trying to stop the core dump */ + if ( l > 10480765 ) { + ast_log(LOG_WARNING, "Buffer Overflow Detected, memory size exceeded\n"); + fclose(ss.f); + ss.f = NULL; + ss.fd = -1; + ast_mutex_unlock(&s->__lock); + *status = 500; + goto generic_callback_out; + } + + if ( MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, ss.fd, 0)) ) { + ast_log(LOG_WARNING, "mmap failed. Manager request output was not processed\n"); } else { - if (option_verbose > 1) { - if (displayconnects) - ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr)); + /* ensure that string is null-terminated */ + char *newbuf = ast_malloc(l + 1); + memcpy(newbuf, buf, l); + newbuf[l] = '\0'; + char *tmpbuf; + + if (format == FORMAT_XML) { + tmpbuf = xml_translate(newbuf, params); + } else if (format == FORMAT_HTML) { + tmpbuf = html_translate(newbuf); + } else { + tmpbuf = newbuf; } - ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr)); + + if (tmpbuf) { + size_t wlen, tlen; + if ((retval = malloc((wlen = strlen(workspace)) + (tlen = strlen(tmpbuf)) + 128))) { + strcpy(retval, workspace); + strcpy(retval + wlen, tmpbuf); + c = retval + wlen + tlen; + /* Leftover space for footer, if any */ + len = 120; + } + } + + if (tmpbuf != newbuf) + free(tmpbuf); + + free(s->outputstr); + s->outputstr = NULL; + munmap(buf, l); + free(newbuf); } - s->needdestroy = 1; } - ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]); - sprintf(tmp, "%08x", s->managerid); - ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie))); - if (format == FORMAT_HTML) - ast_build_string(&c, &len, "Asterisk™ Manager Interface"); + fclose(ss.f); + ss.f = NULL; + ss.fd = -1; + } else if (s->outputstr) { + char *tmp; if (format == FORMAT_XML) { - ast_build_string(&c, &len, "\n"); + tmp = xml_translate(s->outputstr->str, params); } else if (format == FORMAT_HTML) { - ast_build_string(&c, &len, "

  Asterisk Manager Information  

\r\n"); - ast_build_string(&c, &len, "\r\n"); + tmp = html_translate(s->outputstr->str); + } else { + tmp = s->outputstr->str; } - ast_mutex_lock(&s->__lock); - if (ss.fd > -1) { - char *buf; - size_t l; - - /* Ensure buffer is NULL-terminated */ - fprintf(ss.f, "%c", 0); - - if ((l = lseek(ss.fd, 0, SEEK_END)) > 0) { - if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, ss.fd, 0))) { - ast_log(LOG_WARNING, "mmap failed. Manager request output was not processed\n"); - } else { - char *tmpbuf; - if (format == FORMAT_XML) - tmpbuf = xml_translate(buf, params); - else if (format == FORMAT_HTML) - tmpbuf = html_translate(buf); - else - tmpbuf = buf; - if (tmpbuf) { - size_t wlen, tlen; - if ((retval = malloc((wlen = strlen(workspace)) + (tlen = strlen(tmpbuf)) + 128))) { - strcpy(retval, workspace); - strcpy(retval + wlen, tmpbuf); - c = retval + wlen + tlen; - /* Leftover space for footer, if any */ - len = 120; - } - } - if (tmpbuf != buf) - free(tmpbuf); - free(s->outputstr); - s->outputstr = NULL; - munmap(buf, l); - } - } - fclose(ss.f); - ss.f = NULL; - ss.fd = -1; - } else if (s->outputstr) { - char *tmp; - if (format == FORMAT_XML) - tmp = xml_translate(s->outputstr->str, params); - else if (format == FORMAT_HTML) - tmp = html_translate(s->outputstr->str); - else - tmp = s->outputstr->str; - if (tmp) { - retval = malloc(strlen(workspace) + strlen(tmp) + 128); - if (retval) { - strcpy(retval, workspace); - strcpy(retval + strlen(retval), tmp); - c = retval + strlen(retval); - len = 120; - } + + if (tmp) { + retval = malloc(strlen(workspace) + strlen(tmp) + 128); + if (retval) { + strcpy(retval, workspace); + strcpy(retval + strlen(retval), tmp); + c = retval + strlen(retval); + len = 120; } - if (tmp != s->outputstr->str) - free(tmp); - free(s->outputstr); - s->outputstr = NULL; } - ast_mutex_unlock(&s->__lock); - /* Still okay because c would safely be pointing to workspace even - if retval failed to allocate above */ - if (format == FORMAT_XML) { - ast_build_string(&c, &len, "\n"); - } else if (format == FORMAT_HTML) - ast_build_string(&c, &len, "

  Manager Tester

\r\n"); - } else { - *status = 500; - *title = strdup("Server Error"); + + if (tmp != s->outputstr->str) { + free(tmp); + } + + free(s->outputstr); + s->outputstr = NULL; } + ast_mutex_unlock(&s->__lock); + + /* Still okay because c would safely be pointing to workspace even + if retval failed to allocate above */ + if (format == FORMAT_XML) { + ast_build_string(&c, &len, "
\n"); + } else if (format == FORMAT_HTML) { + ast_build_string(&c, &len, "\r\n"); + } + ast_mutex_lock(&s->__lock); if (s->needdestroy) { if (s->inuse == 1) { @@ -2973,8 +3023,9 @@ pthread_kill(s->waiting_thread, SIGURG); s->inuse--; } - } else + } else { s->inuse--; + } ast_mutex_unlock(&s->__lock); if (blastaway) @@ -3043,7 +3094,7 @@ ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events); ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff); ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup); - ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" ); + ast_manager_register2("Status", EVENT_FLAG_CALL, action_status, "Lists channel status", mandescr_status ); ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar ); ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar ); ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);