summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--lib/discovery.cc1
1 files changed, 1 insertions, 0 deletions
diff --git a/lib/discovery.cc b/lib/discovery.cc
index c118c80..3b90977 100644
--- a/lib/discovery.cc
+++ b/lib/discovery.cc
@@ -1,582 +1,583 @@
1#include <list> 1#include <list>
2#include <opkele/curl.h> 2#include <opkele/curl.h>
3#include <opkele/expat.h> 3#include <opkele/expat.h>
4#include <opkele/uris.h> 4#include <opkele/uris.h>
5#include <opkele/discovery.h> 5#include <opkele/discovery.h>
6#include <opkele/exception.h> 6#include <opkele/exception.h>
7#include <opkele/util.h> 7#include <opkele/util.h>
8#include <opkele/tidy.h> 8#include <opkele/tidy.h>
9#include <opkele/data.h> 9#include <opkele/data.h>
10#include <opkele/debug.h> 10#include <opkele/debug.h>
11 11
12#include "config.h" 12#include "config.h"
13 13
14#define XRDS_HEADER "X-XRDS-Location" 14#define XRDS_HEADER "X-XRDS-Location"
15#define CT_HEADER "Content-Type" 15#define CT_HEADER "Content-Type"
16 16
17namespace opkele { 17namespace opkele {
18 using std::list; 18 using std::list;
19 using xrd::XRD_t; 19 using xrd::XRD_t;
20 using xrd::service_t; 20 using xrd::service_t;
21 21
22 /* TODO: the whole discovery thing needs cleanup and optimization due to 22 /* TODO: the whole discovery thing needs cleanup and optimization due to
23 * many changes of concept. */ 23 * many changes of concept. */
24 24
25 static const size_t max_html = 16384; 25 static const size_t max_html = 16384;
26 26
27 static const struct service_type_t { 27 static const struct service_type_t {
28 const char *uri; 28 const char *uri;
29 const char *forceid; 29 const char *forceid;
30 } op_service_types[] = { 30 } op_service_types[] = {
31 { STURI_OPENID20_OP, IDURI_SELECT20 }, 31 { STURI_OPENID20_OP, IDURI_SELECT20 },
32 { STURI_OPENID20, 0 }, 32 { STURI_OPENID20, 0 },
33 { STURI_OPENID11, 0 }, 33 { STURI_OPENID11, 0 },
34 { STURI_OPENID10, 0 } 34 { STURI_OPENID10, 0 }
35 }; 35 };
36 enum { 36 enum {
37 st_index_1 = 2, st_index_2 = 1 37 st_index_1 = 2, st_index_2 = 1
38 }; 38 };
39 39
40 40
41 static inline bool is_qelement(const XML_Char *n,const char *qen) { 41 static inline bool is_qelement(const XML_Char *n,const char *qen) {
42 return !strcasecmp(n,qen); 42 return !strcasecmp(n,qen);
43 } 43 }
44 static inline bool is_element(const XML_Char *n,const char *en) { 44 static inline bool is_element(const XML_Char *n,const char *en) {
45 if(!strcasecmp(n,en)) return true; 45 if(!strcasecmp(n,en)) return true;
46 int nl = strlen(n), enl = strlen(en); 46 int nl = strlen(n), enl = strlen(en);
47 if( (nl>=(enl+1)) && n[nl-enl-1]=='\t' 47 if( (nl>=(enl+1)) && n[nl-enl-1]=='\t'
48 && !strcasecmp(&n[nl-enl],en) ) 48 && !strcasecmp(&n[nl-enl],en) )
49 return true; 49 return true;
50 return false; 50 return false;
51 } 51 }
52 52
53 static long element_priority(const XML_Char **a) { 53 static long element_priority(const XML_Char **a) {
54 for(;*a;++a) 54 for(;*a;++a)
55 if(!strcasecmp(*(a++),"priority")) { 55 if(!strcasecmp(*(a++),"priority")) {
56 long rv; 56 long rv;
57 return (sscanf(*a,"%ld",&rv)==1)?rv:-1; 57 return (sscanf(*a,"%ld",&rv)==1)?rv:-1;
58 } 58 }
59 return -1; 59 return -1;
60 } 60 }
61 /* TODO: ideally all attributes should be 61 /* TODO: ideally all attributes should be
62 * retrieved in one run */ 62 * retrieved in one run */
63 static const char *element_attr(const XML_Char **a, const char *at) { 63 static const char *element_attr(const XML_Char **a, const char *at) {
64 for(;*a;++a) 64 for(;*a;++a)
65 if(!strcasecmp(*(a++),at)) { 65 if(!strcasecmp(*(a++),at)) {
66 return *a; 66 return *a;
67 } 67 }
68 return 0; 68 return 0;
69 } 69 }
70 70
71 class idigger_t : public util::curl_t, public util::expat_t { 71 class idigger_t : public util::curl_t, public util::expat_t {
72 public: 72 public:
73 string xri_proxy; 73 string xri_proxy;
74 74
75 enum { 75 enum {
76 xmode_html = 1, xmode_xrd = 2, xmode_cid = 4, 76 xmode_html = 1, xmode_xrd = 2, xmode_cid = 4,
77 xmode_noredirs = 8 77 xmode_noredirs = 8
78 }; 78 };
79 int xmode; 79 int xmode;
80 80
81 string xrds_location; 81 string xrds_location;
82 string http_content_type; 82 string http_content_type;
83 service_t html_openid1; 83 service_t html_openid1;
84 service_t html_openid2; 84 service_t html_openid2;
85 string cdata_buf; 85 string cdata_buf;
86 long status_code; 86 long status_code;
87 string status_string; 87 string status_string;
88 88
89 typedef list<string> pt_stack_t; 89 typedef list<string> pt_stack_t;
90 pt_stack_t pt_stack; 90 pt_stack_t pt_stack;
91 int skipping; 91 int skipping;
92 bool parser_choked; 92 bool parser_choked;
93 string save_html; 93 string save_html;
94 94
95 XRD_t *xrd; 95 XRD_t *xrd;
96 service_t *xrd_service; 96 service_t *xrd_service;
97 string* cdata; 97 string* cdata;
98 98
99 idigger_t() 99 idigger_t()
100 : util::curl_t(easy_init()), 100 : util::curl_t(easy_init()),
101 util::expat_t(0), 101 util::expat_t(0),
102 xri_proxy(XRI_PROXY_URL) { 102 xri_proxy(XRI_PROXY_URL) {
103 CURLcode r; 103 CURLcode r;
104 (r=misc_sets()) 104 (r=misc_sets())
105 || (r=set_write()) 105 || (r=set_write())
106 || (r=set_header()) 106 || (r=set_header())
107 ; 107 ;
108 if(r) 108 if(r)
109 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 109 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
110 } 110 }
111 ~idigger_t() throw() { } 111 ~idigger_t() throw() { }
112 112
113 void yadiscover(endpoint_discovery_iterator oi,const string& yurl,const char **types,bool redirs) { 113 void yadiscover(endpoint_discovery_iterator oi,const string& yurl,const char **types,bool redirs) {
114 idiscovery_t idis; 114 idiscovery_t idis;
115 idis.xri_identity = false; 115 idis.xri_identity = false;
116 discover_at(idis,yurl,xmode_html|xmode_xrd|(redirs?0:xmode_noredirs)); 116 discover_at(idis,yurl,xmode_html|xmode_xrd|(redirs?0:xmode_noredirs));
117 if(!xrds_location.empty()) { 117 if(!xrds_location.empty()) {
118 idis.clear(); 118 idis.clear();
119 discover_at(idis,xrds_location,xmode_xrd); 119 discover_at(idis,xrds_location,xmode_xrd);
120 } 120 }
121 idis.normalized_id = idis.canonicalized_id = yurl; 121 idis.normalized_id = idis.canonicalized_id = yurl;
122 service_type_t st; 122 service_type_t st;
123 for(st.uri=*types;*types;st.uri=*(++types)) 123 for(st.uri=*types;*types;st.uri=*(++types))
124 queue_endpoints(oi,idis,&st); 124 queue_endpoints(oi,idis,&st);
125 } 125 }
126 126
127 string discover(endpoint_discovery_iterator& oi,const string& identity) { 127 string discover(endpoint_discovery_iterator& oi,const string& identity) {
128 string rv; 128 string rv;
129 idiscovery_t idis; 129 idiscovery_t idis;
130 string::size_type fsc = identity.find_first_not_of(data::_whitespace_chars); 130 string::size_type fsc = identity.find_first_not_of(data::_whitespace_chars);
131 if(fsc==string::npos) 131 if(fsc==string::npos)
132 throw bad_input(OPKELE_CP_ "whitespace-only identity"); 132 throw bad_input(OPKELE_CP_ "whitespace-only identity");
133 string::size_type lsc = identity.find_last_not_of(data::_whitespace_chars); 133 string::size_type lsc = identity.find_last_not_of(data::_whitespace_chars);
134 assert(lsc!=string::npos); 134 assert(lsc!=string::npos);
135 if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1)) 135 if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1))
136 fsc += sizeof("xri://")-1; 136 fsc += sizeof("xri://")-1;
137 if((fsc+1)>=lsc) 137 if((fsc+1)>=lsc)
138 throw bad_input(OPKELE_CP_ "not a character of importance in identity"); 138 throw bad_input(OPKELE_CP_ "not a character of importance in identity");
139 string id(identity,fsc,lsc-fsc+1); 139 string id(identity,fsc,lsc-fsc+1);
140 idis.clear(); 140 idis.clear();
141 if(strchr(data::_iname_leaders,id[0])) { 141 if(strchr(data::_iname_leaders,id[0])) {
142 /* TODO: further normalize xri identity? Like folding case 142 /* TODO: further normalize xri identity? Like folding case
143 * or whatever... */ 143 * or whatever... */
144 rv = id; 144 rv = id;
145 set<string> cids; 145 set<string> cids;
146 for(const struct service_type_t *st=op_service_types; 146 for(const struct service_type_t *st=op_service_types;
147 st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st) { 147 st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st) {
148 idis.clear(); 148 idis.clear();
149 discover_at( idis, 149 discover_at( idis,
150 xri_proxy + util::url_encode(id)+ 150 xri_proxy + util::url_encode(id)+
151 "?_xrd_t="+util::url_encode(st->uri)+ 151 "?_xrd_t="+util::url_encode(st->uri)+
152 "&_xrd_r=application/xrd%2Bxml" 152 "&_xrd_r=application/xrd%2Bxml"
153 ";sep=true;refs=true", 153 ";sep=true;refs=true",
154 xmode_xrd ); 154 xmode_xrd );
155 if(status_code==241) continue; 155 if(status_code==241) continue;
156 if(status_code!=100) 156 if(status_code!=100)
157 throw failed_xri_resolution(OPKELE_CP_ 157 throw failed_xri_resolution(OPKELE_CP_
158 "XRI resolution failed with '"+status_string+"' message" 158 "XRI resolution failed with '"+status_string+"' message"
159 ", while looking for SEP with type '"+st->uri+"'", status_code); 159 ", while looking for SEP with type '"+st->uri+"'", status_code);
160 if(idis.xrd.canonical_ids.empty()) 160 if(idis.xrd.canonical_ids.empty())
161 throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID for XRI identity found"); 161 throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID for XRI identity found");
162 string cid = idis.xrd.canonical_ids.begin()->second; 162 string cid = idis.xrd.canonical_ids.begin()->second;
163 if(cids.find(cid)==cids.end()) { 163 if(cids.find(cid)==cids.end()) {
164 cids.insert(cid); 164 cids.insert(cid);
165 idis.clear(); 165 idis.clear();
166 discover_at( idis, 166 discover_at( idis,
167 xri_proxy + util::url_encode(id)+ 167 xri_proxy + util::url_encode(id)+
168 "?_xrd_t="+util::url_encode(st->uri)+ 168 "?_xrd_t="+util::url_encode(st->uri)+
169 "&_xrd_r=application/xrd%2Bxml" 169 "&_xrd_r=application/xrd%2Bxml"
170 ";sep=true;refs=true", 170 ";sep=true;refs=true",
171 xmode_xrd ); 171 xmode_xrd );
172 if(status_code==241) continue; 172 if(status_code==241) continue;
173 if(status_code!=100) 173 if(status_code!=100)
174 throw failed_xri_resolution(OPKELE_CP_ 174 throw failed_xri_resolution(OPKELE_CP_
175 "XRI resolution failed with '"+status_string+"' message" 175 "XRI resolution failed with '"+status_string+"' message"
176 ", while looking for SEP with type '"+st->uri+"'" 176 ", while looking for SEP with type '"+st->uri+"'"
177 " on canonical id", status_code); 177 " on canonical id", status_code);
178 } 178 }
179 idis.canonicalized_id = cid; 179 idis.canonicalized_id = cid;
180 idis.normalized_id = rv; idis.xri_identity = true; 180 idis.normalized_id = rv; idis.xri_identity = true;
181 queue_endpoints(oi,idis,st); 181 queue_endpoints(oi,idis,st);
182 } 182 }
183 }else{ 183 }else{
184 idis.xri_identity = false; 184 idis.xri_identity = false;
185 if(id.find("://")==string::npos) 185 if(id.find("://")==string::npos)
186 id.insert(0,"http://"); 186 id.insert(0,"http://");
187 string::size_type fp = id.find('#'); 187 string::size_type fp = id.find('#');
188 if(fp!=string::npos) { 188 if(fp!=string::npos) {
189 string::size_type qp = id.find('?'); 189 string::size_type qp = id.find('?');
190 if(qp==string::npos || qp<fp) 190 if(qp==string::npos || qp<fp)
191 id.erase(fp); 191 id.erase(fp);
192 else if(qp>fp) 192 else if(qp>fp)
193 id.erase(fp,qp-fp); 193 id.erase(fp,qp-fp);
194 } 194 }
195 rv = idis.normalized_id = util::rfc_3986_normalize_uri(id); 195 rv = idis.normalized_id = util::rfc_3986_normalize_uri(id);
196 discover_at(idis,id,xmode_html|xmode_xrd); 196 discover_at(idis,id,xmode_html|xmode_xrd);
197 const char * eu = 0; 197 const char * eu = 0;
198 CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); 198 CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu);
199 if(r) 199 if(r)
200 throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); 200 throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r);
201 string cid = util::strip_uri_fragment_part( idis.canonicalized_id = util::rfc_3986_normalize_uri(eu) ); 201 string cid = util::strip_uri_fragment_part( idis.canonicalized_id = util::rfc_3986_normalize_uri(eu) );
202 if(xrds_location.empty()) { 202 if(xrds_location.empty()) {
203 if(idis.xrd.empty()) 203 if(idis.xrd.empty())
204 html2xrd(oi,idis); 204 html2xrd(oi,idis);
205 else{ 205 else{
206 for(const service_type_t *st=op_service_types; 206 for(const service_type_t *st=op_service_types;
207 st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st) 207 st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st)
208 queue_endpoints(oi,idis,st); 208 queue_endpoints(oi,idis,st);
209 } 209 }
210 }else{ 210 }else{
211 idis.clear(); 211 idis.clear();
212 idis.canonicalized_id = cid; 212 idis.canonicalized_id = cid;
213 discover_at(idis,xrds_location,xmode_xrd); 213 discover_at(idis,xrds_location,xmode_xrd);
214 if(idis.xrd.empty()) 214 if(idis.xrd.empty())
215 html2xrd(oi,idis); 215 html2xrd(oi,idis);
216 else{ 216 else{
217 for(const service_type_t *st=op_service_types; 217 for(const service_type_t *st=op_service_types;
218 st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st) 218 st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st)
219 queue_endpoints(oi,idis,st); 219 queue_endpoints(oi,idis,st);
220 } 220 }
221 } 221 }
222 } 222 }
223 return rv; 223 return rv;
224 } 224 }
225 225
226 void discover_at(idiscovery_t& idis,const string& url,int xm) { 226 void discover_at(idiscovery_t& idis,const string& url,int xm) {
227 CURLcode r = easy_setopt(CURLOPT_MAXREDIRS, (xm&xmode_noredirs)?0:5); 227 CURLcode r = easy_setopt(CURLOPT_MAXREDIRS, (xm&xmode_noredirs)?0:5);
228 if(r) 228 if(r)
229 throw exception_curl(OPKELE_CP_ "failed to set curly maxredirs option"); 229 throw exception_curl(OPKELE_CP_ "failed to set curly maxredirs option");
230 if( (r=easy_setopt(CURLOPT_URL,url.c_str())) ) 230 if( (r=easy_setopt(CURLOPT_URL,url.c_str())) )
231 throw exception_curl(OPKELE_CP_ "failed to set curly urlie",r); 231 throw exception_curl(OPKELE_CP_ "failed to set curly urlie",r);
232 232
233 http_content_type.clear(); 233 http_content_type.clear();
234 xmode = xm; 234 xmode = xm;
235 prepare_to_parse(); 235 prepare_to_parse();
236 if(xmode&xmode_html) { 236 if(xmode&xmode_html) {
237 xrds_location.clear(); 237 xrds_location.clear();
238 save_html.clear(); 238 save_html.clear();
239 save_html.reserve(max_html); 239 save_html.reserve(max_html);
240 } 240 }
241 xrd = &idis.xrd; 241 xrd = &idis.xrd;
242 242
243 r = easy_perform(); 243 r = easy_perform();
244 if(r && r!=CURLE_WRITE_ERROR) 244 if(r && r!=CURLE_WRITE_ERROR)
245 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 245 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
246 246
247 if(!parser_choked) { 247 if(!parser_choked) {
248 parse(0,0,true); 248 parse(0,0,true);
249 }else if(xmode&xmode_html){ 249 }else if(xmode&xmode_html){
250 /* TODO: do not bother if we've seen xml */ 250 /* TODO: do not bother if we've seen xml */
251 try { 251 try {
252 util::tidy_doc_t td = util::tidy_doc_t::create(); 252 util::tidy_doc_t td = util::tidy_doc_t::create();
253 if(!td) 253 if(!td)
254 throw exception_tidy(OPKELE_CP_ "failed to create htmltidy document"); 254 throw exception_tidy(OPKELE_CP_ "failed to create htmltidy document");
255#ifndef NDEBUG 255#ifndef NDEBUG
256 td.opt_set(TidyQuiet,false); 256 td.opt_set(TidyQuiet,false);
257 td.opt_set(TidyShowWarnings,false); 257 td.opt_set(TidyShowWarnings,false);
258#endif /* NDEBUG */ 258#endif /* NDEBUG */
259 td.opt_set(TidyForceOutput,true); 259 td.opt_set(TidyForceOutput,true);
260 td.opt_set(TidyXhtmlOut,true); 260 td.opt_set(TidyXhtmlOut,true);
261 td.opt_set(TidyDoctypeMode,TidyDoctypeOmit); 261 td.opt_set(TidyDoctypeMode,TidyDoctypeOmit);
262 td.opt_set(TidyMark,false); 262 td.opt_set(TidyMark,false);
263 td.opt_set(TidyNumEntities,true);
263 if(td.parse_string(save_html)<=0) 264 if(td.parse_string(save_html)<=0)
264 throw exception_tidy(OPKELE_CP_ "tidy failed to parse document"); 265 throw exception_tidy(OPKELE_CP_ "tidy failed to parse document");
265 if(td.clean_and_repair()<=0) 266 if(td.clean_and_repair()<=0)
266 throw exception_tidy(OPKELE_CP_ "tidy failed to clean and repair"); 267 throw exception_tidy(OPKELE_CP_ "tidy failed to clean and repair");
267 util::tidy_buf_t tide; 268 util::tidy_buf_t tide;
268 if(td.save_buffer(tide)<=0) 269 if(td.save_buffer(tide)<=0)
269 throw exception_tidy(OPKELE_CP_ "tidy failed to save buffer"); 270 throw exception_tidy(OPKELE_CP_ "tidy failed to save buffer");
270 prepare_to_parse(); 271 prepare_to_parse();
271 parse(tide.c_str(),tide.size(),true); 272 parse(tide.c_str(),tide.size(),true);
272 }catch(exception_tidy& et) { } 273 }catch(exception_tidy& et) { }
273 } 274 }
274 save_html.clear(); 275 save_html.clear();
275 } 276 }
276 277
277 void prepare_to_parse() { 278 void prepare_to_parse() {
278 (*(expat_t*)this) = parser_create_ns(); 279 (*(expat_t*)this) = parser_create_ns();
279 set_user_data(); set_element_handler(); 280 set_user_data(); set_element_handler();
280 set_character_data_handler(); 281 set_character_data_handler();
281 282
282 if(xmode&xmode_html) { 283 if(xmode&xmode_html) {
283 html_openid1.clear(); html_openid2.clear(); 284 html_openid1.clear(); html_openid2.clear();
284 parser_choked = false; 285 parser_choked = false;
285 } 286 }
286 287
287 cdata = 0; xrd_service = 0; skipping = 0; 288 cdata = 0; xrd_service = 0; skipping = 0;
288 pt_stack.clear(); 289 pt_stack.clear();
289 status_code = 100; status_string.clear(); 290 status_code = 100; status_string.clear();
290 } 291 }
291 292
292 void html2xrd(endpoint_discovery_iterator& oi,idiscovery_t& id) { 293 void html2xrd(endpoint_discovery_iterator& oi,idiscovery_t& id) {
293 XRD_t& x = id.xrd; 294 XRD_t& x = id.xrd;
294 if(!html_openid2.uris.empty()) { 295 if(!html_openid2.uris.empty()) {
295 html_openid2.types.insert(STURI_OPENID20); 296 html_openid2.types.insert(STURI_OPENID20);
296 x.services.add(-1,html_openid2); 297 x.services.add(-1,html_openid2);
297 queue_endpoints(oi,id,&op_service_types[st_index_2]); 298 queue_endpoints(oi,id,&op_service_types[st_index_2]);
298 } 299 }
299 if(!html_openid1.uris.empty()) { 300 if(!html_openid1.uris.empty()) {
300 html_openid1.types.insert(STURI_OPENID11); 301 html_openid1.types.insert(STURI_OPENID11);
301 x.services.add(-1,html_openid1); 302 x.services.add(-1,html_openid1);
302 queue_endpoints(oi,id,&op_service_types[st_index_1]); 303 queue_endpoints(oi,id,&op_service_types[st_index_1]);
303 } 304 }
304 } 305 }
305 306
306 size_t write(void *p,size_t s,size_t nm) { 307 size_t write(void *p,size_t s,size_t nm) {
307 /* TODO: limit total size */ 308 /* TODO: limit total size */
308 size_t bytes = s*nm; 309 size_t bytes = s*nm;
309 const char *inbuf = (const char*)p; 310 const char *inbuf = (const char*)p;
310 if(xmode&xmode_html) { 311 if(xmode&xmode_html) {
311 size_t mbts = save_html.capacity()-save_html.size(); 312 size_t mbts = save_html.capacity()-save_html.size();
312 size_t bts = 0; 313 size_t bts = 0;
313 if(mbts>0) { 314 if(mbts>0) {
314 bts = (bytes>mbts)?mbts:bytes; 315 bts = (bytes>mbts)?mbts:bytes;
315 save_html.append(inbuf,bts); 316 save_html.append(inbuf,bts);
316 } 317 }
317 if(skipping<0) return bts; 318 if(skipping<0) return bts;
318 } 319 }
319 if(skipping<0) return 0; 320 if(skipping<0) return 0;
320 bool rp = parse(inbuf,bytes,false); 321 bool rp = parse(inbuf,bytes,false);
321 if(!rp) { 322 if(!rp) {
322 parser_choked = true; 323 parser_choked = true;
323 skipping = -1; 324 skipping = -1;
324 if(!(xmode&xmode_html)) 325 if(!(xmode&xmode_html))
325 bytes = 0; 326 bytes = 0;
326 } 327 }
327 return bytes; 328 return bytes;
328 } 329 }
329 size_t header(void *p,size_t s,size_t nm) { 330 size_t header(void *p,size_t s,size_t nm) {
330 size_t bytes = s*nm; 331 size_t bytes = s*nm;
331 const char *h = (const char*)p; 332 const char *h = (const char*)p;
332 const char *colon = (const char*)memchr(p,':',bytes); 333 const char *colon = (const char*)memchr(p,':',bytes);
333 const char *space = (const char*)memchr(p,' ',bytes); 334 const char *space = (const char*)memchr(p,' ',bytes);
334 if(space && ( (!colon) || space<colon ) ) { 335 if(space && ( (!colon) || space<colon ) ) {
335 xrds_location.clear(); http_content_type.clear(); 336 xrds_location.clear(); http_content_type.clear();
336 }else if(colon) { 337 }else if(colon) {
337 const char *hv = ++colon; 338 const char *hv = ++colon;
338 size_t hnl = colon-h; 339 size_t hnl = colon-h;
339 int rb; 340 int rb;
340 for(rb = bytes-hnl-1;rb>0 && isspace(*hv);++hv,--rb); 341 for(rb = bytes-hnl-1;rb>0 && isspace(*hv);++hv,--rb);
341 while(rb>0 && isspace(hv[rb-1])) --rb; 342 while(rb>0 && isspace(hv[rb-1])) --rb;
342 if(rb) { 343 if(rb) {
343 if( (hnl>=sizeof(XRDS_HEADER)) 344 if( (hnl>=sizeof(XRDS_HEADER))
344 && !strncasecmp(h,XRDS_HEADER":", 345 && !strncasecmp(h,XRDS_HEADER":",
345 sizeof(XRDS_HEADER)) ) { 346 sizeof(XRDS_HEADER)) ) {
346 xrds_location.assign(hv,rb); 347 xrds_location.assign(hv,rb);
347 }else if( (hnl>=sizeof(CT_HEADER)) 348 }else if( (hnl>=sizeof(CT_HEADER))
348 && !strncasecmp(h,CT_HEADER":", 349 && !strncasecmp(h,CT_HEADER":",
349 sizeof(CT_HEADER)) ) { 350 sizeof(CT_HEADER)) ) {
350 const char *sc = (const char*)memchr( 351 const char *sc = (const char*)memchr(
351 hv,';',rb); 352 hv,';',rb);
352 http_content_type.assign(hv,sc?(sc-hv):rb); 353 http_content_type.assign(hv,sc?(sc-hv):rb);
353 } 354 }
354 } 355 }
355 } 356 }
356 return curl_t::header(p,s,nm); 357 return curl_t::header(p,s,nm);
357 } 358 }
358 359
359 void start_element(const XML_Char *n,const XML_Char **a) { 360 void start_element(const XML_Char *n,const XML_Char **a) {
360 if(skipping<0) return; 361 if(skipping<0) return;
361 if(skipping) { 362 if(skipping) {
362 if(xmode&xmode_html) 363 if(xmode&xmode_html)
363 html_start_element(n,a); 364 html_start_element(n,a);
364 ++skipping; return; 365 ++skipping; return;
365 } 366 }
366 if(pt_stack.empty()) { 367 if(pt_stack.empty()) {
367 if(is_qelement(n,NSURI_XRDS "\tXRDS")) 368 if(is_qelement(n,NSURI_XRDS "\tXRDS"))
368 return; 369 return;
369 if(is_qelement(n,NSURI_XRD "\tXRD")) { 370 if(is_qelement(n,NSURI_XRD "\tXRD")) {
370 assert(xrd); 371 assert(xrd);
371 xrd->clear(); 372 xrd->clear();
372 pt_stack.push_back(n); 373 pt_stack.push_back(n);
373 }else if(xmode&xmode_html) { 374 }else if(xmode&xmode_html) {
374 html_start_element(n,a); 375 html_start_element(n,a);
375 }else{ 376 }else{
376 skipping = -1; 377 skipping = -1;
377 } 378 }
378 }else{ 379 }else{
379 int pt_s = pt_stack.size(); 380 int pt_s = pt_stack.size();
380 if(pt_s==1) { 381 if(pt_s==1) {
381 if(is_qelement(n,NSURI_XRD "\tCanonicalID")) { 382 if(is_qelement(n,NSURI_XRD "\tCanonicalID")) {
382 assert(xrd); 383 assert(xrd);
383 cdata = &(xrd->canonical_ids.add(element_priority(a),string())); 384 cdata = &(xrd->canonical_ids.add(element_priority(a),string()));
384 }else if(is_qelement(n,NSURI_XRD "\tLocalID")) { 385 }else if(is_qelement(n,NSURI_XRD "\tLocalID")) {
385 assert(xrd); 386 assert(xrd);
386 cdata = &(xrd->local_ids.add(element_priority(a),string())); 387 cdata = &(xrd->local_ids.add(element_priority(a),string()));
387 }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { 388 }else if(is_qelement(n,NSURI_XRD "\tProviderID")) {
388 assert(xrd); 389 assert(xrd);
389 cdata = &(xrd->provider_id); 390 cdata = &(xrd->provider_id);
390 }else if(is_qelement(n,NSURI_XRD "\tService")) { 391 }else if(is_qelement(n,NSURI_XRD "\tService")) {
391 assert(xrd); 392 assert(xrd);
392 xrd_service = &(xrd->services.add(element_priority(a), 393 xrd_service = &(xrd->services.add(element_priority(a),
393 service_t())); 394 service_t()));
394 pt_stack.push_back(n); 395 pt_stack.push_back(n);
395 }else if(is_qelement(n,NSURI_XRD "\tStatus")) { 396 }else if(is_qelement(n,NSURI_XRD "\tStatus")) {
396 for(;*a;) { 397 for(;*a;) {
397 if(!strcasecmp(*(a++),"code")) { 398 if(!strcasecmp(*(a++),"code")) {
398 if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) { 399 if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) {
399 cdata = &status_string; 400 cdata = &status_string;
400 pt_stack.push_back(n); 401 pt_stack.push_back(n);
401 break; 402 break;
402 } 403 }
403 }else 404 }else
404 ++a; 405 ++a;
405 } 406 }
406 }else if(is_qelement(n,NSURI_XRD "\tExpires")) { 407 }else if(is_qelement(n,NSURI_XRD "\tExpires")) {
407 assert(xrd); 408 assert(xrd);
408 cdata_buf.clear(); 409 cdata_buf.clear();
409 cdata = &cdata_buf; 410 cdata = &cdata_buf;
410 }else if(xmode&xmode_html) { 411 }else if(xmode&xmode_html) {
411 html_start_element(n,a); 412 html_start_element(n,a);
412 }else{ 413 }else{
413 skipping = 1; 414 skipping = 1;
414 } 415 }
415 }else if(pt_s==2) { 416 }else if(pt_s==2) {
416 if(is_qelement(pt_stack.back().c_str(), NSURI_XRD "\tService")) { 417 if(is_qelement(pt_stack.back().c_str(), NSURI_XRD "\tService")) {
417 if(is_qelement(n,NSURI_XRD "\tType")) { 418 if(is_qelement(n,NSURI_XRD "\tType")) {
418 assert(xrd); assert(xrd_service); 419 assert(xrd); assert(xrd_service);
419 cdata_buf.clear(); 420 cdata_buf.clear();
420 cdata = &cdata_buf; 421 cdata = &cdata_buf;
421 }else if(is_qelement(n,NSURI_XRD "\tURI")) { 422 }else if(is_qelement(n,NSURI_XRD "\tURI")) {
422 assert(xrd); assert(xrd_service); 423 assert(xrd); assert(xrd_service);
423 const char *append = element_attr(a,"append"); 424 const char *append = element_attr(a,"append");
424 xrd::uri_t& uri = xrd_service->uris.add(element_priority(a),xrd::uri_t("",append?append:"")); 425 xrd::uri_t& uri = xrd_service->uris.add(element_priority(a),xrd::uri_t("",append?append:""));
425 cdata = &uri.uri; 426 cdata = &uri.uri;
426 }else if(is_qelement(n,NSURI_XRD "\tLocalID") 427 }else if(is_qelement(n,NSURI_XRD "\tLocalID")
427 || is_qelement(n,NSURI_OPENID10 "\tDelegate") ) { 428 || is_qelement(n,NSURI_OPENID10 "\tDelegate") ) {
428 assert(xrd); assert(xrd_service); 429 assert(xrd); assert(xrd_service);
429 cdata = &(xrd_service->local_ids.add(element_priority(a),string())); 430 cdata = &(xrd_service->local_ids.add(element_priority(a),string()));
430 }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { 431 }else if(is_qelement(n,NSURI_XRD "\tProviderID")) {
431 assert(xrd); assert(xrd_service); 432 assert(xrd); assert(xrd_service);
432 cdata = &(xrd_service->provider_id); 433 cdata = &(xrd_service->provider_id);
433 }else{ 434 }else{
434 skipping = 1; 435 skipping = 1;
435 } 436 }
436 }else 437 }else
437 skipping = 1; 438 skipping = 1;
438 }else if(xmode&xmode_html) { 439 }else if(xmode&xmode_html) {
439 html_start_element(n,a); 440 html_start_element(n,a);
440 }else{ 441 }else{
441 skipping = 1; 442 skipping = 1;
442 } 443 }
443 } 444 }
444 } 445 }
445 void end_element(const XML_Char *n) { 446 void end_element(const XML_Char *n) {
446 if(skipping<0) return; 447 if(skipping<0) return;
447 if(skipping) { 448 if(skipping) {
448 --skipping; return; 449 --skipping; return;
449 } 450 }
450 if(is_qelement(n,NSURI_XRD "\tType")) { 451 if(is_qelement(n,NSURI_XRD "\tType")) {
451 assert(xrd); assert(xrd_service); assert(cdata==&cdata_buf); 452 assert(xrd); assert(xrd_service); assert(cdata==&cdata_buf);
452 xrd_service->types.insert(cdata_buf); 453 xrd_service->types.insert(cdata_buf);
453 }else if(is_qelement(n,NSURI_XRD "\tService")) { 454 }else if(is_qelement(n,NSURI_XRD "\tService")) {
454 assert(xrd); assert(xrd_service); 455 assert(xrd); assert(xrd_service);
455 assert(!pt_stack.empty()); 456 assert(!pt_stack.empty());
456 assert(pt_stack.back()==(NSURI_XRD "\tService")); 457 assert(pt_stack.back()==(NSURI_XRD "\tService"));
457 pt_stack.pop_back(); 458 pt_stack.pop_back();
458 xrd_service = 0; 459 xrd_service = 0;
459 }else if(is_qelement(n,NSURI_XRD "\tStatus")) { 460 }else if(is_qelement(n,NSURI_XRD "\tStatus")) {
460 assert(xrd); 461 assert(xrd);
461 if(is_qelement(pt_stack.back().c_str(),n)) { 462 if(is_qelement(pt_stack.back().c_str(),n)) {
462 assert(cdata==&status_string); 463 assert(cdata==&status_string);
463 pt_stack.pop_back(); 464 pt_stack.pop_back();
464 if(status_code!=100) 465 if(status_code!=100)
465 skipping = -1; 466 skipping = -1;
466 } 467 }
467 }else if(is_qelement(n,NSURI_XRD "\tExpires")) { 468 }else if(is_qelement(n,NSURI_XRD "\tExpires")) {
468 assert(xrd); 469 assert(xrd);
469 xrd->expires = util::w3c_to_time(cdata_buf); 470 xrd->expires = util::w3c_to_time(cdata_buf);
470 }else if((xmode&xmode_html) && is_element(n,"head")) { 471 }else if((xmode&xmode_html) && is_element(n,"head")) {
471 skipping = -1; 472 skipping = -1;
472 } 473 }
473 cdata = 0; 474 cdata = 0;
474 } 475 }
475 void character_data(const XML_Char *s,int l) { 476 void character_data(const XML_Char *s,int l) {
476 if(skipping) return; 477 if(skipping) return;
477 if(cdata) cdata->append(s,l); 478 if(cdata) cdata->append(s,l);
478 } 479 }
479 480
480 void html_start_element(const XML_Char *n,const XML_Char **a) { 481 void html_start_element(const XML_Char *n,const XML_Char **a) {
481 if(is_element(n,"meta")) { 482 if(is_element(n,"meta")) {
482 bool heq = false; 483 bool heq = false;
483 string l; 484 string l;
484 for(;*a;a+=2) { 485 for(;*a;a+=2) {
485 if(!( strcasecmp(a[0],"http-equiv") 486 if(!( strcasecmp(a[0],"http-equiv")
486 || strcasecmp(a[1],XRDS_HEADER) )) 487 || strcasecmp(a[1],XRDS_HEADER) ))
487 heq = true; 488 heq = true;
488 else if(!strcasecmp(a[0],"content")) 489 else if(!strcasecmp(a[0],"content"))
489 l.assign(a[1]); 490 l.assign(a[1]);
490 } 491 }
491 if(heq) 492 if(heq)
492 xrds_location = l; 493 xrds_location = l;
493 }else if(is_element(n,"link")) { 494 }else if(is_element(n,"link")) {
494 string rels; 495 string rels;
495 string href; 496 string href;
496 for(;*a;a+=2) { 497 for(;*a;a+=2) {
497 if( !strcasecmp(a[0],"rel") ) { 498 if( !strcasecmp(a[0],"rel") ) {
498 rels.assign(a[1]); 499 rels.assign(a[1]);
499 }else if( !strcasecmp(a[0],"href") ) { 500 }else if( !strcasecmp(a[0],"href") ) {
500 const char *ns = a[1]; 501 const char *ns = a[1];
501 for(;*ns && isspace(*ns);++ns); 502 for(;*ns && isspace(*ns);++ns);
502 href.assign(ns); 503 href.assign(ns);
503 string::size_type lns=href.find_last_not_of(data::_whitespace_chars); 504 string::size_type lns=href.find_last_not_of(data::_whitespace_chars);
504 href.erase(lns+1); 505 href.erase(lns+1);
505 } 506 }
506 } 507 }
507 for(string::size_type ns=rels.find_first_not_of(data::_whitespace_chars); 508 for(string::size_type ns=rels.find_first_not_of(data::_whitespace_chars);
508 ns!=string::npos; ns=rels.find_first_not_of(data::_whitespace_chars,ns)) { 509 ns!=string::npos; ns=rels.find_first_not_of(data::_whitespace_chars,ns)) {
509 string::size_type s = rels.find_first_of(data::_whitespace_chars,ns); 510 string::size_type s = rels.find_first_of(data::_whitespace_chars,ns);
510 string rel; 511 string rel;
511 if(s==string::npos) { 512 if(s==string::npos) {
512 rel.assign(rels,ns,string::npos); 513 rel.assign(rels,ns,string::npos);
513 ns = string::npos; 514 ns = string::npos;
514 }else{ 515 }else{
515 rel.assign(rels,ns,s-ns); 516 rel.assign(rels,ns,s-ns);
516 ns = s; 517 ns = s;
517 } 518 }
518 if(rel=="openid.server") 519 if(rel=="openid.server")
519 html_openid1.uris.add(-1,xrd::uri_t(href)); 520 html_openid1.uris.add(-1,xrd::uri_t(href));
520 else if(rel=="openid.delegate") 521 else if(rel=="openid.delegate")
521 html_openid1.local_ids.add(-1,href); 522 html_openid1.local_ids.add(-1,href);
522 else if(rel=="openid2.provider") 523 else if(rel=="openid2.provider")
523 html_openid2.uris.add(-1,xrd::uri_t(href)); 524 html_openid2.uris.add(-1,xrd::uri_t(href));
524 else if(rel=="openid2.local_id") 525 else if(rel=="openid2.local_id")
525 html_openid2.local_ids.add(-1,href); 526 html_openid2.local_ids.add(-1,href);
526 } 527 }
527 }else if(is_element(n,"body")) { 528 }else if(is_element(n,"body")) {
528 skipping = -1; 529 skipping = -1;
529 } 530 }
530 } 531 }
531 532
532 void queue_endpoints(endpoint_discovery_iterator& oi, 533 void queue_endpoints(endpoint_discovery_iterator& oi,
533 const idiscovery_t &id, 534 const idiscovery_t &id,
534 const service_type_t *st) { 535 const service_type_t *st) {
535 openid_endpoint_t ep; 536 openid_endpoint_t ep;
536 ep.claimed_id = id.canonicalized_id; 537 ep.claimed_id = id.canonicalized_id;
537 for(xrd::services_t::const_iterator isvc=id.xrd.services.begin(); 538 for(xrd::services_t::const_iterator isvc=id.xrd.services.begin();
538 isvc!=id.xrd.services.end(); ++isvc) { 539 isvc!=id.xrd.services.end(); ++isvc) {
539 const xrd::service_t svc = isvc->second; 540 const xrd::service_t svc = isvc->second;
540 if(svc.types.find(st->uri)==svc.types.end()) continue; 541 if(svc.types.find(st->uri)==svc.types.end()) continue;
541 for(xrd::uris_t::const_iterator iu=svc.uris.begin();iu!=svc.uris.end();++iu) { 542 for(xrd::uris_t::const_iterator iu=svc.uris.begin();iu!=svc.uris.end();++iu) {
542 ep.uri = iu->second.uri; 543 ep.uri = iu->second.uri;
543 if(id.xri_identity) { 544 if(id.xri_identity) {
544 if(iu->second.append=="qxri") { 545 if(iu->second.append=="qxri") {
545 ep.uri += id.normalized_id; 546 ep.uri += id.normalized_id;
546 } /* TODO: else handle other append attribute values */ 547 } /* TODO: else handle other append attribute values */
547 } 548 }
548 if(st->forceid) { 549 if(st->forceid) {
549 ep.local_id = ep.claimed_id = st->forceid; 550 ep.local_id = ep.claimed_id = st->forceid;
550 *(oi++) = ep; 551 *(oi++) = ep;
551 }else{ 552 }else{
552 if(svc.local_ids.empty()) { 553 if(svc.local_ids.empty()) {
553 ep.local_id = ep.claimed_id; 554 ep.local_id = ep.claimed_id;
554 *(oi++) = ep; 555 *(oi++) = ep;
555 }else{ 556 }else{
556 for(xrd::local_ids_t::const_iterator ilid=svc.local_ids.begin(); 557 for(xrd::local_ids_t::const_iterator ilid=svc.local_ids.begin();
557 ilid!=svc.local_ids.end(); ++ilid) { 558 ilid!=svc.local_ids.end(); ++ilid) {
558 ep.local_id = ilid->second; 559 ep.local_id = ilid->second;
559 *(oi++) = ep; 560 *(oi++) = ep;
560 } 561 }
561 } 562 }
562 } 563 }
563 } 564 }
564 } 565 }
565 } 566 }
566 567
567 }; 568 };
568 569
569 string idiscover(endpoint_discovery_iterator oi,const string& identity) { 570 string idiscover(endpoint_discovery_iterator oi,const string& identity) {
570 idigger_t idigger; 571 idigger_t idigger;
571 return idigger.discover(oi,identity); 572 return idigger.discover(oi,identity);
572 } 573 }
573 574
574 void yadiscover(endpoint_discovery_iterator oi,const string& yurl,const char **types,bool redirs) try { 575 void yadiscover(endpoint_discovery_iterator oi,const string& yurl,const char **types,bool redirs) try {
575 idigger_t idigger; 576 idigger_t idigger;
576 idigger.yadiscover(oi,yurl,types,redirs); 577 idigger.yadiscover(oi,yurl,types,redirs);
577 }catch(exception_curl& ec) { 578 }catch(exception_curl& ec) {
578 if(redirs || ec._error!=CURLE_TOO_MANY_REDIRECTS) 579 if(redirs || ec._error!=CURLE_TOO_MANY_REDIRECTS)
579 throw; 580 throw;
580 } 581 }
581 582
582} 583}