author | Michael Krelin <hacker@klever.net> | 2008-01-20 21:08:05 (UTC) |
---|---|---|
committer | Michael Krelin <hacker@klever.net> | 2008-01-20 21:08:05 (UTC) |
commit | 9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc (patch) (unidiff) | |
tree | 702473142242e80538c4801cc379ec98fba199dd /lib/discovery.cc | |
parent | 395a126cbf59b7a50f44da3096b68bab412ab33d (diff) | |
download | libopkele-9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc.zip libopkele-9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc.tar.gz libopkele-9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc.tar.bz2 |
the whole library rewritten
Signed-off-by: Michael Krelin <hacker@klever.net>
-rw-r--r-- | lib/discovery.cc | 161 |
1 files changed, 126 insertions, 35 deletions
diff --git a/lib/discovery.cc b/lib/discovery.cc index d868308..93409f4 100644 --- a/lib/discovery.cc +++ b/lib/discovery.cc | |||
@@ -13,20 +13,37 @@ | |||
13 | #define XRDS_HEADER "X-XRDS-Location" | 13 | #define XRDS_HEADER "X-XRDS-Location" |
14 | #define CT_HEADER "Content-Type" | 14 | #define CT_HEADER "Content-Type" |
15 | 15 | ||
16 | namespace opkele { | 16 | namespace opkele { |
17 | using std::list; | 17 | using std::list; |
18 | using xrd::XRD_t; | 18 | using xrd::XRD_t; |
19 | using xrd::service_t; | 19 | using xrd::service_t; |
20 | 20 | ||
21 | /* TODO: the whole discovery thing needs cleanup and optimization due to | ||
22 | * many changes of concept. */ | ||
23 | |||
21 | static const char *whitespace = " \t\r\n"; | 24 | static const char *whitespace = " \t\r\n"; |
22 | static const char *i_leaders = "=@+$!("; | 25 | static const char *i_leaders = "=@+$!("; |
23 | static const size_t max_html = 16384; | 26 | static const size_t max_html = 16384; |
24 | 27 | ||
28 | static const struct service_type_t { | ||
29 | const char *uri; | ||
30 | const char *forceid; | ||
31 | } service_types[] = { | ||
32 | { STURI_OPENID20_OP, IDURI_SELECT20 }, | ||
33 | { STURI_OPENID20, 0 }, | ||
34 | { STURI_OPENID11, 0 }, | ||
35 | { STURI_OPENID10, 0 } | ||
36 | }; | ||
37 | enum { | ||
38 | st_index_1 = 2, st_index_2 = 1 | ||
39 | }; | ||
40 | |||
41 | |||
25 | static inline bool is_qelement(const XML_Char *n,const char *qen) { | 42 | static inline bool is_qelement(const XML_Char *n,const char *qen) { |
26 | return !strcasecmp(n,qen); | 43 | return !strcasecmp(n,qen); |
27 | } | 44 | } |
28 | static inline bool is_element(const XML_Char *n,const char *en) { | 45 | static inline bool is_element(const XML_Char *n,const char *en) { |
29 | if(!strcasecmp(n,en)) return true; | 46 | if(!strcasecmp(n,en)) return true; |
30 | int nl = strlen(n), enl = strlen(en); | 47 | int nl = strlen(n), enl = strlen(en); |
31 | if( (nl>=(enl+1)) && n[nl-enl-1]=='\t' | 48 | if( (nl>=(enl+1)) && n[nl-enl-1]=='\t' |
32 | && !strcasecmp(&n[nl-enl],en) ) | 49 | && !strcasecmp(&n[nl-enl],en) ) |
@@ -43,17 +60,17 @@ namespace opkele { | |||
43 | return -1; | 60 | return -1; |
44 | } | 61 | } |
45 | 62 | ||
46 | class idigger_t : public util::curl_t, public util::expat_t { | 63 | class idigger_t : public util::curl_t, public util::expat_t { |
47 | public: | 64 | public: |
48 | string xri_proxy; | 65 | string xri_proxy; |
49 | 66 | ||
50 | enum { | 67 | enum { |
51 | xmode_html = 1, xmode_xrd = 2 | 68 | xmode_html = 1, xmode_xrd = 2, xmode_cid = 4 |
52 | }; | 69 | }; |
53 | int xmode; | 70 | int xmode; |
54 | 71 | ||
55 | string xrds_location; | 72 | string xrds_location; |
56 | string http_content_type; | 73 | string http_content_type; |
57 | service_t html_openid1; | 74 | service_t html_openid1; |
58 | service_t html_openid2; | 75 | service_t html_openid2; |
59 | string cdata_buf; | 76 | string cdata_buf; |
@@ -79,85 +96,124 @@ namespace opkele { | |||
79 | || (r=set_write()) | 96 | || (r=set_write()) |
80 | || (r=set_header()) | 97 | || (r=set_header()) |
81 | ; | 98 | ; |
82 | if(r) | 99 | if(r) |
83 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | 100 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); |
84 | } | 101 | } |
85 | ~idigger_t() throw() { } | 102 | ~idigger_t() throw() { } |
86 | 103 | ||
87 | void discover(idiscovery_t& result,const string& identity) { | 104 | string discover(endpoint_discovery_iterator& oi,const string& identity) { |
88 | result.clear(); | 105 | string rv; |
106 | idiscovery_t idis; | ||
89 | string::size_type fsc = identity.find_first_not_of(whitespace); | 107 | string::size_type fsc = identity.find_first_not_of(whitespace); |
90 | if(fsc==string::npos) | 108 | if(fsc==string::npos) |
91 | throw bad_input(OPKELE_CP_ "whtiespace-only identity"); | 109 | throw bad_input(OPKELE_CP_ "whitespace-only identity"); |
92 | string::size_type lsc = identity.find_last_not_of(whitespace); | 110 | string::size_type lsc = identity.find_last_not_of(whitespace); |
93 | assert(lsc!=string::npos); | 111 | assert(lsc!=string::npos); |
94 | if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1)) | 112 | if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1)) |
95 | fsc += sizeof("xri://")-1; | 113 | fsc += sizeof("xri://")-1; |
96 | if((fsc+1)>=lsc) | 114 | if((fsc+1)>=lsc) |
97 | throw bad_input(OPKELE_CP_ "not a character of importance in identity"); | 115 | throw bad_input(OPKELE_CP_ "not a character of importance in identity"); |
98 | string id(identity,fsc,lsc-fsc+1); | 116 | string id(identity,fsc,lsc-fsc+1); |
117 | idis.clear(); | ||
99 | if(strchr(i_leaders,id[0])) { | 118 | if(strchr(i_leaders,id[0])) { |
100 | result.normalized_id = id; | 119 | /* TODO: further normalize xri identity? Like folding case |
101 | result.xri_identity = true; | 120 | * or whatever... */ |
102 | /* TODO: further canonicalize xri identity? Like folding case or whatever... */ | 121 | rv = idis.normalized_id = id; |
103 | discover_at( | 122 | idis.xri_identity = true; |
104 | result, | 123 | set<string> cids; |
105 | xri_proxy + util::url_encode(id)+ | 124 | for(const struct service_type_t *st=service_types; |
106 | "?_xrd_r=application/xrd+xml;sep=false", xmode_xrd); | 125 | st<&service_types[sizeof(service_types)/sizeof(*service_types)];++st) { |
107 | if(status_code!=100) | 126 | idis.clear(); |
108 | throw failed_xri_resolution(OPKELE_CP_ | 127 | discover_at( idis, |
109 | "XRI resolution failed with '"+status_string+"' message",status_code); | 128 | xri_proxy + util::url_encode(id)+ |
110 | if(result.xrd.canonical_ids.empty()) | 129 | "?_xrd_t="+util::url_encode(st->uri)+ |
111 | throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID for XRI identity found"); | 130 | "&_xrd_r=application/xrd%2Bxml" |
112 | result.canonicalized_id = result.xrd.canonical_ids.begin()->second; | 131 | ";sep=true;refs=true", |
132 | xmode_xrd ); | ||
133 | if(status_code==241) continue; | ||
134 | if(status_code!=100) | ||
135 | throw failed_xri_resolution(OPKELE_CP_ | ||
136 | "XRI resolution failed with '"+status_string+"' message" | ||
137 | ", while looking for SEP with type '"+st->uri+"'", status_code); | ||
138 | if(idis.xrd.canonical_ids.empty()) | ||
139 | throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID found for XRI identity found"); | ||
140 | string cid = idis.xrd.canonical_ids.begin()->second; | ||
141 | if(cids.find(cid)==cids.end()) { | ||
142 | cids.insert(cid); | ||
143 | idis.clear(); | ||
144 | discover_at( idis, | ||
145 | xri_proxy + util::url_encode(id)+ | ||
146 | "?_xrd_t="+util::url_encode(st->uri)+ | ||
147 | "&_xrd_r=application/xrd%2Bxml" | ||
148 | ";sep=true;refs=true", | ||
149 | xmode_xrd ); | ||
150 | if(status_code==241) continue; | ||
151 | if(status_code!=100) | ||
152 | throw failed_xri_resolution(OPKELE_CP_ | ||
153 | "XRI resolution failed with '"+status_string+"' message" | ||
154 | ", while looking for SEP with type '"+st->uri+"'" | ||
155 | " on canonical id", status_code); | ||
156 | } | ||
157 | idis.canonicalized_id = cid; | ||
158 | queue_endpoints(oi,idis,st); | ||
159 | } | ||
113 | }else{ | 160 | }else{ |
114 | result.xri_identity = false; | 161 | idis.xri_identity = false; |
115 | if(id.find("://")==string::npos) | 162 | if(id.find("://")==string::npos) |
116 | id.insert(0,"http://"); | 163 | id.insert(0,"http://"); |
117 | string::size_type fp = id.find('#'); | 164 | string::size_type fp = id.find('#'); |
118 | if(fp!=string::npos) { | 165 | if(fp!=string::npos) { |
119 | string::size_type qp = id.find('?'); | 166 | string::size_type qp = id.find('?'); |
120 | if(qp==string::npos || qp<fp) | 167 | if(qp==string::npos || qp<fp) |
121 | id.erase(fp); | 168 | id.erase(fp); |
122 | else if(qp>fp) | 169 | else if(qp>fp) |
123 | id.erase(fp,qp-fp); | 170 | id.erase(fp,qp-fp); |
124 | } | 171 | } |
125 | result.normalized_id = util::rfc_3986_normalize_uri(id); | 172 | rv = idis.normalized_id = util::rfc_3986_normalize_uri(id); |
126 | discover_at(result,id,xmode_html|xmode_xrd); | 173 | discover_at(idis,id,xmode_html|xmode_xrd); |
127 | const char * eu = 0; | 174 | const char * eu = 0; |
128 | CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); | 175 | CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); |
129 | if(r) | 176 | if(r) |
130 | throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); | 177 | throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); |
131 | result.canonicalized_id = util::rfc_3986_normalize_uri(eu); /* XXX: strip fragment part? */ | 178 | string cid = util::strip_uri_fragment_part( idis.canonicalized_id = util::rfc_3986_normalize_uri(eu) ); |
132 | if(xrds_location.empty()) { | 179 | if(xrds_location.empty()) { |
133 | html2xrd(result.xrd); | 180 | html2xrd(oi,idis); |
134 | }else{ | 181 | }else{ |
135 | discover_at(result,xrds_location,xmode_xrd); | 182 | idis.clear(); |
136 | if(result.xrd.empty()) | 183 | idis.canonicalized_id = cid; |
137 | html2xrd(result.xrd); | 184 | discover_at(idis,xrds_location,xmode_xrd); |
185 | if(idis.xrd.empty()) | ||
186 | html2xrd(oi,idis); | ||
187 | else{ | ||
188 | for(const service_type_t *st=service_types; | ||
189 | st<&service_types[sizeof(service_types)/sizeof(*service_types)];++st) | ||
190 | queue_endpoints(oi,idis,st); | ||
191 | } | ||
138 | } | 192 | } |
139 | } | 193 | } |
194 | return rv; | ||
140 | } | 195 | } |
141 | 196 | ||
142 | void discover_at(idiscovery_t& result,const string& url,int xm) { | 197 | void discover_at(idiscovery_t& idis,const string& url,int xm) { |
198 | DOUT_("Doing discovery at " << url); | ||
143 | CURLcode r = easy_setopt(CURLOPT_URL,url.c_str()); | 199 | CURLcode r = easy_setopt(CURLOPT_URL,url.c_str()); |
144 | if(r) | 200 | if(r) |
145 | throw exception_curl(OPKELE_CP_ "failed to set culry urlie",r); | 201 | throw exception_curl(OPKELE_CP_ "failed to set culry urlie",r); |
146 | 202 | ||
147 | http_content_type.clear(); | 203 | http_content_type.clear(); |
148 | xmode = xm; | 204 | xmode = xm; |
149 | prepare_to_parse(); | 205 | prepare_to_parse(); |
150 | if(xmode&xmode_html) { | 206 | if(xmode&xmode_html) { |
151 | xrds_location.clear(); | 207 | xrds_location.clear(); |
152 | save_html.clear(); | 208 | save_html.clear(); |
153 | save_html.reserve(max_html); | 209 | save_html.reserve(max_html); |
154 | } | 210 | } |
155 | xrd = &result.xrd; | 211 | xrd = &idis.xrd; |
156 | 212 | ||
157 | r = easy_perform(); | 213 | r = easy_perform(); |
158 | if(r && r!=CURLE_WRITE_ERROR) | 214 | if(r && r!=CURLE_WRITE_ERROR) |
159 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | 215 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); |
160 | 216 | ||
161 | if(!parser_choked) { | 217 | if(!parser_choked) { |
162 | parse(0,0,true); | 218 | parse(0,0,true); |
163 | }else{ | 219 | }else{ |
@@ -194,27 +250,31 @@ namespace opkele { | |||
194 | set_character_data_handler(); | 250 | set_character_data_handler(); |
195 | 251 | ||
196 | if(xmode&xmode_html) { | 252 | if(xmode&xmode_html) { |
197 | html_openid1.clear(); html_openid2.clear(); | 253 | html_openid1.clear(); html_openid2.clear(); |
198 | parser_choked = false; | 254 | parser_choked = false; |
199 | } | 255 | } |
200 | 256 | ||
201 | cdata = 0; xrd_service = 0; skipping = 0; | 257 | cdata = 0; xrd_service = 0; skipping = 0; |
258 | pt_stack.clear(); | ||
202 | status_code = 100; status_string.clear(); | 259 | status_code = 100; status_string.clear(); |
203 | } | 260 | } |
204 | 261 | ||
205 | void html2xrd(XRD_t& x) { | 262 | void html2xrd(endpoint_discovery_iterator& oi,idiscovery_t& id) { |
206 | if(!html_openid1.uris.empty()) { | 263 | XRD_t& x = id.xrd; |
207 | html_openid1.types.insert(STURI_OPENID11); | ||
208 | x.services.add(-1,html_openid1); | ||
209 | } | ||
210 | if(!html_openid2.uris.empty()) { | 264 | if(!html_openid2.uris.empty()) { |
211 | html_openid2.types.insert(STURI_OPENID20); | 265 | html_openid2.types.insert(STURI_OPENID20); |
212 | x.services.add(-1,html_openid2); | 266 | x.services.add(-1,html_openid2); |
267 | queue_endpoints(oi,id,&service_types[st_index_2]); | ||
268 | } | ||
269 | if(!html_openid1.uris.empty()) { | ||
270 | html_openid1.types.insert(STURI_OPENID11); | ||
271 | x.services.add(-1,html_openid1); | ||
272 | queue_endpoints(oi,id,&service_types[st_index_1]); | ||
213 | } | 273 | } |
214 | } | 274 | } |
215 | 275 | ||
216 | size_t write(void *p,size_t s,size_t nm) { | 276 | size_t write(void *p,size_t s,size_t nm) { |
217 | /* TODO: limit total size */ | 277 | /* TODO: limit total size */ |
218 | size_t bytes = s*nm; | 278 | size_t bytes = s*nm; |
219 | const char *inbuf = (const char*)p; | 279 | const char *inbuf = (const char*)p; |
220 | if(xmode&xmode_html) { | 280 | if(xmode&xmode_html) { |
@@ -305,17 +365,18 @@ namespace opkele { | |||
305 | }else if(is_qelement(n,NSURI_XRD "\tStatus")) { | 365 | }else if(is_qelement(n,NSURI_XRD "\tStatus")) { |
306 | for(;*a;) { | 366 | for(;*a;) { |
307 | if(!strcasecmp(*(a++),"code")) { | 367 | if(!strcasecmp(*(a++),"code")) { |
308 | if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) { | 368 | if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) { |
309 | cdata = &status_string; | 369 | cdata = &status_string; |
310 | pt_stack.push_back(n); | 370 | pt_stack.push_back(n); |
311 | break; | 371 | break; |
312 | } | 372 | } |
313 | } | 373 | }else |
374 | ++a; | ||
314 | } | 375 | } |
315 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { | 376 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { |
316 | assert(xrd); | 377 | assert(xrd); |
317 | cdata_buf.clear(); | 378 | cdata_buf.clear(); |
318 | cdata = &cdata_buf; | 379 | cdata = &cdata_buf; |
319 | }else if(xmode&xmode_html) { | 380 | }else if(xmode&xmode_html) { |
320 | html_start_element(n,a); | 381 | html_start_element(n,a); |
321 | }else{ | 382 | }else{ |
@@ -431,16 +492,46 @@ namespace opkele { | |||
431 | else if(rel=="openid2.local_id") | 492 | else if(rel=="openid2.local_id") |
432 | html_openid2.local_ids.add(-1,href); | 493 | html_openid2.local_ids.add(-1,href); |
433 | } | 494 | } |
434 | }else if(is_element(n,"body")) { | 495 | }else if(is_element(n,"body")) { |
435 | skipping = -1; | 496 | skipping = -1; |
436 | } | 497 | } |
437 | } | 498 | } |
438 | 499 | ||
500 | void queue_endpoints(endpoint_discovery_iterator& oi, | ||
501 | const idiscovery_t &id, | ||
502 | const service_type_t *st) { | ||
503 | openid_endpoint_t ep; | ||
504 | ep.claimed_id = id.canonicalized_id; | ||
505 | for(xrd::services_t::const_iterator isvc=id.xrd.services.begin(); | ||
506 | isvc!=id.xrd.services.end(); ++isvc) { | ||
507 | const xrd::service_t svc = isvc->second; | ||
508 | if(svc.types.find(st->uri)==svc.types.end()) continue; | ||
509 | for(xrd::uris_t::const_iterator iu=svc.uris.begin();iu!=svc.uris.end();++iu) { | ||
510 | ep.uri = iu->second; | ||
511 | if(st->forceid) { | ||
512 | ep.local_id = ep.claimed_id = st->forceid; | ||
513 | *(oi++) = ep; | ||
514 | }else{ | ||
515 | if(svc.local_ids.empty()) { | ||
516 | ep.local_id = ep.claimed_id; | ||
517 | *(oi++) = ep; | ||
518 | }else{ | ||
519 | for(xrd::local_ids_t::const_iterator ilid=svc.local_ids.begin(); | ||
520 | ilid!=svc.local_ids.end(); ++ilid) { | ||
521 | ep.local_id = ilid->second; | ||
522 | *(oi++) = ep; | ||
523 | } | ||
524 | } | ||
525 | } | ||
526 | } | ||
527 | } | ||
528 | } | ||
529 | |||
439 | }; | 530 | }; |
440 | 531 | ||
441 | void idiscover(idiscovery_t& result,const string& identity) { | 532 | string idiscover(endpoint_discovery_iterator oi,const string& identity) { |
442 | idigger_t idigger; | 533 | idigger_t idigger; |
443 | idigger.discover(result,identity); | 534 | return idigger.discover(oi,identity); |
444 | } | 535 | } |
445 | 536 | ||
446 | } | 537 | } |