#include "api.h" #include #include #include #include #define IIS_TYPE 'IIS' #define MIN_BUFFER 1024 const char *mime_type(const char *ext); ///////////////////////////////////////////////////////////////////// class zsIIS { long bytes; char ctype[MIN_BUFFER]; char message[MIN_BUFFER]; LPEXTENSION_CONTROL_BLOCK pECB; public: zsIIS(LPEXTENSION_CONTROL_BLOCK pECB); ~zsIIS(); DWORD send(void* ptr, DWORD size); DWORD send(char* msg) { return send(msg, strlen(msg)); } void mime(const char *ext); long size() { return bytes; } void error(const char *msg) { strncpy(message, msg, MIN_BUFFER-1); } char *error() { return message; } LPEXTENSION_CONTROL_BLOCK pecb() { return pECB; } }; zsIIS::zsIIS(LPEXTENSION_CONTROL_BLOCK pECB) { this->pECB = pECB; mime("htm"); bytes = 0; memset(message, 0, MIN_BUFFER); } zsIIS::~zsIIS() { } void zsIIS::mime(const char *ext) { const char *p = mime_type(ext); if (p) strcpy(ctype, p); else strcpy(ctype, ext); } DWORD zsIIS::send(void* ptr, DWORD size) { if (bytes==0) { // send content header once char header[MIN_BUFFER]; sprintf(header, "Content-type: %s\r\n\r\n", ctype); HSE_SEND_HEADER_EX_INFO hdi; hdi.pszStatus = "200 OK"; hdi.pszHeader = header; hdi.cchStatus = strlen(hdi.pszStatus); hdi.cchHeader = strlen(hdi.pszHeader); hdi.fKeepConn = FALSE; if (!pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &hdi, NULL, NULL)) return HSE_STATUS_ERROR; } if (ptr && size > 0) { if (!pECB->WriteClient(pECB->ConnID, ptr, &size, HSE_IO_SYNC)) return HSE_STATUS_ERROR; } bytes += size; return HSE_STATUS_SUCCESS; } std::string decode_url(const char *s, int len) { std::string ret; int i = 0; while (i < len) { if (s[i] == '%' && s[i+1] && s[i+2]) { char tmp[] = "0x0__"; tmp[3] = s[++i]; tmp[4] = s[++i]; ret += strtol(tmp, NULL, 16); } else { if (s[i] == '+') ret += ' '; else ret += s[i]; } i++; } return ret; } bool parse_get(void *ctx, const char *src, zsIIS &iis) { void *arr = api_create_array(0, 32); api_set_object(ctx, "HTTP_PARS", arr); const char *ptr = strchr(src, '='); while (src && ptr) { std::string key, value; key = decode_url(src, ptr-src); if (key.empty()) { iis.error("Failed to decode parameter name."); return false; } src = ptr + 1; ptr = strchr(src, '&'); if (!ptr) { // the last parameter value value = decode_url(src, strlen(src)); } else { value = decode_url(src, ptr-src); src = ptr + 1; ptr = strchr(src, '='); } if (!value.empty()) { api_set_array_object(ctx, arr, key.c_str(), api_create_string(0, value.c_str())); } else { api_set_array_object(ctx, arr, key.c_str(), api_create_string(0, "")); } } return true; } bool process_post(void *ctx, zsIIS &iis) { LPEXTENSION_CONTROL_BLOCK pECB = iis.pecb(); DWORD nbuf, read=0, unread=pECB->cbTotalBytes, total=pECB->cbTotalBytes; std::vector data(pECB->cbTotalBytes+1); // get all data if (pECB->cbAvailable > 0) { memcpy(&data[0], pECB->lpbData, pECB->cbAvailable); read += pECB->cbAvailable; unread -= pECB->cbAvailable; } while (unread > 0) { nbuf = unread; if (!pECB->ReadClient(pECB->ConnID, &data[read], &nbuf)) { iis.error("Failed in calling ReadClient."); return false; } if(nbuf <= 0) break; read += nbuf; unread -= nbuf; if (read > total) { iis.error("Read bytes > total bytes."); return false; } } if (read != total) { iis.error("Read bytes != total bytes."); return false; } data[read] = 0; // in case of simple "post", treat parameters as from "get" char buf[MIN_BUFFER]; nbuf = MIN_BUFFER; if (!pECB->GetServerVariable(pECB->ConnID, "CONTENT_TYPE", buf, &nbuf)) return parse_get(ctx, &data[0], iis); buf[nbuf] = 0; if (!strstr(buf, "multipart/form-data")) return parse_get(ctx, &data[0], iis); // process multipart/form-data void *arr = api_create_array(0, 32); api_set_object(ctx, "HTTP_PARS", arr); std::string s0("\r\n"), s1("\r\nContent-Disposition: form-data; name=\""), s2("\"; filename=\""), s3("\"\r\nContent-Type: "), s4("\r\n\r\n"); char *ptr0=&data[0], *ptr1, *ptr2; const char *end=ptr0+read; // save the first MIN_BUFFER data for debugging iis.error(ptr0); // find content deposition section ptr1 = strstr(ptr0, s1.c_str()); if (!ptr1) return false; // get boundary string int n = ptr1 - ptr0; if (n <= 0) return false; s0.append(ptr0, n); // points ptr0 to parameter name ptr0 = ptr1 + s1.size(); while (ptr0 < end) { // find end of content deposition section ptr2 = strstr(ptr0, s4.c_str()); if (!ptr2) return false; // make the next search short ptr2[0] = 0; // find file name ptr1 = strstr(ptr0, s2.c_str()); if (ptr1) { // process single file upload n = ptr1 - ptr0; if (n <= 0) return false; // decode parameter name std::string name = decode_url(ptr0, n); // points ptr0 to filename ptr0 = ptr1 + s2.size(); // content type follow filename ptr1 = strstr(ptr0, s3.c_str()); n = ptr1 - ptr0; if (!ptr1 || n <= 0) return false; // decode parameter filename std::string fname = decode_url(ptr0, n); // points ptr0 to data ptr0 = ptr2 + s4.size(); // data may be binary, so do this to find the boundary. ptr1 = ptr0; bool flag = false; while (ptr1 < end) { ptr2 = strstr(ptr1, s0.c_str()); if (ptr2) { n = ptr2 - ptr0; if (n <= 0) { iis.error("Uploaded file is empty."); return false; } void *arr2 = api_create_array(0, 3); api_set_array_object(ctx, arr, name.c_str(), arr2); void *tmp = malloc(n); memcpy(tmp, ptr0, n); api_set_array_object(ctx, arr2, "0", api_create_string(0, fname.c_str())); api_set_array_object(ctx, arr2, "1", api_create_user(0, tmp, 0, free, 0)); api_set_array_object(ctx, arr2, "2", api_create_integer(0, n)); flag = true; ptr0 = ptr2 + s0.size(); break; } ptr1++; } if (!flag) return false; } else { // process parameter name n = ptr2 - ptr0; // dispose \" n--; if (n <= 0) { iis.error("Parameter name is empty."); return false; } // key=value pair std::string name = decode_url(ptr0, n); // find parameter value ptr0 = ptr2 + s4.size(); ptr1 = strstr(ptr0, s0.c_str()); n = ptr1 - ptr0; if (n > 0) { std::string value = decode_url(ptr0, n); api_set_array_object(ctx, arr, name.c_str(), api_create_string(0, value.c_str())); } else { api_set_array_object(ctx, arr, name.c_str(), api_create_string(0, "")); } ptr0 = ptr1 + s0.size(); } // find the next content deposition ptr1 = strstr(ptr0, s1.c_str()); if (!ptr1) break; ptr0 = ptr1 + s1.size(); } return true; } BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO* pVer) { pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR); lstrcpyn(pVer->lpszExtensionDesc, "Z-Script ISAPI Extension", HSE_MAX_EXT_DLL_NAME_LEN-1); return TRUE; } DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK pECB) { zsIIS iis(pECB); // Get the physical path of script file. int len1 = strlen(pECB->lpszPathTranslated); int len2 = strlen(pECB->lpszPathInfo); int i, j; for (i = len1-1, j = len2-1; i > 0 && j > 0; i--, j--) { if (toupper(pECB->lpszPathTranslated[i]) != toupper(pECB->lpszPathInfo[j])) { if (pECB->lpszPathTranslated[i] != '\\' || pECB->lpszPathInfo[j] != '/') break; } } std::string path(pECB->lpszPathTranslated); path = path.substr(0,i+2); for (i = 0; i < path.size(); i++) { if (path.at(i) == '\\') path.at(i) = '/'; } char error[512]; // Parse script void *module = api_parse_file2(pECB->lpszPathInfo+j+2, error, path.c_str()); if (!module) { return iis.send(error, strlen(error)); } void *ctx = api_get_context(module); // Script may use HTTP_PATH to do path related IO api_set_object(ctx, "HTTP_PATH", api_create_string(0, path.c_str())); // Process reauest if (strcmp(pECB->lpszMethod, "POST") == 0) { if (!process_post(ctx, iis)) { api_delete_module(module); return iis.send(iis.error()); } } else { if (!parse_get(ctx, pECB->lpszQueryString, iis)) { api_delete_module(module); return iis.send(iis.error()); } } // Pass iis to the module for function call by script api_set_object(ctx, "HTTP_IIS", api_create_user(ctx, &iis, 0, 0, IIS_TYPE)); api_set_object(ctx, "HTTP_SOAP", api_create_user(ctx, &iis, 0, 0, IIS_TYPE)); if (api_exec_module(module, error) <= 0) { api_delete_module(module); if (!strcmp(error, "error: default exception")) return iis.send("Busy. Try again."); else return iis.send(error); } // Always reply with something api_delete_module(module); if (iis.size() <= 0) iis.send("Default OK response."); return HSE_STATUS_SUCCESS; } void* iis_mime(void* ctx, int nargs, void **args) { if (nargs < 2) api_input_error(ctx); zsIIS *u = (zsIIS*)api_get_user(ctx, args[0], IIS_TYPE); u->mime(api_get_string(ctx, args[1])); return 0; } void* iis_send(void* ctx, int nargs, void **args) { if (nargs < 2) api_input_error(ctx); zsIIS *u = (zsIIS*)api_get_user(ctx, args[0], IIS_TYPE); if (nargs == 2) { const char *msg = api_get_string(ctx, args[1]); u->send((void*)msg, strlen(msg)); } else { // Send binary data u->send(api_get_ptr(ctx, args[1]), api_get_integer(ctx, args[2])); } return 0; } void* iis_vars(void* ctx, int nargs, void **args) { if (nargs < 2) api_input_error(ctx); zsIIS *u = (zsIIS*)api_get_user(ctx, args[0], IIS_TYPE); char buf[MIN_BUFFER]; DWORD nb = MIN_BUFFER-1; // Get server variable BOOL status = u->pecb()->GetServerVariable(u->pecb()->ConnID, (char*)api_get_string(ctx, args[1]), buf, &nb); if (status == TRUE) { buf[nb] = 0; return api_create_string(ctx, buf); } return 0; } void* iis_fsend(void *ctx, int nargs, void** args) { if (nargs < 2) api_input_error(ctx); zsIIS *u = (zsIIS*)api_get_user(ctx, args[0], IIS_TYPE); FILE *f = fopen(api_get_string(ctx, args[1]), "r"); if (!f) return 0; fseek(f, 0, SEEK_END); int size = ftell(f) - 2; if (size <= 0) { fclose(f); return api_create_integer(ctx, size); } rewind(f); void *ptr = malloc(size); if (!ptr) return 0; fread(ptr, 1, size, f); fclose(f); u->send(ptr, size); return api_create_integer(ctx, size); } void* iis_exec(void *ctx, int nargs, void** args) { return api_create_string(ctx, "exec is not allowed in web application."); } BOOL WINAPI DllMain(IN HINSTANCE hinstDll, IN DWORD dwReason, IN LPVOID lpvContext) { switch(dwReason) { case DLL_PROCESS_ATTACH: api_add_primitive("mime", IIS_TYPE, iis_mime); api_add_primitive("send", IIS_TYPE, iis_send); api_add_primitive("fsend", IIS_TYPE, iis_fsend); api_add_primitive("vars", IIS_TYPE, iis_vars); api_add_primitive("exec", 0, iis_exec); break; case DLL_PROCESS_DETACH : break; } return TRUE; } ///////////////////////////////////////////////////////////////////// struct mineType { const char *ext, *type; }; static mineType g_mine[] = { {"*", "application/octet-stream" }, {"htm", "text/html" }, {"html", "text/html" }, {"pdf", "application/pdf" }, {"jpeg", "image/jpeg" }, {"png", "image/png" }, {"mpg", "video/mpeg" }, {"mpeg", "video/mpeg" }, {"asf", "video/x-ms-asf" }, {"avi", "video/x-msvideo" }, {"bmp", "image/bmp" }, {"jpg", "image/jpeg" }, {"gif", "image/gif" }, {"ico", "image/x-icon" }, {"txt", "text/plain" }, {"css", "text/css" }, {"zip", "application/x-zip-compressed" }, {"tgz", "application/x-tar-gz" }, {"tar.gz", "application/x-tar-gz" }, {"tar", "application/x-tar" }, {"gz", "application/x-gunzip" }, {"arj", "application/x-arj-compressed" }, {"rar", "application/x-arj-compressed" }, {"wav", "audio/x-wav" }, {"mp3", "audio/x-mp3" }, {"mid", "audio/mid" }, {"m3u", "audio/x-mpegurl" }, {"ram", "audio/x-pn-realaudio" }, {"ra", "audio/x-pn-realaudio" }, {"svg", "image/svg+xml" }, {"doc", "application/msword", }, {"exe", "application/octet-stream" }, {"xls", "application/excel" }, {"ppt", "application/vnd.ms-powerpoint" }, {"rtf", "application/rtf" }, {"mathml", "application/mathml+xml" }, {"xml", "application/xml" }, {"xsl", "application/xml" }, {"dtd", "application/xml-dtd" }, {"xslt", "application/xslt+xml" }, {"xhtml", "application/xhtml+xml" }, {"xht", "application/xhtml+xml" }, {"mid", "audio/midi" }, {"midi", "audio/midi" }, {NULL, NULL } }; const char *mime_type(const char *ext) { mineType *p = g_mine; while (p->ext) { if (strcmp(p->ext, ext) == 0) break; p++; } return p->type; }