static switch_status_t http_stream_write(switch_stream_handle_t *handle, const char *fmt, ...) { TSession *r = (TSession *) handle->data; int ret = 1; char *data; switch_event_t *evnt = handle->param_event; va_list ap; va_start(ap, fmt); ret = switch_vasprintf(&data, fmt, ap); va_end(ap); if (data) { /* Stream Content-Type (http header) to the xmlrpc (web) client, if fs api command did not do it yet. */ /* If (Content-Type in event) then the header was already replied. */ /* If fs api command is not "web aware", this will set the Content-Type to "text/plain". */ const char *http_refresh = NULL; const char *ct = NULL; const char *refresh = NULL; if (evnt && !(ct = switch_event_get_header(evnt, "Content-Type"))){ const char *val = switch_stristr("Content-Type", data); if (!val) { val = "Content-Type: text/plain\r\n\r\n"; ret = HTTPWrite(r, val, (uint32_t) strlen(val)); } /* flag to prevent running this more than once per http reply */ switch_event_add_header_string(evnt, SWITCH_STACK_BOTTOM, "Content-Type", strstr(val,":")+2); ct = switch_event_get_header(evnt, "Content-Type"); } if (ret) { ret = HTTPWrite(r, data, (uint32_t) strlen(data)); } switch_safe_free(data); /* e.g. "http://www.cluecon.fs/api/show?calls &refresh=5" */ /* fs api command can set event header "HTTP-REFRESH" so that the web page will automagically refresh, if */ /* "refresh=xxx" was part of the http query kv pairs */ if (ret && ct && *ct && (http_refresh = switch_event_get_header(evnt, "HTTP-REFRESH")) && (refresh = switch_event_get_header(evnt, "refresh")) && !strstr("text/html", ct) && (atoi(refresh) > 0 )) { const char *query = switch_event_get_header(evnt, "HTTP-QUERY"); const char *uri = switch_event_get_header(evnt, "HTTP-URI"); if (uri && query && *uri && *query) { char *buf = switch_mprintf("<META HTTP-EQUIV=REFRESH CONTENT=\"%s; URL=%s?%s\">\n", refresh, uri, query); ret = HTTPWrite(r, buf, (uint32_t) strlen(buf)); switch_safe_free(buf); } } /* only one refresh meta header per reply */ if (http_refresh) { switch_event_del_header(evnt, "HTTP-REFRESH"); } } return ret ? SWITCH_STATUS_FALSE : SWITCH_STATUS_SUCCESS; }
static switch_status_t http_stream_raw_write(switch_stream_handle_t *handle, uint8_t *data, switch_size_t datalen) { TSession *r = (TSession *) handle->data; return HTTPWrite(r, (char *) data, (uint32_t) datalen) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; }
void Answer(TSession *r, uint16_t statuscode, char *buffer) { ResponseChunked(r); ResponseStatus(r,statuscode); ResponseContentType(r,"text/html"); ResponseWriteStart(r); HTTPWrite(r,"<HTML><BODY>",12); HTTPWrite(r,buffer,strlen(buffer)); HTTPWrite(r,"</BODY></HTML>",14); HTTPWriteEnd(r); }
static switch_status_t http_stream_write(switch_stream_handle_t *handle, const char *fmt, ...) { va_list ap; TSession *r = handle->data; int ret = 0; char *data; va_start(ap, fmt); ret = switch_vasprintf(&data, fmt, ap); va_end(ap); if (data) { ret = 0; HTTPWrite(r, data, (uint32_t) strlen(data)); free(data); } return ret ? SWITCH_STATUS_FALSE : SWITCH_STATUS_SUCCESS; }
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; }
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; }