+ LPEXTENSION_CONTROL_BLOCK m_lpECB;
+ multimap<string,string> m_headers;
+ mutable vector<string> m_certs;
+ mutable string m_body;
+ mutable bool m_gotBody;
+ int m_port;
+ string m_scheme,m_hostname,m_uri;
+ mutable string m_remote_addr,m_remote_user;
+
+public:
+ ShibTargetIsapiE(LPEXTENSION_CONTROL_BLOCK lpECB, const site_t& site)
+ : AbstractSPRequest(SHIBSP_LOGCAT".ISAPI"), m_lpECB(lpECB), m_gotBody(false) {
+ dynabuf ssl(5);
+ GetServerVariable(lpECB,"HTTPS",ssl,5);
+ bool SSL=(ssl=="on" || ssl=="ON");
+
+ // Scheme may come from site def or be derived from IIS.
+ m_scheme=site.m_scheme;
+ if (m_scheme.empty() || !g_bNormalizeRequest)
+ m_scheme = SSL ? "https" : "http";
+
+ // URL path always come from IIS.
+ dynabuf url(256);
+ GetServerVariable(lpECB,"URL",url,255);
+
+ // Port may come from IIS or from site def.
+ dynabuf port(11);
+ if (!g_bNormalizeRequest || (SSL && site.m_sslport.empty()) || (!SSL && site.m_port.empty()))
+ GetServerVariable(lpECB,"SERVER_PORT",port,10);
+ else if (SSL) {
+ strncpy(port,site.m_sslport.c_str(),10);
+ static_cast<char*>(port)[10]=0;
+ }
+ else {
+ strncpy(port,site.m_port.c_str(),10);
+ static_cast<char*>(port)[10]=0;
+ }
+ m_port = atoi(port);
+
+ dynabuf var(32);
+ GetServerVariable(lpECB, "SERVER_NAME", var, 32);
+
+ // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name.
+ m_hostname=var;
+ if (site.m_name!=m_hostname && site.m_aliases.find(m_hostname)==site.m_aliases.end())
+ m_hostname=site.m_name;
+
+ /*
+ * IIS screws us over on PATH_INFO (the hits keep on coming). We need to figure out if
+ * the server is set up for proper PATH_INFO handling, or "IIS sucks rabid weasels mode",
+ * which is the default. No perfect way to tell, but we can take a good guess by checking
+ * whether the URL is a substring of the PATH_INFO:
+ *
+ * e.g. for /Shibboleth.sso/SAML/POST
+ *
+ * Bad mode (default):
+ * URL: /Shibboleth.sso
+ * PathInfo: /Shibboleth.sso/SAML/POST
+ *
+ * Good mode:
+ * URL: /Shibboleth.sso
+ * PathInfo: /SAML/POST
+ */
+
+ string uri;
+
+ // Clearly we're only in bad mode if path info exists at all.
+ if (lpECB->lpszPathInfo && *(lpECB->lpszPathInfo)) {
+ if (strstr(lpECB->lpszPathInfo,url))
+ // Pretty good chance we're in bad mode, unless the PathInfo repeats the path itself.
+ uri = lpECB->lpszPathInfo;
+ else {
+ uri = url;
+ uri += lpECB->lpszPathInfo;
+ }
+ }
+ else {
+ uri = url;
+ }
+
+ // For consistency with Apache, let's add the query string.
+ if (lpECB->lpszQueryString && *(lpECB->lpszQueryString)) {
+ uri += '?';
+ uri += lpECB->lpszQueryString;
+ }
+
+ setRequestURI(uri.c_str());
+ }
+ ~ShibTargetIsapiE() { }
+
+ const char* getScheme() const {
+ return m_scheme.c_str();
+ }
+ const char* getHostname() const {
+ return m_hostname.c_str();
+ }
+ int getPort() const {
+ return m_port;
+ }
+ const char* getMethod() const {
+ return m_lpECB->lpszMethod;
+ }
+ string getContentType() const {
+ return m_lpECB->lpszContentType ? m_lpECB->lpszContentType : "";
+ }
+ long getContentLength() const {
+ return m_lpECB->cbTotalBytes;
+ }
+ string getRemoteUser() const {
+ if (m_remote_user.empty()) {
+ dynabuf var(16);
+ GetServerVariable(m_lpECB, "REMOTE_USER", var, 32, false);
+ if (!var.empty())
+ m_remote_user = var;
+ }
+ return m_remote_user;
+ }
+ string getRemoteAddr() const {
+ if (m_remote_addr.empty()) {
+ dynabuf var(16);
+ GetServerVariable(m_lpECB, "REMOTE_ADDR", var, 16, false);
+ if (!var.empty())
+ m_remote_addr = var;
+ }
+ return m_remote_addr;
+ }
+ void log(SPLogLevel level, const string& msg) const {
+ AbstractSPRequest::log(level,msg);
+ if (level >= SPError)
+ LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str());
+ }
+ string getHeader(const char* name) const {
+ string hdr("HTTP_");
+ for (; *name; ++name) {
+ if (*name=='-')
+ hdr += '_';
+ else
+ hdr += toupper(*name);
+ }
+ dynabuf buf(128);
+ GetServerVariable(m_lpECB, const_cast<char*>(hdr.c_str()), buf, 128, false);
+ return buf.empty() ? "" : buf;
+ }
+ void setResponseHeader(const char* name, const char* value) {
+ // Set for later.
+ if (value)
+ m_headers.insert(make_pair(name,value));
+ else
+ m_headers.erase(name);
+ }
+ const char* getQueryString() const {
+ return m_lpECB->lpszQueryString;
+ }
+ const char* getRequestBody() const {
+ if (m_gotBody)
+ return m_body.c_str();
+ if (m_lpECB->cbTotalBytes > 1024*1024) // 1MB?
+ throw opensaml::SecurityPolicyException("Size of request body exceeded 1M size limit.");
+ else if (m_lpECB->cbTotalBytes > m_lpECB->cbAvailable) {
+ m_gotBody=true;
+ char buf[8192];
+ DWORD datalen=m_lpECB->cbTotalBytes;
+ while (datalen) {
+ DWORD buflen=8192;
+ BOOL ret = m_lpECB->ReadClient(m_lpECB->ConnID, buf, &buflen);
+ if (!ret || !buflen)
+ throw IOException("Error reading request body from browser.");
+ m_body.append(buf, buflen);
+ datalen-=buflen;
+ }
+ }
+ else if (m_lpECB->cbAvailable) {
+ m_gotBody=true;
+ m_body.assign(reinterpret_cast<char*>(m_lpECB->lpbData),m_lpECB->cbAvailable);
+ }
+ return m_body.c_str();
+ }
+ long sendResponse(istream& in, long status) {
+ string hdr = string("Connection: close\r\n");
+ for (multimap<string,string>::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i)
+ hdr += i->first + ": " + i->second + "\r\n";
+ hdr += "\r\n";
+ const char* codestr="200 OK";
+ switch (status) {
+ case XMLTOOLING_HTTP_STATUS_UNAUTHORIZED: codestr="401 Authorization Required"; break;
+ case XMLTOOLING_HTTP_STATUS_FORBIDDEN: codestr="403 Forbidden"; break;
+ case XMLTOOLING_HTTP_STATUS_NOTFOUND: codestr="404 Not Found"; break;
+ case XMLTOOLING_HTTP_STATUS_ERROR: codestr="500 Server Error"; break;
+ }
+ m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, (void*)codestr, 0, (LPDWORD)hdr.c_str());
+ char buf[1024];
+ while (in) {
+ in.read(buf,1024);
+ DWORD resplen = in.gcount();
+ m_lpECB->WriteClient(m_lpECB->ConnID, buf, &resplen, HSE_IO_SYNC);
+ }