-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 | |||
@@ -18,10 +18,27 @@ namespace opkele { | |||
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 | } |
@@ -48,7 +65,7 @@ namespace opkele { | |||
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 | ||
@@ -84,11 +101,12 @@ namespace opkele { | |||
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)) |
@@ -96,22 +114,51 @@ namespace opkele { | |||
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('#'); |
@@ -122,24 +169,33 @@ namespace opkele { | |||
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); |
@@ -152,7 +208,7 @@ namespace opkele { | |||
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) |
@@ -199,17 +255,21 @@ namespace opkele { | |||
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 | ||
@@ -310,7 +370,8 @@ namespace opkele { | |||
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); |
@@ -436,11 +497,41 @@ namespace opkele { | |||
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 | } |