summaryrefslogtreecommitdiffabout
path: root/lib
authorMichael Krelin <hacker@klever.net>2008-04-06 19:26:50 (UTC)
committer Michael Krelin <hacker@klever.net>2008-04-06 19:26:50 (UTC)
commitaa3a6821f6aaaf4ae63bafbbc76da5f414a75fd4 (patch) (unidiff)
treec64231565e123d678850a7b0560b348ee82b2dcc /lib
parent752e484cd2fc239bc582a88fe7d62a225880ee3b (diff)
downloadlibopkele-aa3a6821f6aaaf4ae63bafbbc76da5f414a75fd4.zip
libopkele-aa3a6821f6aaaf4ae63bafbbc76da5f414a75fd4.tar.gz
libopkele-aa3a6821f6aaaf4ae63bafbbc76da5f414a75fd4.tar.bz2
fix for discovery failing on some entities coming before the tag of interest
One of the numerous opportunities to express my gratitude by means of commit message to Joseph Smarr of Plaxo for spotting it. Thanks, Joseph! Signed-off-by: Michael Krelin <hacker@klever.net>
Diffstat (limited to 'lib') (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}