size_t AbyssServer::Session::Impl::contentLength() const { try { const char * const contentLength = RequestHeaderValue(this->cSessionP, "content-length"); if (contentLength == NULL) throwf("Header is not present"); else { if (contentLength[0] == '\0') throwf("The value is a null string"); else { unsigned long contentLengthValue; char * tail; contentLengthValue = strtoul(contentLength, &tail, 10); if (*tail != '\0') throwf("There's non-numeric crap in the value: '%s'", tail); else if ((unsigned long)(size_t)contentLengthValue != contentLengthValue) throwf("Value is too large; " "we can't even do arithmetic on it: '%s'", contentLength); else return (size_t)contentLengthValue; } } } catch (exception const& e) { throw AbyssServer::Exception( 400, string("Invalid content-length header field. ") + e.what()); } }
static void validateContentType(TSession * const httpRequestP, unsigned int * const httpErrorP) { /*---------------------------------------------------------------------------- If the client didn't specify a content-type of "text/xml", return "400 Bad Request". We can't allow the client to default this header, because some firewall software may rely on all XML-RPC requests using the POST method and a content-type of "text/xml". -----------------------------------------------------------------------------*/ const char * const content_type = RequestHeaderValue(httpRequestP, "content-type"); if (content_type == NULL) *httpErrorP = 400; else { const char * const sempos = strchr(content_type, ';'); unsigned int baselen; /* Length of the base portion of the content type, e.g. "text/xml" int "text/xml;charset=utf-8" */ if (sempos) baselen = sempos - content_type; else baselen = strlen(content_type); if (!xmlrpc_strneq(content_type, "text/xml", baselen)) *httpErrorP = 400; else *httpErrorP = 0; } }
bool RequestAuth(TSession * const sessionP, const char * const credential, const char * const user, const char * const pass) { /*---------------------------------------------------------------------------- Authenticate requester, in a very simplistic fashion. If the request executing on session *sessionP specifies basic authentication (via Authorization header) with username 'user', password 'pass', then return TRUE. Else, return FALSE and set up an authorization failure response (HTTP response status 401) that says user must supply an identity in the 'credential' domain. When we return TRUE, we also set the username in the request info for the session to 'user' so that a future SessionGetRequestInfo can get it. -----------------------------------------------------------------------------*/ bool authorized; char * authHdrPtr; authHdrPtr = RequestHeaderValue(sessionP, "authorization"); if (authHdrPtr) { const char * authType; NextToken((const char **)&authHdrPtr); GetTokenConst(&authHdrPtr, &authType); authType = GetToken(&authHdrPtr); if (authType) { if (xmlrpc_strcaseeq(authType, "basic")) { const char * userPass; char userPassEncoded[80]; NextToken((const char **)&authHdrPtr); xmlrpc_asprintf(&userPass, "%s:%s", user, pass); xmlrpc_base64Encode(userPass, userPassEncoded); xmlrpc_strfree(userPass); if (xmlrpc_streq(authHdrPtr, userPassEncoded)) { sessionP->requestInfo.user = strdup(user); authorized = TRUE; } else authorized = FALSE; } else authorized = FALSE; } else authorized = FALSE; } else authorized = FALSE; if (!authorized) { const char * hdrValue; xmlrpc_asprintf(&hdrValue, "Basic realm=\"%s\"", credential); ResponseAddField(sessionP, "WWW-Authenticate", hdrValue); xmlrpc_strfree(hdrValue); ResponseStatus(sessionP, 401); } return authorized; }
static void validateContentType(TSession * const httpRequestP, const char ** const errorP) { /*---------------------------------------------------------------------------- If the client didn't specify a content-type of "text/xml", fail. We can't allow the client to default this header, because some firewall software may rely on all XML-RPC requests using the POST method and a content-type of "text/xml".x -----------------------------------------------------------------------------*/ const char * const content_type = RequestHeaderValue(httpRequestP, "content-type"); if (content_type == NULL) xmlrpc_asprintf(errorP, "You did not supply a content-type HTTP header"); else { const char * const sempos = strchr(content_type, ';'); unsigned int baselen; /* Length of the base portion of the content type, e.g. "text/xml" int "text/xml;charset=utf-8" */ if (sempos) baselen = sempos - content_type; else baselen = strlen(content_type); if (!xmlrpc_strneq(content_type, "text/xml", baselen)) xmlrpc_asprintf(errorP, "Your content-type HTTP header value '%s' " "does not have a base type of 'text/xml'", content_type); else *errorP = NULL; } }
abyss_bool RequestAuth(TSession *r,char *credential,char *user,char *pass) { char *p,*x; char z[80],t[80]; p=RequestHeaderValue(r,"authorization"); if (p) { NextToken((const char **)&p); x=GetToken(&p); if (x) { if (strcasecmp(x,"basic")==0) { NextToken((const char **)&p); sprintf(z,"%s:%s",user,pass); Base64Encode(z,t); if (strcmp(p,t)==0) { r->request_info.user=strdup(user); return TRUE; }; }; } }; sprintf(z,"Basic realm=\"%s\"",credential); ResponseAddField(r,"WWW-Authenticate",z); ResponseStatus(r,401); return FALSE; }
static void processContentLength(TSession * const httpRequestP, size_t * const inputLenP, bool * const missingP, const char ** const errorP) { /*---------------------------------------------------------------------------- Make sure the content length is present and non-zero. This is technically required by XML-RPC, but we only enforce it because we don't want to figure out how to safely handle HTTP < 1.1 requests without it. -----------------------------------------------------------------------------*/ const char * const content_length = RequestHeaderValue(httpRequestP, "content-length"); if (content_length == NULL) { *missingP = TRUE; *errorP = NULL; } else { *missingP = FALSE; *inputLenP = 0; /* quiet compiler warning */ if (content_length[0] == '\0') xmlrpc_asprintf(errorP, "The value in your content-length " "HTTP header value is a null string"); else { unsigned long contentLengthValue; char * tail; contentLengthValue = strtoul(content_length, &tail, 10); if (*tail != '\0') xmlrpc_asprintf(errorP, "There's non-numeric crap in " "the value of your content-length " "HTTP header: '%s'", tail); else if (contentLengthValue < 1) xmlrpc_asprintf(errorP, "According to your content-length " "HTTP header, your request is empty (zero " "length)"); else if ((unsigned long)(size_t)contentLengthValue != contentLengthValue) xmlrpc_asprintf(errorP, "According to your content-length " "HTTP header, your request is too big to " "process; we can't even do arithmetic on its " "size: %s bytes", content_length); else { *errorP = NULL; *inputLenP = (size_t)contentLengthValue; } } } }
static void storeCookies(TSession * const httpRequestP, unsigned int * const httpErrorP) { /*---------------------------------------------------------------------------- Get the cookie settings from the HTTP headers and remember them for use in responses. -----------------------------------------------------------------------------*/ const char * const cookie = RequestHeaderValue(httpRequestP, "cookie"); if (cookie) { /* Setting the value in an environment variable doesn't make any sense. So for now, cookie code is disabled. -Bryan 04.10.03. setenv("HTTP_COOKIE", cookie, 1); */ } /* TODO: parse HTTP_COOKIE to find auth pair, if there is one */ *httpErrorP = 0; }
static void processContentLength(TSession * const httpRequestP, size_t * const inputLenP, unsigned int * const httpErrorP) { /*---------------------------------------------------------------------------- Make sure the content length is present and non-zero. This is technically required by XML-RPC, but we only enforce it because we don't want to figure out how to safely handle HTTP < 1.1 requests without it. If the length is missing, return "411 Length Required". -----------------------------------------------------------------------------*/ const char * const content_length = RequestHeaderValue(httpRequestP, "content-length"); if (content_length == NULL) *httpErrorP = 411; else { if (content_length[0] == '\0') *httpErrorP = 400; else { unsigned long contentLengthValue; char * tail; contentLengthValue = strtoul(content_length, &tail, 10); if (*tail != '\0') /* There's non-numeric crap in the length */ *httpErrorP = 400; else if (contentLengthValue < 1) *httpErrorP = 400; else if ((unsigned long)(size_t)contentLengthValue != contentLengthValue) *httpErrorP = 400; else { *httpErrorP = 0; *inputLenP = (size_t)contentLengthValue; } } } }
static bool notRecentlyModified(TSession *const sessionP, time_t const fileModTime) { bool retval; const char *imsHdr; imsHdr = RequestHeaderValue(sessionP, "if-modified-since"); if (imsHdr) { bool valid; time_t datetime; DateDecode(imsHdr, &valid, &datetime); if (valid) { if (MIN(fileModTime, sessionP->date) <= datetime) retval = TRUE; else retval = FALSE; } else retval = FALSE; } else retval = FALSE; return retval; }
abyss_bool RequestAuth(TSession * const sessionP, const char * const credential, const char * const user, const char * const pass) { /*---------------------------------------------------------------------------- Authenticate requester, in a very simplistic fashion. If the request executing on session *sessionP specifies basic authentication (via Authorization header) with username 'user', password 'pass', then return true. Else, return false and set up an authorization failure response (HTTP response status 401) that says user must supply an identity in the 'credential' domain. When we return true, we also set the username in the request info for the session to 'user' so that a future SessionGetRequestInfo can get it. -----------------------------------------------------------------------------*/ bool authorized; const char * authValue; authValue = RequestHeaderValue(sessionP, "authorization"); if (authValue) { char * const valueBuffer = malloc(strlen(authValue)); /* A buffer we can mangle as we parse the authorization: value */ if (!authValue) /* Should return error, but we have no way to do that */ authorized = false; else { const char * authType; char * authHdrPtr; strcpy(valueBuffer, authValue); authHdrPtr = &valueBuffer[0]; NextToken((const char **)&authHdrPtr); GetTokenConst(&authHdrPtr, &authType); if (authType) { if (xmlrpc_strcaseeq(authType, "basic")) { const char * userPass; char userPassEncoded[80]; NextToken((const char **)&authHdrPtr); xmlrpc_asprintf(&userPass, "%s:%s", user, pass); xmlrpc_base64Encode(userPass, userPassEncoded); xmlrpc_strfree(userPass); if (xmlrpc_streq(authHdrPtr, userPassEncoded)) { sessionP->requestInfo.user = xmlrpc_strdupsol(user); authorized = true; } else authorized = false; } else authorized = false; } else authorized = false; free(valueBuffer); } } else authorized = false; if (!authorized) { const char * hdrValue; xmlrpc_asprintf(&hdrValue, "Basic realm=\"%s\"", credential); ResponseAddField(sessionP, "WWW-Authenticate", hdrValue); xmlrpc_strfree(hdrValue); ResponseStatus(sessionP, 401); } return authorized; }
abyss_bool handler_hook(TSession * r) { switch_stream_handle_t stream = { 0 }; char *command; int i; char *fs_user = NULL, *fs_domain = NULL; char *path_info = NULL; abyss_bool ret = TRUE; int html = 0, text = 0, xml = 0, api = 0; const char *api_str; const char *uri = 0; TRequestInfo *info = 0; switch_event_t *evnt = 0; /* shortcut to stream.param_event */ if (!r || !(info = &r->requestInfo) || !(uri = info->uri)) { return FALSE; } stream.data = r; stream.write_function = http_stream_write; stream.raw_write_function = http_stream_raw_write; if ((command = strstr(uri, "/api/"))) { command += 5; api++; } else if ((command = strstr(uri, "/webapi/"))) { command += 8; html++; } else if ((command = strstr(uri, "/txtapi/"))) { command += 8; text++; } else if ((command = strstr(uri, "/xmlapi/"))) { command += 8; xml++; } else { return FALSE; /* 404 */ } if ((path_info = strchr(command, '/'))) { *path_info++ = '\0'; } for (i = 0; i < r->responseHeaderFields.size; i++) { TTableItem *ti = &r->responseHeaderFields.item[i]; if (!strcasecmp(ti->name, "freeswitch-user")) { fs_user = ti->value; } else if (!strcasecmp(ti->name, "freeswitch-domain")) { fs_domain = ti->value; } } if (!is_authorized(r, command)) { ret = TRUE; goto end; } /* auth: */ if (switch_event_create(&stream.param_event, SWITCH_EVENT_API) == SWITCH_STATUS_SUCCESS) { const char *const content_length = RequestHeaderValue(r, "content-length"); evnt = stream.param_event; if (html) { switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "Content-Type", "text/html"); } else if (text) { switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "Content-Type", "text/plain"); } else if (xml) { switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "Content-Type", "text/xml"); } if (api) { switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "HTTP-API", "api"); } if (fs_user) switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "FreeSWITCH-User", fs_user); if (fs_domain) switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "FreeSWITCH-Domain", fs_domain); if (path_info) switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "HTTP-Path-Info", path_info); if (info->host) switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "HTTP-HOST", info->host); if (info->from) switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "HTTP-FROM", info->from); if (info->useragent) switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "HTTP-USER-AGENT", info->useragent); if (info->referer) switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "HTTP-REFERER", info->referer); if (info->requestline) switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "HTTP-REQUESTLINE", info->requestline); if (info->user) switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "HTTP-USER", info->user); if (info->port) switch_event_add_header(evnt, SWITCH_STACK_BOTTOM, "HTTP-PORT", "%u", info->port); { char *q, *qd; char *next; char *query = (char *) info->query; char *name, *val; char qbuf[8192] = ""; /* first finish reading from the socket if post method was used*/ if (info->method == m_post && content_length) { int len = atoi(content_length); int qlen = 0; if (len > 0) { int succeeded = TRUE; char *qp = qbuf; char *readError; do { int blen = r->connP->buffersize - r->connP->bufferpos; if ((qlen + blen) > len) { blen = len - qlen; } qlen += blen; if (qlen > sizeof(qbuf)) { break; } memcpy(qp, r->connP->buffer.b + r->connP->bufferpos, blen); qp += blen; if (qlen >= len) { break; } ConnRead(r->connP, 2000, NULL, NULL, &readError); if (readError) { succeeded = FALSE; free(readError); } } while (succeeded); query = qbuf; } } /* parse query and add kv-pairs as event headers */ /* a kv pair starts with '&', '+' or \0 mark the end */ if (query) { switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "HTTP-QUERY", query); qd = strdup(query); } else { qd = strdup(uri); } switch_assert(qd != NULL); q = qd; next = q; do { char *p; if (next = strchr(next, '&')) { if (!query) { /* pass kv pairs from uri to query */ /* "?" is absent in url so parse uri */ *((char *)uri + (next - q - 1)) = '\0'; query = next; switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "HTTP-QUERY", next); /* and strip uri */ /* the start of first kv pair marks the end of uri */ /* to prevent kv-pairs confusing fs api commands */ /* that have arguments separated by space */ } *next++ = '\0'; } for (p = q; p && *p; p++) { if (*p == '+') { *p = ' '; } } /* hmmm, get method requests are already decoded ... */ switch_url_decode(q); name = q; if ((val = strchr(name, '='))) { *val++ = '\0'; switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, name, val); } q = next; } while (q != NULL); free(qd); } } switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "HTTP-URI", uri); /* We made it this far, always OK */ if (!HTTPWrite(r, "HTTP/1.1 200 OK\r\n", (uint32_t) strlen("HTTP/1.1 200 OK\r\n"))) { return TRUE; } ResponseAddField(r, "Connection", "close"); /* generation of the date field */ if (evnt) { ResponseAddField(r, "Date", switch_event_get_header(evnt, "Event-Date-GMT")); } else { const char *dateValue; DateToString(r->date, &dateValue); if (dateValue) { ResponseAddField(r, "Date", dateValue); free((void *)dateValue); } } /* Generation of the server field */ ResponseAddField(r, "Server", "FreeSWITCH-" SWITCH_VERSION_FULL "-mod_xml_rpc"); if (html) { ResponseAddField(r, "Content-Type", "text/html"); } else if (text) { ResponseAddField(r, "Content-Type", "text/plain"); } else if (xml) { ResponseAddField(r, "Content-Type", "text/xml"); } for (i = 0; i < r->responseHeaderFields.size; i++) { TTableItem *ti = &r->responseHeaderFields.item[i]; char *header = switch_mprintf("%s: %s\r\n", ti->name, ti->value); if (!ConnWrite(r->connP, header, (uint32_t) strlen(header))) { switch_safe_free(header); return TRUE; } switch_safe_free(header); } /* send end http header */ if (html||text||xml) { if (!ConnWrite(r->connP, CRLF, 2)) { return TRUE; } } else { /* content-type and end of http header will be streamed by fs api or http_stream_write */ } if (switch_stristr("unload", command) && switch_stristr("mod_xml_rpc", info->query)) { command = "bgapi"; api_str = "unload mod_xml_rpc"; } else if (switch_stristr("reload", command) && switch_stristr("mod_xml_rpc", info->query)) { command = "bgapi"; api_str = "reload mod_xml_rpc"; } else { api_str = info->query; } /* TODO (maybe): take "refresh=xxx" out of query as to not confuse fs api commands */ /* execute actual fs api command */ /* fs api command will write to stream, calling http_stream_write / http_stream_raw_write */ /* switch_api_execute will stream INVALID COMMAND before it fails */ switch_api_execute(command, api_str, NULL, &stream); r->responseStarted = TRUE; ResponseStatus(r, 200); /* we don't want an assertion failure */ r->requestInfo.keepalive = 0; end: return ret; }
static abyss_bool http_directory_auth(TSession *r, char *domain_name) { char *p = NULL; char *x = NULL; char z[256] = "", t[80] = ""; char user[512] = "" ; char *pass = NULL; const char *mypass1 = NULL, *mypass2 = NULL; const char *box = NULL; int at = 0; char *dp = NULL; abyss_bool rval = FALSE; char *dup_domain = NULL; p = RequestHeaderValue(r, "authorization"); if (p) { NextToken((const char **const) &p); x = GetToken(&p); if (x) { if (!strcasecmp(x, "basic")) { NextToken((const char **const) &p); switch_b64_decode(p, user, sizeof(user)); if ((pass = strchr(user, ':'))) { *pass++ = '\0'; } if ((dp = strchr(user, '@'))) { *dp++ = '\0'; domain_name = dp; at++; } if (!domain_name) { if (globals.virtual_host) { if ((domain_name = (char *) r->requestInfo.host)) { if (!strncasecmp(domain_name, "www.", 3)) { domain_name += 4; } } } if (!domain_name) { if (globals.default_domain) { domain_name = globals.default_domain; } else { if ((dup_domain = switch_core_get_variable_dup("domain"))) { domain_name = dup_domain; } } } } if (zstr(user) || zstr(domain_name)) { goto fail; } if (!zstr(globals.realm) && !zstr(globals.user) && !zstr(globals.pass)) { if (at) { switch_snprintf(z, sizeof(z), "%s@%s:%s", globals.user, globals.realm, globals.pass); } else { switch_snprintf(z, sizeof(z), "%s:%s", globals.user, globals.pass); } xmlrpc_base64Encode(z, t); if (!strcmp(p, t)) { goto authed; } } if (!user_attributes(user, domain_name, &mypass1, &mypass2, &box, NULL)) { goto fail; } if (!zstr(mypass2) && !strcasecmp(mypass2, "user-choose")) { switch_safe_free(mypass2); } if (!mypass1) { goto authed; } else { if (at) { switch_snprintf(z, sizeof(z), "%s@%s:%s", user, domain_name, mypass1); } else { switch_snprintf(z, sizeof(z), "%s:%s", user, mypass1); } xmlrpc_base64Encode(z, t); if (!strcmp(p, t)) { goto authed; } if (mypass2) { if (at) { switch_snprintf(z, sizeof(z), "%s@%s:%s", user, domain_name, mypass2); } else { switch_snprintf(z, sizeof(z), "%s:%s", user, mypass2); } xmlrpc_base64Encode(z, t); if (!strcmp(p, t)) { goto authed; } } if (box) { if (at) { switch_snprintf(z, sizeof(z), "%s@%s:%s", box, domain_name, mypass1); } else { switch_snprintf(z, sizeof(z), "%s:%s", box, mypass1); } xmlrpc_base64Encode(z, t); if (!strcmp(p, t)) { goto authed; } if (mypass2) { if (at) { switch_snprintf(z, sizeof(z), "%s@%s:%s", box, domain_name, mypass2); } else { switch_snprintf(z, sizeof(z), "%s:%s", box, mypass2); } xmlrpc_base64Encode(z, t); if (!strcmp(p, t)) { goto authed; } } } } goto fail; authed: switch_snprintf(z, sizeof(z), "%s@%s", (box ? box : user), domain_name); r->requestInfo.user = strdup(z); ResponseAddField(r, "freeswitch-user", (box ? box : user)); ResponseAddField(r, "freeswitch-domain", domain_name); rval = TRUE; goto done; } } } fail: switch_snprintf(z, sizeof(z), "Basic realm=\"%s\"", domain_name ? domain_name : globals.realm); ResponseAddField(r, "WWW-Authenticate", z); ResponseStatus(r, 401); done: switch_safe_free(mypass1); switch_safe_free(mypass2); switch_safe_free(box); switch_safe_free(dup_domain); return rval; }
abyss_bool websocket_hook(TSession *r) { wsh_t *wsh; int ret; int i; ws_opcode_t opcode; uint8_t *data; switch_event_node_t *nodes[MAX_EVENT_BIND_SLOTS]; int node_count = 0; char *p; char *key = NULL; char *version = NULL; char *proto = NULL; char *upgrade = NULL; for (i = 0; i < r->requestHeaderFields.size; i++) { TTableItem * const item = &r->requestHeaderFields.item[i]; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "headers %s: %s\n", item->name, item->value); } key = RequestHeaderValue(r, "sec-websocket-key"); version = RequestHeaderValue(r, "sec-websocket-version"); proto = RequestHeaderValue(r, "sec-websocket-protocol"); upgrade = RequestHeaderValue(r, "upgrade"); if (!key || !version || !proto || !upgrade) return FALSE; if (strncasecmp(upgrade, "websocket", 9) || strncasecmp(proto, "websocket", 9)) return FALSE; wsh = ws_init(r); if (!wsh) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "websocket error %d\n", ret); return FALSE; } ret = ws_handshake_kvp(wsh, key, version, proto); if (ret < 0) wsh->down = 1; if (ret != 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "handshake error %d\n", ret); return FALSE; } if (switch_event_bind_removable("websocket", SWITCH_EVENT_CUSTOM, "websocket::stophook", stop_hook_event_handler, wsh, &nodes[node_count++]) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't bind!\n"); node_count--; } while (!wsh->down) { int bytes = ws_read_frame(wsh, &opcode, &data); if (bytes < 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%d %s\n", opcode, (char *)data); switch_yield(100000); continue; } switch (opcode) { case WSOC_CLOSE: ws_close(wsh, 1000); break; case WSOC_CONTINUATION: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "continue\n"); continue; case WSOC_TEXT: p = data; if (!p) continue; if (!strncasecmp(data, "event ", 6)) { switch_event_types_t type; char *subclass; if (node_count == MAX_EVENT_BIND_SLOTS - 1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "cannot subscribe more than %d events\n", node_count); continue; } p += 6; if (p = strchr(p, ' ')) p++; if (!strncasecmp(p, "json ", 5)) { p += 5; } else if (!strncasecmp(p, "xml ", 4)) { p += 4; } else if (!strncasecmp(p, "plain ", 6)) { p += 6; } if (!*p) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "missing event type in [%s]\n", data); break; } else { } if (subclass = strchr(p, ' ')) { *subclass++ = '\0'; if (!*subclass) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing subclass\n"); continue; } } else { subclass = SWITCH_EVENT_SUBCLASS_ANY; } if (switch_name_event(p, &type) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown event %s\n", p); continue; } if (switch_event_bind_removable("websocket", type, subclass, event_handler, wsh, &nodes[node_count++]) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't bind!\n"); node_count--; continue; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Bind %s\n", data); } } break; default: break; } } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "wsh->down = %d, node_count = %d\n", wsh->down, node_count); switch_yield(2000); while (--node_count >= 0) switch_event_unbind(&nodes[node_count]); switch_safe_free(wsh); return FALSE; }
abyss_bool handler_hook(TSession * r) { //char *mime = "text/html"; char buf[80] = "HTTP/1.1 200 OK\n"; switch_stream_handle_t stream = { 0 }; char *command; int i; TTableItem *ti; char *fs_user = NULL, *fs_domain = NULL; char *path_info = NULL; abyss_bool ret = TRUE; int html = 0, text = 0, xml = 0; const char *api_str; stream.data = r; stream.write_function = http_stream_write; stream.raw_write_function = http_stream_raw_write; if (!r || !r->requestInfo.uri) { return FALSE; } if ((command = strstr(r->requestInfo.uri, "/api/"))) { command += 5; } else if ((command = strstr(r->requestInfo.uri, "/webapi/"))) { command += 8; html++; } else if ((command = strstr(r->requestInfo.uri, "/txtapi/"))) { command += 8; text++; } else if ((command = strstr(r->requestInfo.uri, "/xmlapi/"))) { command += 8; xml++; } else { return FALSE; } if ((path_info = strchr(command, '/'))) { *path_info++ = '\0'; } for (i = 0; i < r->response_headers.size; i++) { ti = &r->response_headers.item[i]; if (!strcasecmp(ti->name, "freeswitch-user")) { fs_user = ti->value; } else if (!strcasecmp(ti->name, "freeswitch-domain")) { fs_domain = ti->value; } } if (is_authorized(r, command)) { goto auth; } ret = TRUE; goto end; auth: if (switch_event_create(&stream.param_event, SWITCH_EVENT_API) == SWITCH_STATUS_SUCCESS) { const char *const content_length = RequestHeaderValue(r, "content-length"); if (html) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "Content-type", "text/html"); else if (text) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "Content-type", "text/plain"); else if (xml) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "Content-type", "text/xml"); if (fs_user) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "FreeSWITCH-User", fs_user); if (fs_domain) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "FreeSWITCH-Domain", fs_domain); if (path_info) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "HTTP-Path-Info", path_info); switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "HTTP-URI", r->requestInfo.uri); if (r->requestInfo.query) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "HTTP-QUERY", r->requestInfo.query); if (r->requestInfo.host) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "HTTP-HOST", r->requestInfo.host); if (r->requestInfo.from) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "HTTP-FROM", r->requestInfo.from); if (r->requestInfo.useragent) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "HTTP-USER-AGENT", r->requestInfo.useragent); if (r->requestInfo.referer) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "HTTP-REFERER", r->requestInfo.referer); if (r->requestInfo.requestline) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "HTTP-REQUESTLINE", r->requestInfo.requestline); if (r->requestInfo.user) switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "HTTP-USER", r->requestInfo.user); if (r->requestInfo.port) switch_event_add_header(stream.param_event, SWITCH_STACK_BOTTOM, "HTTP-PORT", "%u", r->requestInfo.port); if (r->requestInfo.query || content_length) { char *q, *qd; char *next; char *query = (char *) r->requestInfo.query; char *name, *val; char qbuf[8192] = ""; if (r->requestInfo.method == m_post && content_length) { int len = atoi(content_length); int qlen = 0; if (len > 0) { int succeeded; char *qp = qbuf; do { int blen = r->conn->buffersize - r->conn->bufferpos; if ((qlen + blen) > len) { blen = len - qlen; } qlen += blen; if (qlen > sizeof(qbuf)) { break; } memcpy(qp, r->conn->buffer + r->conn->bufferpos, blen); qp += blen; if (qlen >= len) { break; } } while ((succeeded = ConnRead(r->conn, 2000))); query = qbuf; } } if (query) { switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "HTTP-QUERY", query); qd = strdup(query); switch_assert(qd != NULL); q = qd; next = q; do { char *p; if ((next = strchr(next, '&'))) { *next++ = '\0'; } for (p = q; p && *p; p++) { if (*p == '+') { *p = ' '; } } switch_url_decode(q); name = q; if ((val = strchr(name, '='))) { *val++ = '\0'; switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, name, val); } q = next; } while (q != NULL); free(qd); } } } //ResponseChunked(r); //ResponseContentType(r, mime); //ResponseWrite(r); HTTPWrite(r, buf, (uint32_t) strlen(buf)); //HTTPWrite(r, "<pre>\n\n", 7); /* generation of the date field */ { const char *dateValue; DateToString(r->date, &dateValue); if (dateValue) { ResponseAddField(r, "Date", dateValue); } } /* Generation of the server field */ ResponseAddField(r, "Server", "FreeSWITCH-" SWITCH_VERSION_FULL "-mod_xml_rpc"); if (html) { ResponseAddField(r, "Content-Type", "text/html"); } else if (text) { ResponseAddField(r, "Content-Type", "text/plain"); } else if (xml) { ResponseAddField(r, "Content-Type", "text/xml"); } for (i = 0; i < r->response_headers.size; i++) { ti = &r->response_headers.item[i]; ConnWrite(r->conn, ti->name, (uint32_t) strlen(ti->name)); ConnWrite(r->conn, ": ", 2); ConnWrite(r->conn, ti->value, (uint32_t) strlen(ti->value)); ConnWrite(r->conn, CRLF, 2); } switch_snprintf(buf, sizeof(buf), "Connection: close\r\n"); ConnWrite(r->conn, buf, (uint32_t) strlen(buf)); if (html || text || xml) { ConnWrite(r->conn, "\r\n", 2); } if (switch_stristr("unload", command) && switch_stristr("mod_xml_rpc", r->requestInfo.query)) { command = "bgapi"; api_str = "unload mod_xml_rpc"; } else if (switch_stristr("reload", command) && switch_stristr("mod_xml_rpc", r->requestInfo.query)) { command = "bgapi"; api_str = "reload mod_xml_rpc"; } else { api_str = r->requestInfo.query; } if (switch_api_execute(command, api_str, NULL, &stream) == SWITCH_STATUS_SUCCESS) { ResponseStatus(r, 200); r->responseStarted = TRUE; //r->done = TRUE; } else { ResponseStatus(r, 404); ResponseError(r); } //SocketClose(&(r->conn->socket)); HTTPWriteEnd(r); //if (r->conn->channelP) //ConnKill(r->conn); //ChannelInterrupt(r->conn->channelP); //ConnClose(r->conn); //ChannelDestroy(r->conn->channelP); r->requestInfo.keepalive = 0; end: return ret; }
bool AbyssServer::Session::hasContentLength() const { return RequestHeaderValue(this->implP->cSessionP, "content-length")!= NULL; }
static abyss_bool ServerDirectoryHandler(TSession * const r, char * const z, time_t const fileModTime, MIMEType * const mimeTypeP) { TList list; abyss_bool text; abyss_bool ascending; uint16_t sort; /* 1=by name, 2=by date */ TPool pool; TDate date; const char * error; uint16_t responseStatus; TDate dirdate; const char * imsHdr; determineSortType(r->request_info.query, &ascending, &sort, &text, &error); if (error) { ResponseStatus(r,400); xmlrpc_strfree(error); return TRUE; } fileDate(r, fileModTime, &dirdate); imsHdr = RequestHeaderValue(r, "If-Modified-Since"); if (imsHdr) { if (DateDecode(imsHdr, &date)) { if (DateCompare(&dirdate, &date) <= 0) { ResponseStatus(r, 304); ResponseWrite(r); return TRUE; } } } if (!PoolCreate(&pool, 1024)) { ResponseStatus(r, 500); return TRUE; } generateListing(&list, z, r->request_info.uri, &pool, &error, &responseStatus); if (error) { ResponseStatus(r, responseStatus); xmlrpc_strfree(error); PoolFree(&pool); return TRUE; } /* Send something to the user to show that we are still alive */ ResponseStatus(r, 200); ResponseContentType(r, (text ? "text/plain" : "text/html")); if (DateToString(&dirdate, z)) ResponseAddField(r, "Last-Modified", z); ResponseChunked(r); ResponseWrite(r); if (r->request_info.method!=m_head) sendDirectoryDocument(&list, ascending, sort, text, r->request_info.uri, mimeTypeP, r, z); HTTPWriteEndChunk(r); /* Free memory and exit */ ListFree(&list); PoolFree(&pool); return TRUE; }
static abyss_bool ServerFileHandler(TSession * const r, char * const z, time_t const fileModTime, MIMEType * const mimeTypeP) { const char * mediatype; TFile file; uint64_t filesize; uint64_t start; uint64_t end; TDate date; char * p; TDate filedate; mediatype = MIMETypeGuessFromFile2(mimeTypeP, z); if (!FileOpen(&file,z,O_BINARY | O_RDONLY)) { ResponseStatusErrno(r); return TRUE; } fileDate(r, fileModTime, &filedate); p = RequestHeaderValue(r, "if-modified-since"); if (p) { if (DateDecode(p,&date)) { if (DateCompare(&filedate, &date) <= 0) { ResponseStatus(r, 304); ResponseWrite(r); return TRUE; } else r->ranges.size = 0; } } filesize = FileSize(&file); switch (r->ranges.size) { case 0: ResponseStatus(r, 200); break; case 1: { abyss_bool decoded; decoded = RangeDecode((char *)(r->ranges.item[0]), filesize, &start, &end); if (!decoded) { ListFree(&(r->ranges)); ResponseStatus(r, 200); break; } sprintf(z, "bytes %llu-%llu/%llu", start, end, filesize); ResponseAddField(r, "Content-range", z); ResponseContentLength(r, end - start + 1); ResponseStatus(r, 206); } break; default: ResponseContentType(r, "multipart/ranges; boundary=" BOUNDARY); ResponseStatus(r, 206); break; } if (r->ranges.size == 0) { ResponseContentLength(r, filesize); ResponseContentType(r, mediatype); } if (DateToString(&filedate, z)) ResponseAddField(r, "Last-Modified", z); ResponseWrite(r); if (r->request_info.method != m_head) sendBody(r, &file, filesize, mediatype, start, end, z); FileClose(&file); return TRUE; }
/************************************** * ProcessRequest * Procesa una peticion *************************************/ int XmlHandler::ProcessRequest(TRequestInfo *req,TSession * const ses) { xmlrpc_env env; int inputLen; char *method; xmlrpc_value *params = NULL; timeval tv; Log(">ProcessRequest [uri:%s]\n",req->uri); //Init timer getUpdDifTime(&tv); //Creamos un enviroment xmlrpc_env_init(&env); //Si no es post if (req->method != m_post) //Mandamos error return XmlRpcServer::SendError(ses, 405, "Only POST allowed"); //Obtenemos el content type const char * content_type = RequestHeaderValue(ses, (char*)"content-type"); //Si no es el bueno if (content_type == NULL || strcmp(content_type, "text/xml") != 0) return XmlRpcServer::SendError(ses, 400, "Wrong content-type"); //Obtenemos el content length const char * content_length = RequestHeaderValue(ses, (char*)"content-length"); //Si no hay if (content_length == NULL) return XmlRpcServer::SendError(ses,411,"No content-length"); //Obtenemos el entero inputLen = atoi(content_length); //Tiene que ser mayor que cero if ((inputLen < 0) || (inputLen > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))) return XmlRpcServer::SendError(ses,400,"Size limit"); //Creamos un buffer para el body char * buffer = (char *) malloc(inputLen); if (!XmlRpcServer::GetBody(ses,buffer,inputLen)) { //LIberamos el buffer free(buffer); //Y salimos sin devolver nada Log("Operation timedout\n"); return 1; } //Get method name xmlrpc_parse_call(&env,buffer,inputLen,(const char**)&method,¶ms); Log("-ProcessRequest [method:%s]\n",method); //Free name and params free(method); xmlrpc_DECREF(params); //Generamos la respuesta xmlrpc_mem_block *output = xmlrpc_registry_process_call( &env, registry, NULL, buffer, inputLen ); //Si todo ha ido bien if (!env.fault_occurred) { //POnemos el content type ResponseContentType(ses, (char*)"text/xml; charset=\"utf-8\""); //Y mandamos la respuesta XmlRpcServer::SendResponse(ses,200,XMLRPC_MEMBLOCK_CONTENTS(char, output), XMLRPC_MEMBLOCK_SIZE(char, output)); } else