<choice minOccurs="0" maxOccurs="unbounded">\r
<element ref="conf:Path"/>\r
<element ref="conf:PathRegex"/>\r
+ <element ref="conf:Query"/>\r
</choice>\r
</sequence>\r
<attribute name="scheme">\r
<choice minOccurs="0" maxOccurs="unbounded">\r
<element ref="conf:Path"/>\r
<element ref="conf:PathRegex"/>\r
+ <element ref="conf:Query"/>\r
</choice>\r
</sequence>\r
<attribute name="regex" type="conf:string" use="required"/>\r
<choice minOccurs="0" maxOccurs="unbounded">\r
<element ref="conf:Path"/>\r
<element ref="conf:PathRegex"/>\r
+ <element ref="conf:Query"/>\r
</choice>\r
</sequence>\r
<attribute name="name" type="conf:string" use="required"/>\r
<element ref="conf:AccessControl"/>\r
<element ref="conf:AccessControlProvider"/>\r
</choice>\r
+ <element ref="conf:Query" minOccurs="0" maxOccurs="unbounded"/>\r
</sequence>\r
<attribute name="regex" type="conf:string" use="required"/>\r
<attribute name="ignoreCase" type="boolean" default="true"/>\r
</complexType>\r
</element>\r
\r
+ <element name="Query">\r
+ <complexType>\r
+ <sequence>\r
+ <choice minOccurs="0">\r
+ <element ref="conf:htaccess"/>\r
+ <element ref="conf:AccessControl"/>\r
+ <element ref="conf:AccessControlProvider"/>\r
+ </choice>\r
+ <element ref="conf:Query" minOccurs="0" maxOccurs="unbounded"/>\r
+ </sequence>\r
+ <attribute name="name" type="conf:string" use="required"/>\r
+ <attribute name="regex" type="conf:string"/>\r
+ <attributeGroup ref="conf:ContentSettings"/>\r
+ </complexType>\r
+ </element>\r
+ \r
<element name="Applications">\r
<annotation>\r
<documentation>Container for global settings and application-specific overrides</documentation>\r
const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:2.0:native:sp:config") const;\r
\r
// Provides filter to exclude special config elements.\r
- short acceptNode(const DOMNode* node) const;\r
+ short acceptNode(const DOMNode* node) const {\r
+ return FILTER_REJECT;\r
+ }\r
\r
- const Override* locate(const char* path) const;\r
+ const Override* locate(const SPRequest& request) const;\r
AccessControl* getAC() const { return (m_acl ? m_acl : (m_base ? m_base->getAC() : NULL)); }\r
\r
protected:\r
\r
map<string,Override*> m_map;\r
vector< pair<RegularExpression*,Override*> > m_regexps;\r
+ vector< pair< pair<string,RegularExpression*>,Override*> > m_queries;\r
\r
private:\r
const Override* m_base;\r
m_document = doc;\r
}\r
\r
- const Override* findOverride(const char* vhost, const char* path) const;\r
+ const Override* findOverride(const char* vhost, const SPRequest& request) const;\r
\r
private: \r
map<string,Override*> m_extras;\r
static const XMLCh ignoreOption[] = UNICODE_LITERAL_1(i);\r
static const XMLCh Path[] = UNICODE_LITERAL_4(P,a,t,h);\r
static const XMLCh PathRegex[] = UNICODE_LITERAL_9(P,a,t,h,R,e,g,e,x);\r
+ static const XMLCh Query[] = UNICODE_LITERAL_5(Q,u,e,r,y);\r
static const XMLCh name[] = UNICODE_LITERAL_4(n,a,m,e);\r
static const XMLCh regex[] = UNICODE_LITERAL_5(r,e,g,e,x);\r
static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);\r
{\r
SPConfig& conf=SPConfig::getConfig();\r
conf.RequestMapperManager.registerFactory(XML_REQUEST_MAPPER, XMLRequestMapperFactory);\r
- conf.RequestMapperManager.registerFactory("edu.internet2.middleware.shibboleth.sp.provider.XMLRequestMapProvider", XMLRequestMapperFactory);\r
- conf.RequestMapperManager.registerFactory("edu.internet2.middleware.shibboleth.target.provider.XMLRequestMap", XMLRequestMapperFactory);\r
conf.RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, XMLRequestMapperFactory);\r
- conf.RequestMapperManager.registerFactory("edu.internet2.middleware.shibboleth.sp.provider.NativeRequestMapProvider", XMLRequestMapperFactory);\r
-}\r
-\r
-short Override::acceptNode(const DOMNode* node) const\r
-{\r
- if (!XMLString::equals(node->getNamespaceURI(),shibspconstants::SHIB2SPCONFIG_NS))\r
- return FILTER_ACCEPT;\r
- const XMLCh* name=node->getLocalName();\r
- if (XMLString::equals(name,Host) ||\r
- XMLString::equals(name,Path) ||\r
- XMLString::equals(name,_AccessControl) ||\r
- XMLString::equals(name,htaccess) ||\r
- XMLString::equals(name,AccessControlProvider))\r
- return FILTER_REJECT;\r
-\r
- return FILTER_ACCEPT;\r
}\r
\r
void Override::loadACL(const DOMElement* e, Category& log)\r
for (char* pch=dup; *pch; pch++)\r
*pch=tolower(*pch);\r
if (m_map.count(dup)) {\r
- log.warn("Skipping duplicate Path element (%s)",dup);\r
+ log.warn("skipping duplicate Path element (%s)",dup);\r
free(dup);\r
delete o;\r
continue;\r
}\r
m_map[dup]=o;\r
- log.debug("Added Path mapping (%s)", dup);\r
+ log.debug("added Path mapping (%s)", dup);\r
free(dup);\r
}\r
\r
for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,PathRegex)) {\r
const XMLCh* n=path->getAttributeNS(NULL,regex);\r
if (!n || !*n) {\r
- log.warn("Skipping PathRegex element (%d) with empty regex attribute",i);\r
+ log.warn("skipping PathRegex element (%d) with empty regex attribute",i);\r
continue;\r
}\r
\r
throw ConfigurationException("Invalid regular expression in PathRegex element.");\r
}\r
\r
- log.debug("Added <PathRegex> mapping (%s)", m_regexps.back().second->getString("regex").second);\r
+ if (log.isDebugEnabled())\r
+ log.debug("added <PathRegex> mapping (%s)", m_regexps.back().second->getString("regex").second);\r
+ }\r
+ }\r
+\r
+ // Handle nested Querys.\r
+ path = XMLHelper::getFirstChildElement(e,Query);\r
+ for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Query)) {\r
+ const XMLCh* n=path->getAttributeNS(NULL,name);\r
+ if (!n || !*n) {\r
+ log.warn("skipping Query element (%d) with empty name attribute",i);\r
+ continue;\r
+ }\r
+ auto_ptr_char ntemp(n);\r
+ const XMLCh* v=path->getAttributeNS(NULL,regex);\r
+\r
+ auto_ptr<Override> o(new Override(path,log,this));\r
+ try {\r
+ RegularExpression* re = NULL;\r
+ if (v && *v)\r
+ re = new RegularExpression(v);\r
+ m_queries.push_back(make_pair(make_pair(ntemp.get(),re), o.release()));\r
}\r
+ catch (XMLException& ex) {\r
+ auto_ptr_char tmp(ex.getMessage());\r
+ log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());\r
+ throw ConfigurationException("Invalid regular expression in Query element.");\r
+ }\r
+ \r
+ log.debug("added <Query> mapping (%s)", ntemp.get());\r
}\r
}\r
catch (exception&) {\r
delete i->first;\r
delete i->second;\r
}\r
+ for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {\r
+ delete j->first.second;\r
+ delete j->second;\r
+ }\r
}\r
\r
pair<bool,bool> Override::getBool(const char* name, const char* ns) const\r
return m_base->getPropertySet(name,ns);\r
}\r
\r
-const Override* Override::locate(const char* path) const\r
+const Override* Override::locate(const SPRequest& request) const\r
{\r
// This function is confusing because it's *not* recursive.\r
// The whole path is tokenized and mapped in a loop, so the\r
// path parameter starts with the entire request path and\r
// we can skip the leading slash as irrelevant.\r
+ const char* path = request.getRequestURI();\r
if (*path == '/')\r
path++;\r
\r
\r
free(dup);\r
\r
- // If there's anything left, we try for a regex match on the rest of the path.\r
+ // If there's anything left, we try for a regex match on the rest of the path minus the query string.\r
if (*path) {\r
+ string path2(path);\r
+ path2 = path2.substr(0,path2.find('?'));\r
+\r
for (vector< pair<RegularExpression*,Override*> >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {\r
- if (re->first->matches(path))\r
- return re->second;\r
+ if (re->first->matches(path2.c_str())) {\r
+ o = re->second;\r
+ break;\r
+ }\r
}\r
}\r
\r
+ // Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.\r
+ bool descended;\r
+ do {\r
+ descended = false;\r
+ for (vector< pair< pair<string,RegularExpression*>,Override*> >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {\r
+ vector<const char*> vals;\r
+ if (request.getParameters(q->first.first.c_str(), vals)) {\r
+ if (q->first.second) {\r
+ // We have to match one of the values.\r
+ for (vector<const char*>::const_iterator v = vals.begin(); v != vals.end(); ++v) {\r
+ if (q->first.second->matches(*v)) {\r
+ o = q->second;\r
+ descended = true;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ else {\r
+ // The simple presence of the parameter is sufficient to match.\r
+ o = q->second;\r
+ descended = true;\r
+ }\r
+ }\r
+ }\r
+ } while (descended);\r
+\r
return o;\r
}\r
\r
}\r
}\r
\r
-const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const char* path) const\r
+const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const SPRequest& request) const\r
{\r
const Override* o=NULL;\r
map<string,Override*>::const_iterator i=m_map.find(vhost);\r
}\r
}\r
\r
- return o ? o->locate(path) : this;\r
+ return o ? o->locate(request) : this;\r
}\r
\r
pair<bool,DOMElement*> XMLRequestMapper::load()\r
ostringstream vhost;\r
vhost << request.getScheme() << "://" << request.getHostname() << ':' << request.getPort();\r
\r
- const Override* o=m_impl->findOverride(vhost.str().c_str(), request.getRequestURI());\r
+ const Override* o=m_impl->findOverride(vhost.str().c_str(), request);\r
\r
if (m_log.isDebugEnabled()) {\r
#ifdef _DEBUG\r