summaryrefslogtreecommitdiffabout
path: root/lib
Unidiff
Diffstat (limited to 'lib') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/consumer.cc8
1 files changed, 4 insertions, 4 deletions
diff --git a/lib/consumer.cc b/lib/consumer.cc
index 299b3bc..ff5da91 100644
--- a/lib/consumer.cc
+++ b/lib/consumer.cc
@@ -98,315 +98,315 @@ namespace opkele {
98 if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1") 98 if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1")
99 throw bad_input(OPKELE_CP_ "unsupported assoc_type"); 99 throw bad_input(OPKELE_CP_ "unsupported assoc_type");
100 string st; 100 string st;
101 if(p.has_param("session_type")) st = p.get_param("session_type"); 101 if(p.has_param("session_type")) st = p.get_param("session_type");
102 if((!st.empty()) && st!="DH-SHA1") 102 if((!st.empty()) && st!="DH-SHA1")
103 throw bad_input(OPKELE_CP_ "unsupported session_type"); 103 throw bad_input(OPKELE_CP_ "unsupported session_type");
104 secret_t secret; 104 secret_t secret;
105 if(st.empty()) { 105 if(st.empty()) {
106 secret.from_base64(p.get_param("mac_key")); 106 secret.from_base64(p.get_param("mac_key"));
107 }else{ 107 }else{
108 util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public")); 108 util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public"));
109 vector<unsigned char> ck(DH_size(dh)+1); 109 vector<unsigned char> ck(DH_size(dh)+1);
110 unsigned char *ckptr = &(ck.front())+1; 110 unsigned char *ckptr = &(ck.front())+1;
111 int cklen = DH_compute_key(ckptr,s_pub,dh); 111 int cklen = DH_compute_key(ckptr,s_pub,dh);
112 if(cklen<0) 112 if(cklen<0)
113 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); 113 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
114 if(cklen && (*ckptr)&0x80) { 114 if(cklen && (*ckptr)&0x80) {
115 (*(--ckptr)) = 0; ++cklen; 115 (*(--ckptr)) = 0; ++cklen;
116 } 116 }
117 unsigned char key_sha1[SHA_DIGEST_LENGTH]; 117 unsigned char key_sha1[SHA_DIGEST_LENGTH];
118 SHA1(ckptr,cklen,key_sha1); 118 SHA1(ckptr,cklen,key_sha1);
119 secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key")); 119 secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key"));
120 } 120 }
121 int expires_in = 0; 121 int expires_in = 0;
122 if(p.has_param("expires_in")) { 122 if(p.has_param("expires_in")) {
123 expires_in = util::string_to_long(p.get_param("expires_in")); 123 expires_in = util::string_to_long(p.get_param("expires_in"));
124 }else if(p.has_param("issued") && p.has_param("expiry")) { 124 }else if(p.has_param("issued") && p.has_param("expiry")) {
125 expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued")); 125 expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued"));
126 }else 126 }else
127 throw bad_input(OPKELE_CP_ "no expiration information"); 127 throw bad_input(OPKELE_CP_ "no expiration information");
128 return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in); 128 return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in);
129 } 129 }
130 130
131 string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 131 string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
132 return checkid_(mode_checkid_immediate,identity,return_to,trust_root,ext); 132 return checkid_(mode_checkid_immediate,identity,return_to,trust_root,ext);
133 } 133 }
134 string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 134 string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
135 return checkid_(mode_checkid_setup,identity,return_to,trust_root,ext); 135 return checkid_(mode_checkid_setup,identity,return_to,trust_root,ext);
136 } 136 }
137 string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 137 string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
138 params_t p; 138 params_t p;
139 if(mode==mode_checkid_immediate) 139 if(mode==mode_checkid_immediate)
140 p["mode"]="checkid_immediate"; 140 p["mode"]="checkid_immediate";
141 else if(mode==mode_checkid_setup) 141 else if(mode==mode_checkid_setup)
142 p["mode"]="checkid_setup"; 142 p["mode"]="checkid_setup";
143 else 143 else
144 throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); 144 throw bad_input(OPKELE_CP_ "unknown checkid_* mode");
145 string iurl = canonicalize(identity); 145 string iurl = canonicalize(identity);
146 string server, delegate; 146 string server, delegate;
147 retrieve_links(iurl,server,delegate); 147 retrieve_links(iurl,server,delegate);
148 p["identity"] = delegate.empty()?iurl:delegate; 148 p["identity"] = delegate.empty()?iurl:delegate;
149 if(!trust_root.empty()) 149 if(!trust_root.empty())
150 p["trust_root"] = trust_root; 150 p["trust_root"] = trust_root;
151 p["return_to"] = return_to; 151 p["return_to"] = return_to;
152 try { 152 try {
153 string ah = find_assoc(server)->handle(); 153 string ah = find_assoc(server)->handle();
154 p["assoc_handle"] = ah; 154 p["assoc_handle"] = ah;
155 }catch(failed_lookup& fl) { 155 }catch(failed_lookup& fl) {
156 string ah = associate(server)->handle(); 156 string ah = associate(server)->handle();
157 p["assoc_handle"] = ah; 157 p["assoc_handle"] = ah;
158 } 158 }
159 if(ext) ext->checkid_hook(p,identity); 159 if(ext) ext->checkid_hook(p,identity);
160 return p.append_query(server); 160 return p.append_query(server);
161 } 161 }
162 162
163 void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) { 163 void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) {
164 if(pin.has_param("openid.user_setup_url")) 164 if(pin.has_param("openid.user_setup_url"))
165 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url")); 165 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url"));
166 string server,delegate; 166 string server,delegate;
167 retrieve_links(identity.empty()?pin.get_param("openid.identity"):canonicalize(identity),server,delegate); 167 retrieve_links(identity.empty()?pin.get_param("openid.identity"):canonicalize(identity),server,delegate);
168 params_t ps; 168 params_t ps;
169 try { 169 try {
170 assoc_t assoc = retrieve_assoc(server,pin.get_param("openid.assoc_handle")); 170 assoc_t assoc = retrieve_assoc(server,pin.get_param("openid.assoc_handle"));
171 if(assoc->is_expired()) /* TODO: or should I throw some other exception to force programmer fix his implementation? */ 171 if(assoc->is_expired()) /* TODO: or should I throw some other exception to force programmer fix his implementation? */
172 throw failed_lookup(OPKELE_CP_ "retrieve_assoc() has returned expired handle"); 172 throw failed_lookup(OPKELE_CP_ "retrieve_assoc() has returned expired handle");
173 const string& sigenc = pin.get_param("openid.sig"); 173 const string& sigenc = pin.get_param("openid.sig");
174 vector<unsigned char> sig; 174 vector<unsigned char> sig;
175 util::decode_base64(sigenc,sig); 175 util::decode_base64(sigenc,sig);
176 const string& slist = pin.get_param("openid.signed"); 176 const string& slist = pin.get_param("openid.signed");
177 string kv; 177 string kv;
178 string::size_type p = 0; 178 string::size_type p = 0;
179 while(true) { 179 while(true) {
180 string::size_type co = slist.find(',',p); 180 string::size_type co = slist.find(',',p);
181 string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p); 181 string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p);
182 kv += f; 182 kv += f;
183 kv += ':'; 183 kv += ':';
184 f.insert(0,"openid."); 184 f.insert(0,"openid.");
185 kv += pin.get_param(f); 185 kv += pin.get_param(f);
186 kv += '\n'; 186 kv += '\n';
187 if(ext) ps[f.substr(sizeof("openid.")-1)] = pin.get_param(f); 187 if(ext) ps[f.substr(sizeof("openid.")-1)] = pin.get_param(f);
188 if(co==string::npos) 188 if(co==string::npos)
189 break; 189 break;
190 p = co+1; 190 p = co+1;
191 } 191 }
192 secret_t secret = assoc->secret(); 192 secret_t secret = assoc->secret();
193 unsigned int md_len = 0; 193 unsigned int md_len = 0;
194 unsigned char *md = HMAC( 194 unsigned char *md = HMAC(
195 EVP_sha1(), 195 EVP_sha1(),
196 &(secret.front()),secret.size(), 196 &(secret.front()),secret.size(),
197 (const unsigned char *)kv.data(),kv.length(), 197 (const unsigned char *)kv.data(),kv.length(),
198 0,&md_len); 198 0,&md_len);
199 if(sig.size()!=md_len || memcmp(&(sig.front()),md,md_len)) 199 if(sig.size()!=md_len || memcmp(&(sig.front()),md,md_len))
200 throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); 200 throw id_res_mismatch(OPKELE_CP_ "signature mismatch");
201 }catch(failed_lookup& e) { /* XXX: more specific? */ 201 }catch(failed_lookup& e) { /* XXX: more specific? */
202 const string& slist = pin.get_param("openid.signed"); 202 const string& slist = pin.get_param("openid.signed");
203 string::size_type pp = 0; 203 string::size_type pp = 0;
204 params_t p; 204 params_t p;
205 while(true) { 205 while(true) {
206 string::size_type co = slist.find(',',pp); 206 string::size_type co = slist.find(',',pp);
207 string f = "openid."; 207 string f = "openid.";
208 f += (co==string::npos)?slist.substr(pp):slist.substr(pp,co-pp); 208 f += (co==string::npos)?slist.substr(pp):slist.substr(pp,co-pp);
209 p[f] = pin.get_param(f); 209 p[f] = pin.get_param(f);
210 if(co==string::npos) 210 if(co==string::npos)
211 break; 211 break;
212 pp = co+1; 212 pp = co+1;
213 } 213 }
214 p["openid.assoc_handle"] = pin.get_param("openid.assoc_handle"); 214 p["openid.assoc_handle"] = pin.get_param("openid.assoc_handle");
215 p["openid.sig"] = pin.get_param("openid.sig"); 215 p["openid.sig"] = pin.get_param("openid.sig");
216 p["openid.signed"] = pin.get_param("openid.signed"); 216 p["openid.signed"] = pin.get_param("openid.signed");
217 try { 217 try {
218 string ih = pin.get_param("openid.invalidate_handle"); 218 string ih = pin.get_param("openid.invalidate_handle");
219 p["openid.invalidate_handle"] = ih; 219 p["openid.invalidate_handle"] = ih;
220 }catch(failed_lookup& fl) { } 220 }catch(failed_lookup& fl) { }
221 try { 221 try {
222 check_authentication(server,p); 222 check_authentication(server,p);
223 }catch(failed_check_authentication& fca) { 223 }catch(failed_check_authentication& fca) {
224 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); 224 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()");
225 } 225 }
226 } 226 }
227 if(ext) ext->id_res_hook(pin,ps,identity); 227 if(ext) ext->id_res_hook(pin,ps,identity);
228 } 228 }
229 229
230 void consumer_t::check_authentication(const string& server,const params_t& p) { 230 void consumer_t::check_authentication(const string& server,const params_t& p) {
231 string request = "openid.mode=check_authentication"; 231 string request = "openid.mode=check_authentication";
232 for(params_t::const_iterator i=p.begin();i!=p.end();++i) { 232 for(params_t::const_iterator i=p.begin();i!=p.end();++i) {
233 if(i->first!="openid.mode") { 233 if(i->first!="openid.mode") {
234 request += '&'; 234 request += '&';
235 request += i->first; 235 request += i->first;
236 request += '='; 236 request += '=';
237 request += util::url_encode(i->second); 237 request += util::url_encode(i->second);
238 } 238 }
239 } 239 }
240 curl_t curl = curl_easy_init(); 240 curl_t curl = curl_easy_init();
241 if(!curl) 241 if(!curl)
242 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()"); 242 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()");
243 string response; 243 string response;
244 CURLcode r; 244 CURLcode r;
245 (r=curl_misc_sets(curl)) 245 (r=curl_misc_sets(curl))
246 || (r=curl_easy_setopt(curl,CURLOPT_URL,server.c_str())) 246 || (r=curl_easy_setopt(curl,CURLOPT_URL,server.c_str()))
247 || (r=curl_easy_setopt(curl,CURLOPT_POST,1)) 247 || (r=curl_easy_setopt(curl,CURLOPT_POST,1))
248 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDS,request.data())) 248 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDS,request.data()))
249 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,request.length())) 249 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,request.length()))
250 || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring)) 250 || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring))
251 || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&response)) 251 || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&response))
252 ; 252 ;
253 if(r) 253 if(r)
254 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r); 254 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r);
255 if(r=curl_easy_perform(curl)) 255 if(r=curl_easy_perform(curl))
256 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r); 256 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r);
257 params_t pp; pp.parse_keyvalues(response); 257 params_t pp; pp.parse_keyvalues(response);
258 if(pp.has_param("invalidate_handle")) 258 if(pp.has_param("invalidate_handle"))
259 invalidate_assoc(server,pp.get_param("invalidate_handle")); 259 invalidate_assoc(server,pp.get_param("invalidate_handle"));
260 if(pp.has_param("is_valid")) { 260 if(pp.has_param("is_valid")) {
261 if(pp.get_param("is_valid")=="true") 261 if(pp.get_param("is_valid")=="true")
262 return; 262 return;
263 }else if(pp.has_param("lifetime")) { 263 }else if(pp.has_param("lifetime")) {
264 if(util::string_to_long(pp.get_param("lifetime"))) 264 if(util::string_to_long(pp.get_param("lifetime")))
265 return; 265 return;
266 } 266 }
267 throw failed_check_authentication(OPKELE_CP_ "failed to verify response"); 267 throw failed_check_authentication(OPKELE_CP_ "failed to verify response");
268 } 268 }
269 269
270 void consumer_t::retrieve_links(const string& url,string& server,string& delegate) { 270 void consumer_t::retrieve_links(const string& url,string& server,string& delegate) {
271#if defined(USE_LIBPCRECPP) || defined(USE_PCREPP) 271#if defined(USE_LIBPCRECPP) || defined(USE_PCREPP)
272 server.erase(); 272 server.erase();
273 delegate.erase(); 273 delegate.erase();
274 curl_t curl = curl_easy_init(); 274 curl_t curl = curl_easy_init();
275 if(!curl) 275 if(!curl)
276 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()"); 276 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()");
277 string html; 277 string html;
278 CURLcode r; 278 CURLcode r;
279 (r=curl_misc_sets(curl)) 279 (r=curl_misc_sets(curl))
280 || (r=curl_easy_setopt(curl,CURLOPT_URL,url.c_str())) 280 || (r=curl_easy_setopt(curl,CURLOPT_URL,url.c_str()))
281 || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring)) 281 || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring))
282 || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&html)) 282 || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&html))
283 ; 283 ;
284 if(r) 284 if(r)
285 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r); 285 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r);
286 r = curl_easy_perform(curl); 286 r = curl_easy_perform(curl);
287 if(r && r!=CURLE_WRITE_ERROR) 287 if(r && r!=CURLE_WRITE_ERROR)
288 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r); 288 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r);
289 // strip out everything past body 289 // strip out everything past body
290 static const char *re_hdre = "<head[^>]*>", 290 static const char *re_hdre = "<\\s*head[^>]*>",
291 *re_lre = "<link\\b([^>]+)>", 291 *re_lre = "<\\s*link\\b([^>]+)>",
292 *re_rre = "\\brel=['\"]([^'\"]+)['\"]", 292 *re_rre = "\\brel\\s*=\\s*['\"]\\s*([^'\"\\s]+)\\s*['\"]",
293 *re_hre = "\\bhref=['\"]([^'\"]+)['\"]"; 293 *re_hre = "\\bhref\\s*=\\s*['\"]\\s*([^'\"\\s]+)\\s*['\"]";
294#if defined(USE_LIBPCRECPP) 294#if defined(USE_LIBPCRECPP)
295 static pcrecpp::RE_Options ro(PCRE_CASELESS|PCRE_DOTALL); 295 static pcrecpp::RE_Options ro(PCRE_CASELESS|PCRE_DOTALL);
296 static pcrecpp::RE 296 static pcrecpp::RE
297 bre("<body\\b.*",ro), hdre(re_hdre,ro), 297 bre("<body\\b.*",ro), hdre(re_hdre,ro),
298 lre(re_lre,ro), rre(re_rre), hre(re_hre,ro); 298 lre(re_lre,ro), rre(re_rre), hre(re_hre,ro);
299 bre.Replace("",&html); 299 bre.Replace("",&html);
300 pcrecpp::StringPiece hpiece(html); 300 pcrecpp::StringPiece hpiece(html);
301 if(!hdre.FindAndConsume(&hpiece)) 301 if(!hdre.FindAndConsume(&hpiece))
302 throw bad_input(OPKELE_CP_ "failed to find head"); 302 throw bad_input(OPKELE_CP_ "failed to find head");
303 string attrs; 303 string attrs;
304 while(lre.FindAndConsume(&hpiece,&attrs)) { 304 while(lre.FindAndConsume(&hpiece,&attrs)) {
305 pcrecpp::StringPiece rel, href; 305 pcrecpp::StringPiece rel, href;
306 if(!(rre.PartialMatch(attrs,&rel) && hre.PartialMatch(attrs,&href))) 306 if(!(rre.PartialMatch(attrs,&rel) && hre.PartialMatch(attrs,&href)))
307 continue; 307 continue;
308 if(rel=="openid.server") { 308 if(rel=="openid.server") {
309 href.CopyToString(&server); 309 href.CopyToString(&server);
310 if(!delegate.empty()) 310 if(!delegate.empty())
311 break; 311 break;
312 }else if(rel=="openid.delegate") { 312 }else if(rel=="openid.delegate") {
313 href.CopyToString(&delegate); 313 href.CopyToString(&delegate);
314 if(!server.empty()) 314 if(!server.empty())
315 break; 315 break;
316 } 316 }
317 } 317 }
318#elif defined(USE_PCREPP) 318#elif defined(USE_PCREPP)
319 pcrepp::Pcre bre("<body\\b",PCRE_CASELESS); 319 pcrepp::Pcre bre("<body\\b",PCRE_CASELESS);
320 if(bre.search(html)) 320 if(bre.search(html))
321 html.erase(bre.get_match_start()); 321 html.erase(bre.get_match_start());
322 pcrepp::Pcre hdre(re_hdre,PCRE_CASELESS); 322 pcrepp::Pcre hdre(re_hdre,PCRE_CASELESS);
323 if(!hdre.search(html)) 323 if(!hdre.search(html))
324 throw bad_input(OPKELE_CP_ "failed to find head"); 324 throw bad_input(OPKELE_CP_ "failed to find head");
325 html.erase(0,hdre.get_match_end()+1); 325 html.erase(0,hdre.get_match_end()+1);
326 pcrepp::Pcre lre(re_lre,PCRE_CASELESS), rre(re_rre,PCRE_CASELESS), hre(re_hre,PCRE_CASELESS); 326 pcrepp::Pcre lre(re_lre,PCRE_CASELESS), rre(re_rre,PCRE_CASELESS), hre(re_hre,PCRE_CASELESS);
327 while(lre.search(html)) { 327 while(lre.search(html)) {
328 string attrs = lre[0]; 328 string attrs = lre[0];
329 html.erase(0,lre.get_match_end()+1); 329 html.erase(0,lre.get_match_end()+1);
330 if(!(rre.search(attrs)&&hre.search(attrs))) 330 if(!(rre.search(attrs)&&hre.search(attrs)))
331 continue; 331 continue;
332 if(rre[0]=="openid.server") { 332 if(rre[0]=="openid.server") {
333 server = hre[0]; 333 server = hre[0];
334 if(!delegate.empty()) 334 if(!delegate.empty())
335 break; 335 break;
336 }else if(rre[0]=="openid.delegate") { 336 }else if(rre[0]=="openid.delegate") {
337 delegate = hre[0]; 337 delegate = hre[0];
338 if(!server.empty()) 338 if(!server.empty())
339 break; 339 break;
340 } 340 }
341 } 341 }
342#else 342#else
343 #error "I must have gone crazy" 343 #error "I must have gone crazy"
344#endif 344#endif
345 if(server.empty()) 345 if(server.empty())
346 throw failed_assertion(OPKELE_CP_ "The location has no openid.server declaration"); 346 throw failed_assertion(OPKELE_CP_ "The location has no openid.server declaration");
347#else /* none of the RE bindings enabled */ 347#else /* none of the RE bindings enabled */
348 throw not_implemented(OPKELE_CP_ "No internal implementation of retrieve_links were provided at compile-time"); 348 throw not_implemented(OPKELE_CP_ "No internal implementation of retrieve_links were provided at compile-time");
349#endif 349#endif
350 } 350 }
351 351
352 assoc_t consumer_t::find_assoc(const string& server) { 352 assoc_t consumer_t::find_assoc(const string& server) {
353 throw failed_lookup(OPKELE_CP_ "no find_assoc() provided"); 353 throw failed_lookup(OPKELE_CP_ "no find_assoc() provided");
354 } 354 }
355 355
356 string consumer_t::normalize(const string& url) { 356 string consumer_t::normalize(const string& url) {
357 string rv = url; 357 string rv = url;
358 // strip leading and trailing spaces 358 // strip leading and trailing spaces
359 string::size_type i = rv.find_first_not_of(" \t\r\n"); 359 string::size_type i = rv.find_first_not_of(" \t\r\n");
360 if(i==string::npos) 360 if(i==string::npos)
361 throw bad_input(OPKELE_CP_ "empty URL"); 361 throw bad_input(OPKELE_CP_ "empty URL");
362 if(i) 362 if(i)
363 rv.erase(0,i); 363 rv.erase(0,i);
364 i = rv.find_last_not_of(" \t\r\n"); 364 i = rv.find_last_not_of(" \t\r\n");
365 assert(i!=string::npos); 365 assert(i!=string::npos);
366 if(i<(rv.length()-1)) 366 if(i<(rv.length()-1))
367 rv.erase(i+1); 367 rv.erase(i+1);
368 // add missing http:// 368 // add missing http://
369 i = rv.find("://"); 369 i = rv.find("://");
370 if(i==string::npos) { // primitive. but do we need more? 370 if(i==string::npos) { // primitive. but do we need more?
371 rv.insert(0,"http://"); 371 rv.insert(0,"http://");
372 i = sizeof("http://")-1; 372 i = sizeof("http://")-1;
373 }else{ 373 }else{
374 i += sizeof("://")-1; 374 i += sizeof("://")-1;
375 } 375 }
376 string::size_type qm = rv.find('?',i); 376 string::size_type qm = rv.find('?',i);
377 string::size_type sl = rv.find('/',i); 377 string::size_type sl = rv.find('/',i);
378 if(qm!=string::npos) { 378 if(qm!=string::npos) {
379 if(sl==string::npos || sl>qm) 379 if(sl==string::npos || sl>qm)
380 rv.insert(qm,1,'/'); 380 rv.insert(qm,1,'/');
381 }else{ 381 }else{
382 if(sl==string::npos) 382 if(sl==string::npos)
383 rv += '/'; 383 rv += '/';
384 } 384 }
385 return rv; 385 return rv;
386 } 386 }
387 387
388 string consumer_t::canonicalize(const string& url) { 388 string consumer_t::canonicalize(const string& url) {
389 string rv = normalize(url); 389 string rv = normalize(url);
390 curl_t curl = curl_easy_init(); 390 curl_t curl = curl_easy_init();
391 if(!curl) 391 if(!curl)
392 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()"); 392 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()");
393 string html; 393 string html;
394 CURLcode r; 394 CURLcode r;
395 (r=curl_misc_sets(curl)) 395 (r=curl_misc_sets(curl))
396 || (r=curl_easy_setopt(curl,CURLOPT_URL,rv.c_str())) 396 || (r=curl_easy_setopt(curl,CURLOPT_URL,rv.c_str()))
397 || (r=curl_easy_setopt(curl,CURLOPT_NOBODY,1)) 397 || (r=curl_easy_setopt(curl,CURLOPT_NOBODY,1))
398 ; 398 ;
399 if(r) 399 if(r)
400 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r); 400 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r);
401 r = curl_easy_perform(curl); 401 r = curl_easy_perform(curl);
402 if(r) 402 if(r)
403 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r); 403 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r);
404 const char *eu = 0; 404 const char *eu = 0;
405 r = curl_easy_getinfo(curl,CURLINFO_EFFECTIVE_URL,&eu); 405 r = curl_easy_getinfo(curl,CURLINFO_EFFECTIVE_URL,&eu);
406 if(r) 406 if(r)
407 throw exception_curl(OPKELE_CP_ "failed to curl_easy_getinfo(..CURLINFO_EFFECTIVE_URL..)",r); 407 throw exception_curl(OPKELE_CP_ "failed to curl_easy_getinfo(..CURLINFO_EFFECTIVE_URL..)",r);
408 rv = eu; 408 rv = eu;
409 return normalize(rv); 409 return normalize(rv);
410 } 410 }
411 411
412} 412}