summaryrefslogtreecommitdiffabout
path: root/lib
Side-by-side diff
Diffstat (limited to 'lib') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/basic_rp.cc3
-rw-r--r--lib/discovery.cc53
-rw-r--r--lib/expat.cc9
3 files changed, 49 insertions, 16 deletions
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc
index 3cad71c..9c7113b 100644
--- a/lib/basic_rp.cc
+++ b/lib/basic_rp.cc
@@ -125,193 +125,194 @@ namespace opkele {
mode_t mode,
const string& return_to,const string& realm,
extension_t *ext) {
rv.reset_fields();
rv.set_field("ns",OIURI_OPENID20);
if(mode==mode_checkid_immediate)
rv.set_field("mode","checkid_immediate");
else if(mode==mode_checkid_setup)
rv.set_field("mode","checkid_setup");
else
throw bad_input(OPKELE_CP_ "unknown checkid_* mode");
if(realm.empty() && return_to.empty())
throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty");
if(!realm.empty()) {
rv.set_field("realm",realm);
rv.set_field("trust_root",realm);
}
if(!return_to.empty())
rv.set_field("return_to",return_to);
const openid_endpoint_t& ep = get_endpoint();
rv.set_field("claimed_id",ep.claimed_id);
rv.set_field("identity",ep.local_id);
try {
rv.set_field("assoc_handle",find_assoc(ep.uri)->handle());
}catch(dumb_RP& drp) {
}catch(failed_lookup& fl) {
try {
rv.set_field("assoc_handle",associate(ep.uri)->handle());
}catch(dumb_RP& drp) { }
} OPKELE_RETHROW
if(ext) ext->rp_checkid_hook(rv);
return rv;
}
class signed_part_message_proxy : public basic_openid_message {
public:
const basic_openid_message& x;
set<string> signeds;
signed_part_message_proxy(const basic_openid_message& xx) : x(xx) {
const string& slist = x.get_field("signed");
string::size_type p = 0;
while(true) {
string::size_type co = slist.find(',',p);
string f = (co==string::npos)
?slist.substr(p):slist.substr(p,co-p);
signeds.insert(f);
if(co==string::npos) break;
p = co+1;
}
}
bool has_field(const string& n) const {
return signeds.find(n)!=signeds.end() && x.has_field(n); }
const string& get_field(const string& n) const {
if(signeds.find(n)==signeds.end())
throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed");
return x.get_field(n); }
fields_iterator fields_begin() const {
return signeds.begin(); }
fields_iterator fields_end() const {
return signeds.end(); }
};
static void parse_query(const string& u,string::size_type q,
map<string,string>& p) {
if(q==string::npos)
return;
assert(u[q]=='?');
++q;
string::size_type l = u.size();
while(q<l) {
string::size_type eq = u.find('=',q);
string::size_type am = u.find('&',q);
if(am==string::npos) {
if(eq==string::npos) {
p[""] = u.substr(q);
}else{
p[u.substr(q,eq-q)] = u.substr(eq+1);
}
break;
}else{
if(eq==string::npos || eq>am) {
p[""] = u.substr(q,eq-q);
}else{
p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1);
}
q = ++am;
}
}
}
void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) {
reset_vars();
bool o2 = om.has_field("ns")
- && om.get_field("ns")==OIURI_OPENID20;
+ && om.get_field("ns")==OIURI_OPENID20
+ && om.has_field("op_endpoint") && !om.get_field("op_endpoint").empty();
if( (!o2) && om.has_field("user_setup_url"))
throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",
om.get_field("user_setup_url"));
string m = om.get_field("mode");
if(o2 && m=="setup_needed")
throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided");
if(m=="cancel")
throw id_res_cancel(OPKELE_CP_ "authentication cancelled");
bool go_dumb=false;
try {
string OP = o2
?om.get_field("op_endpoint")
:get_endpoint().uri;
assoc_t assoc = retrieve_assoc(
OP,om.get_field("assoc_handle"));
if(om.get_field("sig")!=util::base64_signature(assoc,om))
throw id_res_mismatch(OPKELE_CP_ "signature mismatch");
}catch(dumb_RP& drp) {
go_dumb=true;
}catch(failed_lookup& e) {
go_dumb=true;
} OPKELE_RETHROW
if(go_dumb) {
try {
string OP = o2
?om.get_field("op_endpoint")
:get_endpoint().uri;
check_authentication(OP,om);
}catch(failed_check_authentication& fca) {
throw id_res_failed(OPKELE_CP_ "failed to check_authentication()");
} OPKELE_RETHROW
}
signed_part_message_proxy signeds(om);
if(o2) {
check_nonce(om.get_field("op_endpoint"),
om.get_field("response_nonce"));
static const char *mustsign[] = {
"op_endpoint", "return_to", "response_nonce", "assoc_handle",
"claimed_id", "identity" };
for(size_t ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) {
if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms]))
throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs");
}
if( (
(om.has_field("claimed_id")?1:0)
^
(om.has_field("identity")?1:0)
)&1 )
throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent");
string turl = util::rfc_3986_normalize_uri(get_this_url());
util::strip_uri_fragment_part(turl);
string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to"));
util::strip_uri_fragment_part(rurl);
string::size_type
tq = turl.find('?'), rq = rurl.find('?');
if(
((tq==string::npos)?turl:turl.substr(0,tq))
!=
((rq==string::npos)?rurl:rurl.substr(0,rq))
)
throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url");
map<string,string> tp; parse_query(turl,tq,tp);
map<string,string> rp; parse_query(rurl,rq,rp);
for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) {
map<string,string>::const_iterator tpi = tp.find(rpi->first);
if(tpi==tp.end())
throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request");
if(tpi->second!=rpi->second)
throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request");
}
if(om.has_field("claimed_id")) {
claimed_id = om.get_field("claimed_id");
identity = om.get_field("identity");
verify_OP(
om.get_field("op_endpoint"),
claimed_id, identity );
}
}else{
claimed_id = get_endpoint().claimed_id;
/* TODO: check if this is the identity we asked for */
identity = om.get_field("identity");
}
if(ext) ext->rp_id_res_hook(om,signeds);
}
void basic_RP::check_authentication(const string& OP,
const basic_openid_message& om){
openid_message_t res;
static const string checkauthmode = "check_authentication";
direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP);
if(res.has_field("is_valid")) {
if(res.get_field("is_valid")=="true") {
if(res.has_field("invalidate_handle"))
diff --git a/lib/discovery.cc b/lib/discovery.cc
index bd1f917..b4ed3b6 100644
--- a/lib/discovery.cc
+++ b/lib/discovery.cc
@@ -190,398 +190,421 @@ namespace opkele {
string::size_type qp = id.find('?');
if(qp==string::npos || qp<fp)
id.erase(fp);
else if(qp>fp)
id.erase(fp,qp-fp);
}
rv = idis.normalized_id = util::rfc_3986_normalize_uri(id);
discover_at(idis,id,xmode_html|xmode_xrd);
const char * eu = 0;
CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu);
if(r)
throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r);
string cid = util::strip_uri_fragment_part( idis.canonicalized_id = util::rfc_3986_normalize_uri(eu) );
if(xrds_location.empty()) {
if(idis.xrd.empty())
html2xrd(oi,idis);
else{
for(const service_type_t *st=op_service_types;
st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st)
queue_endpoints(oi,idis,st);
}
}else{
idis.clear();
idis.canonicalized_id = cid;
discover_at(idis,xrds_location,xmode_xrd);
if(idis.xrd.empty())
html2xrd(oi,idis);
else{
for(const service_type_t *st=op_service_types;
st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st)
queue_endpoints(oi,idis,st);
}
}
}
return rv;
}
void discover_at(idiscovery_t& idis,const string& url,int xm) {
CURLcode r = easy_setopt(CURLOPT_MAXREDIRS, (xm&xmode_noredirs)?0:5);
if(r)
throw exception_curl(OPKELE_CP_ "failed to set curly maxredirs option");
if( (r=easy_setopt(CURLOPT_URL,url.c_str())) )
throw exception_curl(OPKELE_CP_ "failed to set curly urlie",r);
http_content_type.clear();
xmode = xm;
prepare_to_parse();
if(xmode&xmode_html) {
xrds_location.clear();
save_html.clear();
save_html.reserve(max_html);
}
xrd = &idis.xrd;
r = easy_perform();
if(r && r!=CURLE_WRITE_ERROR)
throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
if(!parser_choked) {
parse(0,0,true);
}else if(xmode&xmode_html){
/* TODO: do not bother if we've seen xml */
try {
util::tidy_doc_t td = util::tidy_doc_t::create();
if(!td)
throw exception_tidy(OPKELE_CP_ "failed to create htmltidy document");
#ifdef NDEBUG
td.opt_set(TidyQuiet,true);
td.opt_set(TidyShowWarnings,false);
#else /* NDEBUG */
td.opt_set(TidyQuiet,false);
td.opt_set(TidyShowWarnings,true);
#endif /* NDEBUG */
td.opt_set(TidyForceOutput,true);
td.opt_set(TidyXhtmlOut,true);
td.opt_set(TidyDoctypeMode,TidyDoctypeOmit);
td.opt_set(TidyMark,false);
td.opt_set(TidyNumEntities,true);
if(td.parse_string(save_html)<=0)
throw exception_tidy(OPKELE_CP_ "tidy failed to parse document");
if(td.clean_and_repair()<=0)
throw exception_tidy(OPKELE_CP_ "tidy failed to clean and repair");
util::tidy_buf_t tide;
if(td.save_buffer(tide)<=0)
throw exception_tidy(OPKELE_CP_ "tidy failed to save buffer");
prepare_to_parse();
parse(tide.c_str(),tide.size(),true);
}catch(exception_tidy& et) { }
}
save_html.clear();
}
void prepare_to_parse() {
(*(expat_t*)this) = parser_create_ns();
set_user_data(); set_element_handler();
set_character_data_handler();
+ set_unknown_encoding_handler();
if(xmode&xmode_html) {
html_openid1.clear(); html_openid2.clear();
parser_choked = false;
}
cdata = 0; xrd_service = 0; skipping = 0;
pt_stack.clear();
status_code = 100; status_string.clear();
}
void html2xrd(endpoint_discovery_iterator& oi,idiscovery_t& id) {
XRD_t& x = id.xrd;
if(!html_openid2.uris.empty()) {
html_openid2.types.insert(STURI_OPENID20);
x.services.add(-1,html_openid2);
queue_endpoints(oi,id,&op_service_types[st_index_2]);
}
if(!html_openid1.uris.empty()) {
html_openid1.types.insert(STURI_OPENID11);
x.services.add(-1,html_openid1);
queue_endpoints(oi,id,&op_service_types[st_index_1]);
}
}
size_t write(void *p,size_t s,size_t nm) {
/* TODO: limit total size */
size_t bytes = s*nm;
const char *inbuf = (const char*)p;
if(xmode&xmode_html) {
size_t mbts = save_html.capacity()-save_html.size();
size_t bts = 0;
if(mbts>0) {
bts = (bytes>mbts)?mbts:bytes;
save_html.append(inbuf,bts);
}
if(skipping<0) return bts;
}
if(skipping<0) return 0;
bool rp = parse(inbuf,bytes,false);
if(!rp) {
parser_choked = true;
skipping = -1;
if(!(xmode&xmode_html))
bytes = 0;
}
return bytes;
}
size_t header(void *p,size_t s,size_t nm) {
size_t bytes = s*nm;
const char *h = (const char*)p;
const char *colon = (const char*)memchr(p,':',bytes);
const char *space = (const char*)memchr(p,' ',bytes);
if(space && ( (!colon) || space<colon ) ) {
xrds_location.clear(); http_content_type.clear();
}else if(colon) {
const char *hv = ++colon;
size_t hnl = colon-h;
int rb;
for(rb = bytes-hnl-1;rb>0 && isspace(*hv);++hv,--rb) ;
while(rb>0 && isspace(hv[rb-1])) --rb;
if(rb) {
if( (hnl>=sizeof(XRDS_HEADER))
&& !strncasecmp(h,XRDS_HEADER":",
sizeof(XRDS_HEADER)) ) {
xrds_location.assign(hv,rb);
}else if( (hnl>=sizeof(CT_HEADER))
&& !strncasecmp(h,CT_HEADER":",
sizeof(CT_HEADER)) ) {
const char *sc = (const char*)memchr(
hv,';',rb);
http_content_type.assign(hv,sc?(sc-hv):rb);
}
}
}
return curl_t::header(p,s,nm);
}
void start_element(const XML_Char *n,const XML_Char **a) {
if(skipping<0) return;
if(skipping) {
if(xmode&xmode_html)
html_start_element(n,a);
++skipping; return;
}
if(pt_stack.empty()) {
if(is_qelement(n,NSURI_XRDS "\tXRDS"))
return;
if(is_qelement(n,NSURI_XRD "\tXRD")) {
assert(xrd);
xrd->clear();
pt_stack.push_back(n);
}else if(xmode&xmode_html) {
html_start_element(n,a);
}else{
skipping = -1;
}
}else{
int pt_s = pt_stack.size();
if(pt_s==1) {
if(is_qelement(n,NSURI_XRD "\tCanonicalID")) {
assert(xrd);
cdata = &(xrd->canonical_ids.add(element_priority(a),string()));
}else if(is_qelement(n,NSURI_XRD "\tLocalID")) {
assert(xrd);
cdata = &(xrd->local_ids.add(element_priority(a),string()));
}else if(is_qelement(n,NSURI_XRD "\tProviderID")) {
assert(xrd);
cdata = &(xrd->provider_id);
}else if(is_qelement(n,NSURI_XRD "\tService")) {
assert(xrd);
xrd_service = &(xrd->services.add(element_priority(a),
service_t()));
pt_stack.push_back(n);
}else if(is_qelement(n,NSURI_XRD "\tStatus")) {
for(;*a;) {
if(!strcasecmp(*(a++),"code")) {
if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) {
cdata = &status_string;
pt_stack.push_back(n);
break;
}
}else
++a;
}
}else if(is_qelement(n,NSURI_XRD "\tExpires")) {
assert(xrd);
cdata_buf.clear();
cdata = &cdata_buf;
}else if(xmode&xmode_html) {
html_start_element(n,a);
}else{
skipping = 1;
}
}else if(pt_s==2) {
if(is_qelement(pt_stack.back().c_str(), NSURI_XRD "\tService")) {
if(is_qelement(n,NSURI_XRD "\tType")) {
assert(xrd); assert(xrd_service);
cdata_buf.clear();
cdata = &cdata_buf;
}else if(is_qelement(n,NSURI_XRD "\tURI")) {
assert(xrd); assert(xrd_service);
const char *append = element_attr(a,"append");
xrd::uri_t& uri = xrd_service->uris.add(element_priority(a),xrd::uri_t("",append?append:""));
cdata = &uri.uri;
}else if(is_qelement(n,NSURI_XRD "\tLocalID")
|| is_qelement(n,NSURI_OPENID10 "\tDelegate") ) {
assert(xrd); assert(xrd_service);
cdata = &(xrd_service->local_ids.add(element_priority(a),string()));
}else if(is_qelement(n,NSURI_XRD "\tProviderID")) {
assert(xrd); assert(xrd_service);
cdata = &(xrd_service->provider_id);
}else{
skipping = 1;
}
}else
skipping = 1;
}else if(xmode&xmode_html) {
html_start_element(n,a);
}else{
skipping = 1;
}
}
}
void end_element(const XML_Char *n) {
if(skipping<0) return;
if(skipping) {
--skipping; return;
}
if(is_qelement(n,NSURI_XRD "\tType")) {
- assert(xrd); assert(xrd_service); assert(cdata==&cdata_buf);
- xrd_service->types.insert(cdata_buf);
+ if(xrd && xrd_service) {
+ assert(cdata==&cdata_buf);
+ xrd_service->types.insert(cdata_buf);
+ }
}else if(is_qelement(n,NSURI_XRD "\tService")) {
- assert(xrd); assert(xrd_service);
- assert(!pt_stack.empty());
- assert(pt_stack.back()==(NSURI_XRD "\tService"));
- pt_stack.pop_back();
- xrd_service = 0;
- }else if(is_qelement(n,NSURI_XRD "\tStatus")) {
- assert(xrd);
- if(is_qelement(pt_stack.back().c_str(),n)) {
- assert(cdata==&status_string);
+ if(!(xrd && xrd_service)) {
+ skipping = -1;
+ }else{
+ assert(!pt_stack.empty());
+ assert(pt_stack.back()==(NSURI_XRD "\tService"));
pt_stack.pop_back();
- if(status_code!=100)
- skipping = -1;
+ xrd_service = 0;
+ }
+ }else if(is_qelement(n,NSURI_XRD "\tStatus")) {
+ if(!xrd) {
+ skipping=-1;
+ }else{
+ if(is_qelement(pt_stack.back().c_str(),n)) {
+ assert(cdata==&status_string);
+ pt_stack.pop_back();
+ if(status_code!=100)
+ skipping = -1;
+ }
}
}else if(is_qelement(n,NSURI_XRD "\tExpires")) {
- assert(xrd);
- xrd->expires = util::w3c_to_time(cdata_buf);
+ if(!xrd) {
+ skipping=-1;
+ }else{
+ xrd->expires = util::w3c_to_time(cdata_buf);
+ }
+ }else if(is_qelement(n,NSURI_XRD "\tXRD")) {
+ assert(!pt_stack.empty());
+ assert(pt_stack.back()==(NSURI_XRD "\tXRD"));
+ pt_stack.pop_back();
}else if((xmode&xmode_html) && is_element(n,"head")) {
skipping = -1;
}
cdata = 0;
}
void character_data(const XML_Char *s,int l) {
if(skipping) return;
if(cdata) cdata->append(s,l);
}
void html_start_element(const XML_Char *n,const XML_Char **a) {
if(is_element(n,"meta")) {
bool heq = false;
string l;
for(;*a;a+=2) {
if(!( strcasecmp(a[0],"http-equiv")
|| strcasecmp(a[1],XRDS_HEADER) ))
heq = true;
else if(!strcasecmp(a[0],"content"))
l.assign(a[1]);
}
if(heq)
xrds_location = l;
}else if(is_element(n,"link")) {
string rels;
string href;
for(;*a;a+=2) {
if( !strcasecmp(a[0],"rel") ) {
rels.assign(a[1]);
}else if( !strcasecmp(a[0],"href") ) {
const char *ns = a[1];
for(;*ns && isspace(*ns);++ns) ;
href.assign(ns);
string::size_type lns=href.find_last_not_of(data::_whitespace_chars);
href.erase(lns+1);
}
}
for(string::size_type ns=rels.find_first_not_of(data::_whitespace_chars);
ns!=string::npos; ns=rels.find_first_not_of(data::_whitespace_chars,ns)) {
string::size_type s = rels.find_first_of(data::_whitespace_chars,ns);
string rel;
if(s==string::npos) {
rel.assign(rels,ns,string::npos);
ns = string::npos;
}else{
rel.assign(rels,ns,s-ns);
ns = s;
}
if(rel=="openid.server")
html_openid1.uris.add(-1,xrd::uri_t(href));
else if(rel=="openid.delegate")
html_openid1.local_ids.add(-1,href);
else if(rel=="openid2.provider")
html_openid2.uris.add(-1,xrd::uri_t(href));
else if(rel=="openid2.local_id")
html_openid2.local_ids.add(-1,href);
}
}else if(is_element(n,"body")) {
skipping = -1;
}
}
void queue_endpoints(endpoint_discovery_iterator& oi,
const idiscovery_t &id,
const service_type_t *st) {
openid_endpoint_t ep;
ep.claimed_id = id.canonicalized_id;
for(xrd::services_t::const_iterator isvc=id.xrd.services.begin();
isvc!=id.xrd.services.end(); ++isvc) {
const xrd::service_t svc = isvc->second;
if(svc.types.find(st->uri)==svc.types.end()) continue;
for(xrd::uris_t::const_iterator iu=svc.uris.begin();iu!=svc.uris.end();++iu) {
ep.uri = iu->second.uri;
if(id.xri_identity) {
if(iu->second.append=="qxri") {
ep.uri += id.normalized_id;
} /* TODO: else handle other append attribute values */
}
if(st->forceid) {
ep.local_id = ep.claimed_id = st->forceid;
*(oi++) = ep;
}else{
if(svc.local_ids.empty()) {
ep.local_id = ep.claimed_id;
*(oi++) = ep;
}else{
for(xrd::local_ids_t::const_iterator ilid=svc.local_ids.begin();
ilid!=svc.local_ids.end(); ++ilid) {
ep.local_id = ilid->second;
*(oi++) = ep;
}
}
}
}
}
}
+ int unknown_encoding(const XML_Char* /* n */,XML_Encoding *i) {
+ for(unsigned int ii=0;ii < sizeof(i->map)/sizeof(i->map[0]);++ii)
+ i->map[ii] = ii;
+ i->convert = 0; i->release = 0;
+ return XML_STATUS_OK;
+ }
+
};
string idiscover(endpoint_discovery_iterator oi,const string& identity) {
idigger_t idigger;
return idigger.discover(oi,identity);
}
void yadiscover(endpoint_discovery_iterator oi,const string& yurl,const char **types,bool redirs) try {
idigger_t idigger;
idigger.yadiscover(oi,yurl,types,redirs);
}catch(exception_curl& ec) {
if(redirs || ec._error!=CURLE_TOO_MANY_REDIRECTS)
throw;
}
}
diff --git a/lib/expat.cc b/lib/expat.cc
index c4dab7e..fb58a9a 100644
--- a/lib/expat.cc
+++ b/lib/expat.cc
@@ -1,97 +1,106 @@
#include <opkele/expat.h>
namespace opkele {
namespace util {
expat_t::~expat_t() throw() {
if(_x)
XML_ParserFree(_x);
}
expat_t& expat_t::operator=(XML_Parser x) {
if(_x)
XML_ParserFree(_x);
_x = x;
return *this;
}
static void _start_element(void* ud,const XML_Char *n,const XML_Char **a) {
((expat_t*)ud)->start_element(n,a);
}
static void _end_element(void *ud,const XML_Char *n) {
((expat_t*)ud)->end_element(n);
}
void expat_t::set_element_handler() {
assert(_x);
XML_SetElementHandler(_x,_start_element,_end_element);
}
static void _character_data(void *ud,const XML_Char *s,int l) {
((expat_t*)ud)->character_data(s,l);
}
void expat_t::set_character_data_handler() {
assert(_x);
XML_SetCharacterDataHandler(_x,_character_data);
}
static void _processing_instruction(void *ud,const XML_Char *t,const XML_Char *d) {
((expat_t*)ud)->processing_instruction(t,d);
}
void expat_t::set_processing_instruction_handler() {
assert(_x);
XML_SetProcessingInstructionHandler(_x,_processing_instruction);
}
static void _comment(void *ud,const XML_Char *d) {
((expat_t*)ud)->comment(d);
}
void expat_t::set_comment_handler() {
assert(_x);
XML_SetCommentHandler(_x,_comment);
}
static void _start_cdata_section(void *ud) {
((expat_t*)ud)->start_cdata_section();
}
static void _end_cdata_section(void *ud) {
((expat_t*)ud)->end_cdata_section();
}
void expat_t::set_cdata_section_handler() {
assert(_x);
XML_SetCdataSectionHandler(_x,_start_cdata_section,_end_cdata_section);
}
static void _default_handler(void *ud,const XML_Char *s,int l) {
((expat_t*)ud)->default_handler(s,l);
}
void expat_t::set_default_handler() {
assert(_x);
XML_SetDefaultHandler(_x,_default_handler);
}
void expat_t::set_default_handler_expand() {
assert(_x);
XML_SetDefaultHandlerExpand(_x,_default_handler);
}
static void _start_namespace_decl(void *ud,const XML_Char *p,const XML_Char *u) {
((expat_t*)ud)->start_namespace_decl(p,u);
}
static void _end_namespace_decl(void *ud,const XML_Char *p) {
((expat_t*)ud)->end_namespace_decl(p);
}
void expat_t::set_namespace_decl_handler() {
assert(_x);
XML_SetNamespaceDeclHandler(_x,_start_namespace_decl,_end_namespace_decl);
}
+ static int _unknown_encoding(void *ehd,const XML_Char *n,XML_Encoding *i) {
+ return ((expat_t*)ehd)->unknown_encoding(n,i);
+ }
+
+ void expat_t::set_unknown_encoding_handler() {
+ assert(_x);
+ XML_SetUnknownEncodingHandler(_x,_unknown_encoding,this);
+ }
+
}
}