summaryrefslogtreecommitdiffabout
path: root/lib
authorMichael Krelin <hacker@klever.net>2008-02-22 12:33:42 (UTC)
committer Michael Krelin <hacker@klever.net>2008-02-22 12:33:42 (UTC)
commit4c9564dce14fdc46ef6739426ee1cad87ed58ad1 (patch) (unidiff)
tree6f1f520d56b5e346abb6bf9514b697c7e2240819 /lib
parentdaf2d4bcb4a31df6b46d3da7a33ee3f98d85e464 (diff)
downloadlibopkele-4c9564dce14fdc46ef6739426ee1cad87ed58ad1.zip
libopkele-4c9564dce14fdc46ef6739426ee1cad87ed58ad1.tar.gz
libopkele-4c9564dce14fdc46ef6739426ee1cad87ed58ad1.tar.bz2
fix the discovery for the case when identity URL points to XRDS document.
Signed-off-by: Michael Krelin <hacker@klever.net>
Diffstat (limited to 'lib') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/discovery.cc8
1 files changed, 7 insertions, 1 deletions
diff --git a/lib/discovery.cc b/lib/discovery.cc
index 5913ad4..c118c80 100644
--- a/lib/discovery.cc
+++ b/lib/discovery.cc
@@ -1,576 +1,582 @@
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 html2xrd(oi,idis); 203 if(idis.xrd.empty())
204 html2xrd(oi,idis);
205 else{
206 for(const service_type_t *st=op_service_types;
207 st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st)
208 queue_endpoints(oi,idis,st);
209 }
204 }else{ 210 }else{
205 idis.clear(); 211 idis.clear();
206 idis.canonicalized_id = cid; 212 idis.canonicalized_id = cid;
207 discover_at(idis,xrds_location,xmode_xrd); 213 discover_at(idis,xrds_location,xmode_xrd);
208 if(idis.xrd.empty()) 214 if(idis.xrd.empty())
209 html2xrd(oi,idis); 215 html2xrd(oi,idis);
210 else{ 216 else{
211 for(const service_type_t *st=op_service_types; 217 for(const service_type_t *st=op_service_types;
212 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)
213 queue_endpoints(oi,idis,st); 219 queue_endpoints(oi,idis,st);
214 } 220 }
215 } 221 }
216 } 222 }
217 return rv; 223 return rv;
218 } 224 }
219 225
220 void discover_at(idiscovery_t& idis,const string& url,int xm) { 226 void discover_at(idiscovery_t& idis,const string& url,int xm) {
221 CURLcode r = easy_setopt(CURLOPT_MAXREDIRS, (xm&xmode_noredirs)?0:5); 227 CURLcode r = easy_setopt(CURLOPT_MAXREDIRS, (xm&xmode_noredirs)?0:5);
222 if(r) 228 if(r)
223 throw exception_curl(OPKELE_CP_ "failed to set curly maxredirs option"); 229 throw exception_curl(OPKELE_CP_ "failed to set curly maxredirs option");
224 if( (r=easy_setopt(CURLOPT_URL,url.c_str())) ) 230 if( (r=easy_setopt(CURLOPT_URL,url.c_str())) )
225 throw exception_curl(OPKELE_CP_ "failed to set curly urlie",r); 231 throw exception_curl(OPKELE_CP_ "failed to set curly urlie",r);
226 232
227 http_content_type.clear(); 233 http_content_type.clear();
228 xmode = xm; 234 xmode = xm;
229 prepare_to_parse(); 235 prepare_to_parse();
230 if(xmode&xmode_html) { 236 if(xmode&xmode_html) {
231 xrds_location.clear(); 237 xrds_location.clear();
232 save_html.clear(); 238 save_html.clear();
233 save_html.reserve(max_html); 239 save_html.reserve(max_html);
234 } 240 }
235 xrd = &idis.xrd; 241 xrd = &idis.xrd;
236 242
237 r = easy_perform(); 243 r = easy_perform();
238 if(r && r!=CURLE_WRITE_ERROR) 244 if(r && r!=CURLE_WRITE_ERROR)
239 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 245 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
240 246
241 if(!parser_choked) { 247 if(!parser_choked) {
242 parse(0,0,true); 248 parse(0,0,true);
243 }else if(xmode&xmode_html){ 249 }else if(xmode&xmode_html){
244 /* TODO: do not bother if we've seen xml */ 250 /* TODO: do not bother if we've seen xml */
245 try { 251 try {
246 util::tidy_doc_t td = util::tidy_doc_t::create(); 252 util::tidy_doc_t td = util::tidy_doc_t::create();
247 if(!td) 253 if(!td)
248 throw exception_tidy(OPKELE_CP_ "failed to create htmltidy document"); 254 throw exception_tidy(OPKELE_CP_ "failed to create htmltidy document");
249#ifndef NDEBUG 255#ifndef NDEBUG
250 td.opt_set(TidyQuiet,false); 256 td.opt_set(TidyQuiet,false);
251 td.opt_set(TidyShowWarnings,false); 257 td.opt_set(TidyShowWarnings,false);
252#endif /* NDEBUG */ 258#endif /* NDEBUG */
253 td.opt_set(TidyForceOutput,true); 259 td.opt_set(TidyForceOutput,true);
254 td.opt_set(TidyXhtmlOut,true); 260 td.opt_set(TidyXhtmlOut,true);
255 td.opt_set(TidyDoctypeMode,TidyDoctypeOmit); 261 td.opt_set(TidyDoctypeMode,TidyDoctypeOmit);
256 td.opt_set(TidyMark,false); 262 td.opt_set(TidyMark,false);
257 if(td.parse_string(save_html)<=0) 263 if(td.parse_string(save_html)<=0)
258 throw exception_tidy(OPKELE_CP_ "tidy failed to parse document"); 264 throw exception_tidy(OPKELE_CP_ "tidy failed to parse document");
259 if(td.clean_and_repair()<=0) 265 if(td.clean_and_repair()<=0)
260 throw exception_tidy(OPKELE_CP_ "tidy failed to clean and repair"); 266 throw exception_tidy(OPKELE_CP_ "tidy failed to clean and repair");
261 util::tidy_buf_t tide; 267 util::tidy_buf_t tide;
262 if(td.save_buffer(tide)<=0) 268 if(td.save_buffer(tide)<=0)
263 throw exception_tidy(OPKELE_CP_ "tidy failed to save buffer"); 269 throw exception_tidy(OPKELE_CP_ "tidy failed to save buffer");
264 prepare_to_parse(); 270 prepare_to_parse();
265 parse(tide.c_str(),tide.size(),true); 271 parse(tide.c_str(),tide.size(),true);
266 }catch(exception_tidy& et) { } 272 }catch(exception_tidy& et) { }
267 } 273 }
268 save_html.clear(); 274 save_html.clear();
269 } 275 }
270 276
271 void prepare_to_parse() { 277 void prepare_to_parse() {
272 (*(expat_t*)this) = parser_create_ns(); 278 (*(expat_t*)this) = parser_create_ns();
273 set_user_data(); set_element_handler(); 279 set_user_data(); set_element_handler();
274 set_character_data_handler(); 280 set_character_data_handler();
275 281
276 if(xmode&xmode_html) { 282 if(xmode&xmode_html) {
277 html_openid1.clear(); html_openid2.clear(); 283 html_openid1.clear(); html_openid2.clear();
278 parser_choked = false; 284 parser_choked = false;
279 } 285 }
280 286
281 cdata = 0; xrd_service = 0; skipping = 0; 287 cdata = 0; xrd_service = 0; skipping = 0;
282 pt_stack.clear(); 288 pt_stack.clear();
283 status_code = 100; status_string.clear(); 289 status_code = 100; status_string.clear();
284 } 290 }
285 291
286 void html2xrd(endpoint_discovery_iterator& oi,idiscovery_t& id) { 292 void html2xrd(endpoint_discovery_iterator& oi,idiscovery_t& id) {
287 XRD_t& x = id.xrd; 293 XRD_t& x = id.xrd;
288 if(!html_openid2.uris.empty()) { 294 if(!html_openid2.uris.empty()) {
289 html_openid2.types.insert(STURI_OPENID20); 295 html_openid2.types.insert(STURI_OPENID20);
290 x.services.add(-1,html_openid2); 296 x.services.add(-1,html_openid2);
291 queue_endpoints(oi,id,&op_service_types[st_index_2]); 297 queue_endpoints(oi,id,&op_service_types[st_index_2]);
292 } 298 }
293 if(!html_openid1.uris.empty()) { 299 if(!html_openid1.uris.empty()) {
294 html_openid1.types.insert(STURI_OPENID11); 300 html_openid1.types.insert(STURI_OPENID11);
295 x.services.add(-1,html_openid1); 301 x.services.add(-1,html_openid1);
296 queue_endpoints(oi,id,&op_service_types[st_index_1]); 302 queue_endpoints(oi,id,&op_service_types[st_index_1]);
297 } 303 }
298 } 304 }
299 305
300 size_t write(void *p,size_t s,size_t nm) { 306 size_t write(void *p,size_t s,size_t nm) {
301 /* TODO: limit total size */ 307 /* TODO: limit total size */
302 size_t bytes = s*nm; 308 size_t bytes = s*nm;
303 const char *inbuf = (const char*)p; 309 const char *inbuf = (const char*)p;
304 if(xmode&xmode_html) { 310 if(xmode&xmode_html) {
305 size_t mbts = save_html.capacity()-save_html.size(); 311 size_t mbts = save_html.capacity()-save_html.size();
306 size_t bts = 0; 312 size_t bts = 0;
307 if(mbts>0) { 313 if(mbts>0) {
308 bts = (bytes>mbts)?mbts:bytes; 314 bts = (bytes>mbts)?mbts:bytes;
309 save_html.append(inbuf,bts); 315 save_html.append(inbuf,bts);
310 } 316 }
311 if(skipping<0) return bts; 317 if(skipping<0) return bts;
312 } 318 }
313 if(skipping<0) return 0; 319 if(skipping<0) return 0;
314 bool rp = parse(inbuf,bytes,false); 320 bool rp = parse(inbuf,bytes,false);
315 if(!rp) { 321 if(!rp) {
316 parser_choked = true; 322 parser_choked = true;
317 skipping = -1; 323 skipping = -1;
318 if(!(xmode&xmode_html)) 324 if(!(xmode&xmode_html))
319 bytes = 0; 325 bytes = 0;
320 } 326 }
321 return bytes; 327 return bytes;
322 } 328 }
323 size_t header(void *p,size_t s,size_t nm) { 329 size_t header(void *p,size_t s,size_t nm) {
324 size_t bytes = s*nm; 330 size_t bytes = s*nm;
325 const char *h = (const char*)p; 331 const char *h = (const char*)p;
326 const char *colon = (const char*)memchr(p,':',bytes); 332 const char *colon = (const char*)memchr(p,':',bytes);
327 const char *space = (const char*)memchr(p,' ',bytes); 333 const char *space = (const char*)memchr(p,' ',bytes);
328 if(space && ( (!colon) || space<colon ) ) { 334 if(space && ( (!colon) || space<colon ) ) {
329 xrds_location.clear(); http_content_type.clear(); 335 xrds_location.clear(); http_content_type.clear();
330 }else if(colon) { 336 }else if(colon) {
331 const char *hv = ++colon; 337 const char *hv = ++colon;
332 size_t hnl = colon-h; 338 size_t hnl = colon-h;
333 int rb; 339 int rb;
334 for(rb = bytes-hnl-1;rb>0 && isspace(*hv);++hv,--rb); 340 for(rb = bytes-hnl-1;rb>0 && isspace(*hv);++hv,--rb);
335 while(rb>0 && isspace(hv[rb-1])) --rb; 341 while(rb>0 && isspace(hv[rb-1])) --rb;
336 if(rb) { 342 if(rb) {
337 if( (hnl>=sizeof(XRDS_HEADER)) 343 if( (hnl>=sizeof(XRDS_HEADER))
338 && !strncasecmp(h,XRDS_HEADER":", 344 && !strncasecmp(h,XRDS_HEADER":",
339 sizeof(XRDS_HEADER)) ) { 345 sizeof(XRDS_HEADER)) ) {
340 xrds_location.assign(hv,rb); 346 xrds_location.assign(hv,rb);
341 }else if( (hnl>=sizeof(CT_HEADER)) 347 }else if( (hnl>=sizeof(CT_HEADER))
342 && !strncasecmp(h,CT_HEADER":", 348 && !strncasecmp(h,CT_HEADER":",
343 sizeof(CT_HEADER)) ) { 349 sizeof(CT_HEADER)) ) {
344 const char *sc = (const char*)memchr( 350 const char *sc = (const char*)memchr(
345 hv,';',rb); 351 hv,';',rb);
346 http_content_type.assign(hv,sc?(sc-hv):rb); 352 http_content_type.assign(hv,sc?(sc-hv):rb);
347 } 353 }
348 } 354 }
349 } 355 }
350 return curl_t::header(p,s,nm); 356 return curl_t::header(p,s,nm);
351 } 357 }
352 358
353 void start_element(const XML_Char *n,const XML_Char **a) { 359 void start_element(const XML_Char *n,const XML_Char **a) {
354 if(skipping<0) return; 360 if(skipping<0) return;
355 if(skipping) { 361 if(skipping) {
356 if(xmode&xmode_html) 362 if(xmode&xmode_html)
357 html_start_element(n,a); 363 html_start_element(n,a);
358 ++skipping; return; 364 ++skipping; return;
359 } 365 }
360 if(pt_stack.empty()) { 366 if(pt_stack.empty()) {
361 if(is_qelement(n,NSURI_XRDS "\tXRDS")) 367 if(is_qelement(n,NSURI_XRDS "\tXRDS"))
362 return; 368 return;
363 if(is_qelement(n,NSURI_XRD "\tXRD")) { 369 if(is_qelement(n,NSURI_XRD "\tXRD")) {
364 assert(xrd); 370 assert(xrd);
365 xrd->clear(); 371 xrd->clear();
366 pt_stack.push_back(n); 372 pt_stack.push_back(n);
367 }else if(xmode&xmode_html) { 373 }else if(xmode&xmode_html) {
368 html_start_element(n,a); 374 html_start_element(n,a);
369 }else{ 375 }else{
370 skipping = -1; 376 skipping = -1;
371 } 377 }
372 }else{ 378 }else{
373 int pt_s = pt_stack.size(); 379 int pt_s = pt_stack.size();
374 if(pt_s==1) { 380 if(pt_s==1) {
375 if(is_qelement(n,NSURI_XRD "\tCanonicalID")) { 381 if(is_qelement(n,NSURI_XRD "\tCanonicalID")) {
376 assert(xrd); 382 assert(xrd);
377 cdata = &(xrd->canonical_ids.add(element_priority(a),string())); 383 cdata = &(xrd->canonical_ids.add(element_priority(a),string()));
378 }else if(is_qelement(n,NSURI_XRD "\tLocalID")) { 384 }else if(is_qelement(n,NSURI_XRD "\tLocalID")) {
379 assert(xrd); 385 assert(xrd);
380 cdata = &(xrd->local_ids.add(element_priority(a),string())); 386 cdata = &(xrd->local_ids.add(element_priority(a),string()));
381 }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { 387 }else if(is_qelement(n,NSURI_XRD "\tProviderID")) {
382 assert(xrd); 388 assert(xrd);
383 cdata = &(xrd->provider_id); 389 cdata = &(xrd->provider_id);
384 }else if(is_qelement(n,NSURI_XRD "\tService")) { 390 }else if(is_qelement(n,NSURI_XRD "\tService")) {
385 assert(xrd); 391 assert(xrd);
386 xrd_service = &(xrd->services.add(element_priority(a), 392 xrd_service = &(xrd->services.add(element_priority(a),
387 service_t())); 393 service_t()));
388 pt_stack.push_back(n); 394 pt_stack.push_back(n);
389 }else if(is_qelement(n,NSURI_XRD "\tStatus")) { 395 }else if(is_qelement(n,NSURI_XRD "\tStatus")) {
390 for(;*a;) { 396 for(;*a;) {
391 if(!strcasecmp(*(a++),"code")) { 397 if(!strcasecmp(*(a++),"code")) {
392 if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) { 398 if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) {
393 cdata = &status_string; 399 cdata = &status_string;
394 pt_stack.push_back(n); 400 pt_stack.push_back(n);
395 break; 401 break;
396 } 402 }
397 }else 403 }else
398 ++a; 404 ++a;
399 } 405 }
400 }else if(is_qelement(n,NSURI_XRD "\tExpires")) { 406 }else if(is_qelement(n,NSURI_XRD "\tExpires")) {
401 assert(xrd); 407 assert(xrd);
402 cdata_buf.clear(); 408 cdata_buf.clear();
403 cdata = &cdata_buf; 409 cdata = &cdata_buf;
404 }else if(xmode&xmode_html) { 410 }else if(xmode&xmode_html) {
405 html_start_element(n,a); 411 html_start_element(n,a);
406 }else{ 412 }else{
407 skipping = 1; 413 skipping = 1;
408 } 414 }
409 }else if(pt_s==2) { 415 }else if(pt_s==2) {
410 if(is_qelement(pt_stack.back().c_str(), NSURI_XRD "\tService")) { 416 if(is_qelement(pt_stack.back().c_str(), NSURI_XRD "\tService")) {
411 if(is_qelement(n,NSURI_XRD "\tType")) { 417 if(is_qelement(n,NSURI_XRD "\tType")) {
412 assert(xrd); assert(xrd_service); 418 assert(xrd); assert(xrd_service);
413 cdata_buf.clear(); 419 cdata_buf.clear();
414 cdata = &cdata_buf; 420 cdata = &cdata_buf;
415 }else if(is_qelement(n,NSURI_XRD "\tURI")) { 421 }else if(is_qelement(n,NSURI_XRD "\tURI")) {
416 assert(xrd); assert(xrd_service); 422 assert(xrd); assert(xrd_service);
417 const char *append = element_attr(a,"append"); 423 const char *append = element_attr(a,"append");
418 xrd::uri_t& uri = xrd_service->uris.add(element_priority(a),xrd::uri_t("",append?append:"")); 424 xrd::uri_t& uri = xrd_service->uris.add(element_priority(a),xrd::uri_t("",append?append:""));
419 cdata = &uri.uri; 425 cdata = &uri.uri;
420 }else if(is_qelement(n,NSURI_XRD "\tLocalID") 426 }else if(is_qelement(n,NSURI_XRD "\tLocalID")
421 || is_qelement(n,NSURI_OPENID10 "\tDelegate") ) { 427 || is_qelement(n,NSURI_OPENID10 "\tDelegate") ) {
422 assert(xrd); assert(xrd_service); 428 assert(xrd); assert(xrd_service);
423 cdata = &(xrd_service->local_ids.add(element_priority(a),string())); 429 cdata = &(xrd_service->local_ids.add(element_priority(a),string()));
424 }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { 430 }else if(is_qelement(n,NSURI_XRD "\tProviderID")) {
425 assert(xrd); assert(xrd_service); 431 assert(xrd); assert(xrd_service);
426 cdata = &(xrd_service->provider_id); 432 cdata = &(xrd_service->provider_id);
427 }else{ 433 }else{
428 skipping = 1; 434 skipping = 1;
429 } 435 }
430 }else 436 }else
431 skipping = 1; 437 skipping = 1;
432 }else if(xmode&xmode_html) { 438 }else if(xmode&xmode_html) {
433 html_start_element(n,a); 439 html_start_element(n,a);
434 }else{ 440 }else{
435 skipping = 1; 441 skipping = 1;
436 } 442 }
437 } 443 }
438 } 444 }
439 void end_element(const XML_Char *n) { 445 void end_element(const XML_Char *n) {
440 if(skipping<0) return; 446 if(skipping<0) return;
441 if(skipping) { 447 if(skipping) {
442 --skipping; return; 448 --skipping; return;
443 } 449 }
444 if(is_qelement(n,NSURI_XRD "\tType")) { 450 if(is_qelement(n,NSURI_XRD "\tType")) {
445 assert(xrd); assert(xrd_service); assert(cdata==&cdata_buf); 451 assert(xrd); assert(xrd_service); assert(cdata==&cdata_buf);
446 xrd_service->types.insert(cdata_buf); 452 xrd_service->types.insert(cdata_buf);
447 }else if(is_qelement(n,NSURI_XRD "\tService")) { 453 }else if(is_qelement(n,NSURI_XRD "\tService")) {
448 assert(xrd); assert(xrd_service); 454 assert(xrd); assert(xrd_service);
449 assert(!pt_stack.empty()); 455 assert(!pt_stack.empty());
450 assert(pt_stack.back()==(NSURI_XRD "\tService")); 456 assert(pt_stack.back()==(NSURI_XRD "\tService"));
451 pt_stack.pop_back(); 457 pt_stack.pop_back();
452 xrd_service = 0; 458 xrd_service = 0;
453 }else if(is_qelement(n,NSURI_XRD "\tStatus")) { 459 }else if(is_qelement(n,NSURI_XRD "\tStatus")) {
454 assert(xrd); 460 assert(xrd);
455 if(is_qelement(pt_stack.back().c_str(),n)) { 461 if(is_qelement(pt_stack.back().c_str(),n)) {
456 assert(cdata==&status_string); 462 assert(cdata==&status_string);
457 pt_stack.pop_back(); 463 pt_stack.pop_back();
458 if(status_code!=100) 464 if(status_code!=100)
459 skipping = -1; 465 skipping = -1;
460 } 466 }
461 }else if(is_qelement(n,NSURI_XRD "\tExpires")) { 467 }else if(is_qelement(n,NSURI_XRD "\tExpires")) {
462 assert(xrd); 468 assert(xrd);
463 xrd->expires = util::w3c_to_time(cdata_buf); 469 xrd->expires = util::w3c_to_time(cdata_buf);
464 }else if((xmode&xmode_html) && is_element(n,"head")) { 470 }else if((xmode&xmode_html) && is_element(n,"head")) {
465 skipping = -1; 471 skipping = -1;
466 } 472 }
467 cdata = 0; 473 cdata = 0;
468 } 474 }
469 void character_data(const XML_Char *s,int l) { 475 void character_data(const XML_Char *s,int l) {
470 if(skipping) return; 476 if(skipping) return;
471 if(cdata) cdata->append(s,l); 477 if(cdata) cdata->append(s,l);
472 } 478 }
473 479
474 void html_start_element(const XML_Char *n,const XML_Char **a) { 480 void html_start_element(const XML_Char *n,const XML_Char **a) {
475 if(is_element(n,"meta")) { 481 if(is_element(n,"meta")) {
476 bool heq = false; 482 bool heq = false;
477 string l; 483 string l;
478 for(;*a;a+=2) { 484 for(;*a;a+=2) {
479 if(!( strcasecmp(a[0],"http-equiv") 485 if(!( strcasecmp(a[0],"http-equiv")
480 || strcasecmp(a[1],XRDS_HEADER) )) 486 || strcasecmp(a[1],XRDS_HEADER) ))
481 heq = true; 487 heq = true;
482 else if(!strcasecmp(a[0],"content")) 488 else if(!strcasecmp(a[0],"content"))
483 l.assign(a[1]); 489 l.assign(a[1]);
484 } 490 }
485 if(heq) 491 if(heq)
486 xrds_location = l; 492 xrds_location = l;
487 }else if(is_element(n,"link")) { 493 }else if(is_element(n,"link")) {
488 string rels; 494 string rels;
489 string href; 495 string href;
490 for(;*a;a+=2) { 496 for(;*a;a+=2) {
491 if( !strcasecmp(a[0],"rel") ) { 497 if( !strcasecmp(a[0],"rel") ) {
492 rels.assign(a[1]); 498 rels.assign(a[1]);
493 }else if( !strcasecmp(a[0],"href") ) { 499 }else if( !strcasecmp(a[0],"href") ) {
494 const char *ns = a[1]; 500 const char *ns = a[1];
495 for(;*ns && isspace(*ns);++ns); 501 for(;*ns && isspace(*ns);++ns);
496 href.assign(ns); 502 href.assign(ns);
497 string::size_type lns=href.find_last_not_of(data::_whitespace_chars); 503 string::size_type lns=href.find_last_not_of(data::_whitespace_chars);
498 href.erase(lns+1); 504 href.erase(lns+1);
499 } 505 }
500 } 506 }
501 for(string::size_type ns=rels.find_first_not_of(data::_whitespace_chars); 507 for(string::size_type ns=rels.find_first_not_of(data::_whitespace_chars);
502 ns!=string::npos; ns=rels.find_first_not_of(data::_whitespace_chars,ns)) { 508 ns!=string::npos; ns=rels.find_first_not_of(data::_whitespace_chars,ns)) {
503 string::size_type s = rels.find_first_of(data::_whitespace_chars,ns); 509 string::size_type s = rels.find_first_of(data::_whitespace_chars,ns);
504 string rel; 510 string rel;
505 if(s==string::npos) { 511 if(s==string::npos) {
506 rel.assign(rels,ns,string::npos); 512 rel.assign(rels,ns,string::npos);
507 ns = string::npos; 513 ns = string::npos;
508 }else{ 514 }else{
509 rel.assign(rels,ns,s-ns); 515 rel.assign(rels,ns,s-ns);
510 ns = s; 516 ns = s;
511 } 517 }
512 if(rel=="openid.server") 518 if(rel=="openid.server")
513 html_openid1.uris.add(-1,xrd::uri_t(href)); 519 html_openid1.uris.add(-1,xrd::uri_t(href));
514 else if(rel=="openid.delegate") 520 else if(rel=="openid.delegate")
515 html_openid1.local_ids.add(-1,href); 521 html_openid1.local_ids.add(-1,href);
516 else if(rel=="openid2.provider") 522 else if(rel=="openid2.provider")
517 html_openid2.uris.add(-1,xrd::uri_t(href)); 523 html_openid2.uris.add(-1,xrd::uri_t(href));
518 else if(rel=="openid2.local_id") 524 else if(rel=="openid2.local_id")
519 html_openid2.local_ids.add(-1,href); 525 html_openid2.local_ids.add(-1,href);
520 } 526 }
521 }else if(is_element(n,"body")) { 527 }else if(is_element(n,"body")) {
522 skipping = -1; 528 skipping = -1;
523 } 529 }
524 } 530 }
525 531
526 void queue_endpoints(endpoint_discovery_iterator& oi, 532 void queue_endpoints(endpoint_discovery_iterator& oi,
527 const idiscovery_t &id, 533 const idiscovery_t &id,
528 const service_type_t *st) { 534 const service_type_t *st) {
529 openid_endpoint_t ep; 535 openid_endpoint_t ep;
530 ep.claimed_id = id.canonicalized_id; 536 ep.claimed_id = id.canonicalized_id;
531 for(xrd::services_t::const_iterator isvc=id.xrd.services.begin(); 537 for(xrd::services_t::const_iterator isvc=id.xrd.services.begin();
532 isvc!=id.xrd.services.end(); ++isvc) { 538 isvc!=id.xrd.services.end(); ++isvc) {
533 const xrd::service_t svc = isvc->second; 539 const xrd::service_t svc = isvc->second;
534 if(svc.types.find(st->uri)==svc.types.end()) continue; 540 if(svc.types.find(st->uri)==svc.types.end()) continue;
535 for(xrd::uris_t::const_iterator iu=svc.uris.begin();iu!=svc.uris.end();++iu) { 541 for(xrd::uris_t::const_iterator iu=svc.uris.begin();iu!=svc.uris.end();++iu) {
536 ep.uri = iu->second.uri; 542 ep.uri = iu->second.uri;
537 if(id.xri_identity) { 543 if(id.xri_identity) {
538 if(iu->second.append=="qxri") { 544 if(iu->second.append=="qxri") {
539 ep.uri += id.normalized_id; 545 ep.uri += id.normalized_id;
540 } /* TODO: else handle other append attribute values */ 546 } /* TODO: else handle other append attribute values */
541 } 547 }
542 if(st->forceid) { 548 if(st->forceid) {
543 ep.local_id = ep.claimed_id = st->forceid; 549 ep.local_id = ep.claimed_id = st->forceid;
544 *(oi++) = ep; 550 *(oi++) = ep;
545 }else{ 551 }else{
546 if(svc.local_ids.empty()) { 552 if(svc.local_ids.empty()) {
547 ep.local_id = ep.claimed_id; 553 ep.local_id = ep.claimed_id;
548 *(oi++) = ep; 554 *(oi++) = ep;
549 }else{ 555 }else{
550 for(xrd::local_ids_t::const_iterator ilid=svc.local_ids.begin(); 556 for(xrd::local_ids_t::const_iterator ilid=svc.local_ids.begin();
551 ilid!=svc.local_ids.end(); ++ilid) { 557 ilid!=svc.local_ids.end(); ++ilid) {
552 ep.local_id = ilid->second; 558 ep.local_id = ilid->second;
553 *(oi++) = ep; 559 *(oi++) = ep;
554 } 560 }
555 } 561 }
556 } 562 }
557 } 563 }
558 } 564 }
559 } 565 }
560 566
561 }; 567 };
562 568
563 string idiscover(endpoint_discovery_iterator oi,const string& identity) { 569 string idiscover(endpoint_discovery_iterator oi,const string& identity) {
564 idigger_t idigger; 570 idigger_t idigger;
565 return idigger.discover(oi,identity); 571 return idigger.discover(oi,identity);
566 } 572 }
567 573
568 void yadiscover(endpoint_discovery_iterator oi,const string& yurl,const char **types,bool redirs) try { 574 void yadiscover(endpoint_discovery_iterator oi,const string& yurl,const char **types,bool redirs) try {
569 idigger_t idigger; 575 idigger_t idigger;
570 idigger.yadiscover(oi,yurl,types,redirs); 576 idigger.yadiscover(oi,yurl,types,redirs);
571 }catch(exception_curl& ec) { 577 }catch(exception_curl& ec) {
572 if(redirs || ec._error!=CURLE_TOO_MANY_REDIRECTS) 578 if(redirs || ec._error!=CURLE_TOO_MANY_REDIRECTS)
573 throw; 579 throw;
574 } 580 }
575 581
576} 582}