summaryrefslogtreecommitdiffabout
path: root/lib/consumer.cc
Unidiff
Diffstat (limited to 'lib/consumer.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/consumer.cc4
1 files changed, 2 insertions, 2 deletions
diff --git a/lib/consumer.cc b/lib/consumer.cc
index 3c3b4f8..ebda262 100644
--- a/lib/consumer.cc
+++ b/lib/consumer.cc
@@ -1,395 +1,395 @@
1#include <algorithm> 1#include <algorithm>
2#include <cassert> 2#include <cassert>
3#include <cstring> 3#include <cstring>
4#include <opkele/util.h> 4#include <opkele/util.h>
5#include <opkele/curl.h> 5#include <opkele/curl.h>
6#include <opkele/exception.h> 6#include <opkele/exception.h>
7#include <opkele/data.h> 7#include <opkele/data.h>
8#include <opkele/consumer.h> 8#include <opkele/consumer.h>
9#include <openssl/sha.h> 9#include <openssl/sha.h>
10#include <openssl/hmac.h> 10#include <openssl/hmac.h>
11#include <iostream> 11#include <iostream>
12 12
13#include "config.h" 13#include "config.h"
14 14
15#include <pcre.h> 15#include <pcre.h>
16 16
17namespace opkele { 17namespace opkele {
18 using namespace std; 18 using namespace std;
19 using util::curl_t; 19 using util::curl_t;
20 using util::curl_pick_t; 20 using util::curl_pick_t;
21 21
22 class pcre_matches_t { 22 class pcre_matches_t {
23 public: 23 public:
24 int *_ov; 24 int *_ov;
25 int _s; 25 int _s;
26 26
27 pcre_matches_t() : _ov(0), _s(0) { } 27 pcre_matches_t() : _ov(0), _s(0) { }
28 pcre_matches_t(int s) : _ov(0), _s(s) { 28 pcre_matches_t(int s) : _ov(0), _s(s) {
29 if(_s&1) ++_s; 29 if(_s&1) ++_s;
30 _s += _s>>1; 30 _s += _s>>1;
31 _ov = new int[_s]; 31 _ov = new int[_s];
32 } 32 }
33 ~pcre_matches_t() throw() { if(_ov) delete[] _ov; } 33 ~pcre_matches_t() throw() { if(_ov) delete[] _ov; }
34 34
35 int begin(int i) const { return _ov[i<<1]; } 35 int begin(int i) const { return _ov[i<<1]; }
36 int end(int i) const { return _ov[(i<<1)+1]; } 36 int end(int i) const { return _ov[(i<<1)+1]; }
37 int length(int i) const { int t=i<<1; return _ov[t+1]-_ov[t]; } 37 int length(int i) const { int t=i<<1; return _ov[t+1]-_ov[t]; }
38 }; 38 };
39 39
40 class pcre_t { 40 class pcre_t {
41 public: 41 public:
42 pcre *_p; 42 pcre *_p;
43 43
44 pcre_t() : _p(0) { } 44 pcre_t() : _p(0) { }
45 pcre_t(pcre *p) : _p(p) { } 45 pcre_t(pcre *p) : _p(p) { }
46 pcre_t(const char *re,int opts) : _p(0) { 46 pcre_t(const char *re,int opts) : _p(0) {
47 static const char *errptr; static int erroffset; 47 static const char *errptr; static int erroffset;
48 _p = pcre_compile(re,opts,&errptr,&erroffset,NULL); 48 _p = pcre_compile(re,opts,&errptr,&erroffset,NULL);
49 if(!_p) 49 if(!_p)
50 throw internal_error(OPKELE_CP_ string("Failed to compile regexp: ")+errptr); 50 throw internal_error(OPKELE_CP_ string("Failed to compile regexp: ")+errptr);
51 } 51 }
52 ~pcre_t() throw() { if(_p) (*pcre_free)(_p); } 52 ~pcre_t() throw() { if(_p) (*pcre_free)(_p); }
53 53
54 pcre_t& operator=(pcre *p) { if(_p) (*pcre_free)(_p); _p=p; return *this; } 54 pcre_t& operator=(pcre *p) { if(_p) (*pcre_free)(_p); _p=p; return *this; }
55 55
56 operator const pcre*(void) const { return _p; } 56 operator const pcre*(void) const { return _p; }
57 operator pcre*(void) { return _p; } 57 operator pcre*(void) { return _p; }
58 58
59 int exec(const string& s,pcre_matches_t& m) { 59 int exec(const string& s,pcre_matches_t& m) {
60 if(!_p) 60 if(!_p)
61 throw internal_error(OPKELE_CP_ "Trying to execute absent regexp"); 61 throw internal_error(OPKELE_CP_ "Trying to execute absent regexp");
62 return pcre_exec(_p,NULL,s.c_str(),s.length(),0,0,m._ov,m._s); 62 return pcre_exec(_p,NULL,s.c_str(),s.length(),0,0,m._ov,m._s);
63 } 63 }
64 }; 64 };
65 65
66 assoc_t consumer_t::associate(const string& server) { 66 assoc_t consumer_t::associate(const string& server) {
67 util::dh_t dh = DH_new(); 67 util::dh_t dh = DH_new();
68 if(!dh) 68 if(!dh)
69 throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); 69 throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
70 dh->p = util::dec_to_bignum(data::_default_p); 70 dh->p = util::dec_to_bignum(data::_default_p);
71 dh->g = util::dec_to_bignum(data::_default_g); 71 dh->g = util::dec_to_bignum(data::_default_g);
72 if(!DH_generate_key(dh)) 72 if(!DH_generate_key(dh))
73 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); 73 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
74 string request = 74 string request =
75 "openid.mode=associate" 75 "openid.mode=associate"
76 "&openid.assoc_type=HMAC-SHA1" 76 "&openid.assoc_type=HMAC-SHA1"
77 "&openid.session_type=DH-SHA1" 77 "&openid.session_type=DH-SHA1"
78 "&openid.dh_consumer_public="; 78 "&openid.dh_consumer_public=";
79 request += util::url_encode(util::bignum_to_base64(dh->pub_key)); 79 request += util::url_encode(util::bignum_to_base64(dh->pub_key));
80 curl_pick_t curl = curl_pick_t::easy_init(); 80 curl_pick_t curl = curl_pick_t::easy_init();
81 if(!curl) 81 if(!curl)
82 throw exception_curl(OPKELE_CP_ "failed to initialize curl"); 82 throw exception_curl(OPKELE_CP_ "failed to initialize curl");
83 CURLcode r; 83 CURLcode r;
84 (r=curl.misc_sets()) 84 (r=curl.misc_sets())
85 || (r=curl.easy_setopt(CURLOPT_URL,server.c_str())) 85 || (r=curl.easy_setopt(CURLOPT_URL,server.c_str()))
86 || (r=curl.easy_setopt(CURLOPT_POST,1)) 86 || (r=curl.easy_setopt(CURLOPT_POST,1))
87 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) 87 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data()))
88 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) 88 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length()))
89 || (r=curl.set_write()) 89 || (r=curl.set_write())
90 ; 90 ;
91 if(r) 91 if(r)
92 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 92 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
93 if( (r=curl.easy_perform()) ) 93 if( (r=curl.easy_perform()) )
94 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 94 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
95 params_t p; p.parse_keyvalues(curl.response); 95 params_t p; p.parse_keyvalues(curl.response);
96 if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1") 96 if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1")
97 throw bad_input(OPKELE_CP_ "unsupported assoc_type"); 97 throw bad_input(OPKELE_CP_ "unsupported assoc_type");
98 string st; 98 string st;
99 if(p.has_param("session_type")) st = p.get_param("session_type"); 99 if(p.has_param("session_type")) st = p.get_param("session_type");
100 if((!st.empty()) && st!="DH-SHA1") 100 if((!st.empty()) && st!="DH-SHA1")
101 throw bad_input(OPKELE_CP_ "unsupported session_type"); 101 throw bad_input(OPKELE_CP_ "unsupported session_type");
102 secret_t secret; 102 secret_t secret;
103 if(st.empty()) { 103 if(st.empty()) {
104 secret.from_base64(p.get_param("mac_key")); 104 secret.from_base64(p.get_param("mac_key"));
105 }else{ 105 }else{
106 util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public")); 106 util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public"));
107 vector<unsigned char> ck(DH_size(dh)+1); 107 vector<unsigned char> ck(DH_size(dh)+1);
108 unsigned char *ckptr = &(ck.front())+1; 108 unsigned char *ckptr = &(ck.front())+1;
109 int cklen = DH_compute_key(ckptr,s_pub,dh); 109 int cklen = DH_compute_key(ckptr,s_pub,dh);
110 if(cklen<0) 110 if(cklen<0)
111 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); 111 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
112 if(cklen && (*ckptr)&0x80) { 112 if(cklen && (*ckptr)&0x80) {
113 (*(--ckptr)) = 0; ++cklen; 113 (*(--ckptr)) = 0; ++cklen;
114 } 114 }
115 unsigned char key_sha1[SHA_DIGEST_LENGTH]; 115 unsigned char key_sha1[SHA_DIGEST_LENGTH];
116 SHA1(ckptr,cklen,key_sha1); 116 SHA1(ckptr,cklen,key_sha1);
117 secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key")); 117 secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key"));
118 } 118 }
119 int expires_in = 0; 119 int expires_in = 0;
120 if(p.has_param("expires_in")) { 120 if(p.has_param("expires_in")) {
121 expires_in = util::string_to_long(p.get_param("expires_in")); 121 expires_in = util::string_to_long(p.get_param("expires_in"));
122 }else if(p.has_param("issued") && p.has_param("expiry")) { 122 }else if(p.has_param("issued") && p.has_param("expiry")) {
123 expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued")); 123 expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued"));
124 }else 124 }else
125 throw bad_input(OPKELE_CP_ "no expiration information"); 125 throw bad_input(OPKELE_CP_ "no expiration information");
126 return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in); 126 return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in);
127 } 127 }
128 128
129 string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 129 string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
130 return checkid_(mode_checkid_immediate,identity,return_to,trust_root,ext); 130 return checkid_(mode_checkid_immediate,identity,return_to,trust_root,ext);
131 } 131 }
132 string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 132 string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
133 return checkid_(mode_checkid_setup,identity,return_to,trust_root,ext); 133 return checkid_(mode_checkid_setup,identity,return_to,trust_root,ext);
134 } 134 }
135 string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 135 string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
136 params_t p; 136 params_t p;
137 if(mode==mode_checkid_immediate) 137 if(mode==mode_checkid_immediate)
138 p["mode"]="checkid_immediate"; 138 p["mode"]="checkid_immediate";
139 else if(mode==mode_checkid_setup) 139 else if(mode==mode_checkid_setup)
140 p["mode"]="checkid_setup"; 140 p["mode"]="checkid_setup";
141 else 141 else
142 throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); 142 throw bad_input(OPKELE_CP_ "unknown checkid_* mode");
143 string iurl = canonicalize(identity); 143 string iurl = canonicalize(identity);
144 string server, delegate; 144 string server, delegate;
145 retrieve_links(iurl,server,delegate); 145 retrieve_links(iurl,server,delegate);
146 p["identity"] = delegate.empty()?iurl:delegate; 146 p["identity"] = delegate.empty()?iurl:delegate;
147 if(!trust_root.empty()) 147 if(!trust_root.empty())
148 p["trust_root"] = trust_root; 148 p["trust_root"] = trust_root;
149 p["return_to"] = return_to; 149 p["return_to"] = return_to;
150 try { 150 try {
151 string ah = find_assoc(server)->handle(); 151 string ah = find_assoc(server)->handle();
152 p["assoc_handle"] = ah; 152 p["assoc_handle"] = ah;
153 }catch(failed_lookup& fl) { 153 }catch(failed_lookup& fl) {
154 string ah = associate(server)->handle(); 154 string ah = associate(server)->handle();
155 p["assoc_handle"] = ah; 155 p["assoc_handle"] = ah;
156 } 156 }
157 if(ext) ext->checkid_hook(p,identity); 157 if(ext) ext->checkid_hook(p);
158 return p.append_query(server); 158 return p.append_query(server);
159 } 159 }
160 160
161 void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) { 161 void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) {
162 if(pin.has_param("openid.user_setup_url")) 162 if(pin.has_param("openid.user_setup_url"))
163 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url")); 163 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url"));
164 string server,delegate; 164 string server,delegate;
165 retrieve_links(identity.empty()?pin.get_param("openid.identity"):canonicalize(identity),server,delegate); 165 retrieve_links(identity.empty()?pin.get_param("openid.identity"):canonicalize(identity),server,delegate);
166 params_t ps; 166 params_t ps;
167 try { 167 try {
168 assoc_t assoc = retrieve_assoc(server,pin.get_param("openid.assoc_handle")); 168 assoc_t assoc = retrieve_assoc(server,pin.get_param("openid.assoc_handle"));
169 if(assoc->is_expired()) 169 if(assoc->is_expired())
170 throw id_res_expired_on_delivery(OPKELE_CP_ "retrieve_assoc() has returned expired handle"); 170 throw id_res_expired_on_delivery(OPKELE_CP_ "retrieve_assoc() has returned expired handle");
171 const string& sigenc = pin.get_param("openid.sig"); 171 const string& sigenc = pin.get_param("openid.sig");
172 vector<unsigned char> sig; 172 vector<unsigned char> sig;
173 util::decode_base64(sigenc,sig); 173 util::decode_base64(sigenc,sig);
174 const string& slist = pin.get_param("openid.signed"); 174 const string& slist = pin.get_param("openid.signed");
175 string kv; 175 string kv;
176 string::size_type p = 0; 176 string::size_type p = 0;
177 while(true) { 177 while(true) {
178 string::size_type co = slist.find(',',p); 178 string::size_type co = slist.find(',',p);
179 string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p); 179 string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p);
180 kv += f; 180 kv += f;
181 kv += ':'; 181 kv += ':';
182 f.insert(0,"openid."); 182 f.insert(0,"openid.");
183 kv += pin.get_param(f); 183 kv += pin.get_param(f);
184 kv += '\n'; 184 kv += '\n';
185 if(ext) ps[f.substr(sizeof("openid.")-1)] = pin.get_param(f); 185 if(ext) ps[f.substr(sizeof("openid.")-1)] = pin.get_param(f);
186 if(co==string::npos) 186 if(co==string::npos)
187 break; 187 break;
188 p = co+1; 188 p = co+1;
189 } 189 }
190 secret_t secret = assoc->secret(); 190 secret_t secret = assoc->secret();
191 unsigned int md_len = 0; 191 unsigned int md_len = 0;
192 unsigned char *md = HMAC( 192 unsigned char *md = HMAC(
193 EVP_sha1(), 193 EVP_sha1(),
194 &(secret.front()),secret.size(), 194 &(secret.front()),secret.size(),
195 (const unsigned char *)kv.data(),kv.length(), 195 (const unsigned char *)kv.data(),kv.length(),
196 0,&md_len); 196 0,&md_len);
197 if(sig.size()!=md_len || memcmp(&(sig.front()),md,md_len)) 197 if(sig.size()!=md_len || memcmp(&(sig.front()),md,md_len))
198 throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); 198 throw id_res_mismatch(OPKELE_CP_ "signature mismatch");
199 }catch(failed_lookup& e) { 199 }catch(failed_lookup& e) {
200 const string& slist = pin.get_param("openid.signed"); 200 const string& slist = pin.get_param("openid.signed");
201 string::size_type pp = 0; 201 string::size_type pp = 0;
202 params_t p; 202 params_t p;
203 while(true) { 203 while(true) {
204 string::size_type co = slist.find(',',pp); 204 string::size_type co = slist.find(',',pp);
205 string f = "openid."; 205 string f = "openid.";
206 f += (co==string::npos)?slist.substr(pp):slist.substr(pp,co-pp); 206 f += (co==string::npos)?slist.substr(pp):slist.substr(pp,co-pp);
207 p[f] = pin.get_param(f); 207 p[f] = pin.get_param(f);
208 if(co==string::npos) 208 if(co==string::npos)
209 break; 209 break;
210 pp = co+1; 210 pp = co+1;
211 } 211 }
212 p["openid.assoc_handle"] = pin.get_param("openid.assoc_handle"); 212 p["openid.assoc_handle"] = pin.get_param("openid.assoc_handle");
213 p["openid.sig"] = pin.get_param("openid.sig"); 213 p["openid.sig"] = pin.get_param("openid.sig");
214 p["openid.signed"] = pin.get_param("openid.signed"); 214 p["openid.signed"] = pin.get_param("openid.signed");
215 try { 215 try {
216 string ih = pin.get_param("openid.invalidate_handle"); 216 string ih = pin.get_param("openid.invalidate_handle");
217 p["openid.invalidate_handle"] = ih; 217 p["openid.invalidate_handle"] = ih;
218 }catch(failed_lookup& fl) { } 218 }catch(failed_lookup& fl) { }
219 try { 219 try {
220 check_authentication(server,p); 220 check_authentication(server,p);
221 }catch(failed_check_authentication& fca) { 221 }catch(failed_check_authentication& fca) {
222 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); 222 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()");
223 } 223 }
224 } 224 }
225 if(ext) ext->id_res_hook(pin,ps,identity); 225 if(ext) ext->id_res_hook(pin,ps);
226 } 226 }
227 227
228 void consumer_t::check_authentication(const string& server,const params_t& p) { 228 void consumer_t::check_authentication(const string& server,const params_t& p) {
229 string request = "openid.mode=check_authentication"; 229 string request = "openid.mode=check_authentication";
230 for(params_t::const_iterator i=p.begin();i!=p.end();++i) { 230 for(params_t::const_iterator i=p.begin();i!=p.end();++i) {
231 if(i->first!="openid.mode") { 231 if(i->first!="openid.mode") {
232 request += '&'; 232 request += '&';
233 request += i->first; 233 request += i->first;
234 request += '='; 234 request += '=';
235 request += util::url_encode(i->second); 235 request += util::url_encode(i->second);
236 } 236 }
237 } 237 }
238 curl_pick_t curl = curl_pick_t::easy_init(); 238 curl_pick_t curl = curl_pick_t::easy_init();
239 if(!curl) 239 if(!curl)
240 throw exception_curl(OPKELE_CP_ "failed to initialize curl"); 240 throw exception_curl(OPKELE_CP_ "failed to initialize curl");
241 CURLcode r; 241 CURLcode r;
242 (r=curl.misc_sets()) 242 (r=curl.misc_sets())
243 || (r=curl.easy_setopt(CURLOPT_URL,server.c_str())) 243 || (r=curl.easy_setopt(CURLOPT_URL,server.c_str()))
244 || (r=curl.easy_setopt(CURLOPT_POST,1)) 244 || (r=curl.easy_setopt(CURLOPT_POST,1))
245 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) 245 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data()))
246 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) 246 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length()))
247 || (r=curl.set_write()) 247 || (r=curl.set_write())
248 ; 248 ;
249 if(r) 249 if(r)
250 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 250 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
251 if( (r=curl.easy_perform()) ) 251 if( (r=curl.easy_perform()) )
252 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 252 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
253 params_t pp; pp.parse_keyvalues(curl.response); 253 params_t pp; pp.parse_keyvalues(curl.response);
254 if(pp.has_param("invalidate_handle")) 254 if(pp.has_param("invalidate_handle"))
255 invalidate_assoc(server,pp.get_param("invalidate_handle")); 255 invalidate_assoc(server,pp.get_param("invalidate_handle"));
256 if(pp.has_param("is_valid")) { 256 if(pp.has_param("is_valid")) {
257 if(pp.get_param("is_valid")=="true") 257 if(pp.get_param("is_valid")=="true")
258 return; 258 return;
259 }else if(pp.has_param("lifetime")) { 259 }else if(pp.has_param("lifetime")) {
260 if(util::string_to_long(pp.get_param("lifetime"))) 260 if(util::string_to_long(pp.get_param("lifetime")))
261 return; 261 return;
262 } 262 }
263 throw failed_check_authentication(OPKELE_CP_ "failed to verify response"); 263 throw failed_check_authentication(OPKELE_CP_ "failed to verify response");
264 } 264 }
265 265
266 void consumer_t::retrieve_links(const string& url,string& server,string& delegate) { 266 void consumer_t::retrieve_links(const string& url,string& server,string& delegate) {
267 server.erase(); 267 server.erase();
268 delegate.erase(); 268 delegate.erase();
269 curl_pick_t curl = curl_pick_t::easy_init(); 269 curl_pick_t curl = curl_pick_t::easy_init();
270 if(!curl) 270 if(!curl)
271 throw exception_curl(OPKELE_CP_ "failed to initialize curl"); 271 throw exception_curl(OPKELE_CP_ "failed to initialize curl");
272 string& html = curl.response; 272 string& html = curl.response;
273 CURLcode r; 273 CURLcode r;
274 (r=curl.misc_sets()) 274 (r=curl.misc_sets())
275 || (r=curl.easy_setopt(CURLOPT_URL,url.c_str())) 275 || (r=curl.easy_setopt(CURLOPT_URL,url.c_str()))
276 || (r=curl.set_write()); 276 || (r=curl.set_write());
277 ; 277 ;
278 if(r) 278 if(r)
279 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 279 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
280 r = curl.easy_perform(); 280 r = curl.easy_perform();
281 if(r && r!=CURLE_WRITE_ERROR) 281 if(r && r!=CURLE_WRITE_ERROR)
282 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 282 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
283 static const char *re_bre = "<\\s*body\\b", *re_hdre = "<\\s*head[^>]*>", 283 static const char *re_bre = "<\\s*body\\b", *re_hdre = "<\\s*head[^>]*>",
284 *re_lre = "<\\s*link\\b([^>]+)>", 284 *re_lre = "<\\s*link\\b([^>]+)>",
285 *re_rre = "\\brel\\s*=\\s*['\"]([^'\"]+)['\"]", 285 *re_rre = "\\brel\\s*=\\s*['\"]([^'\"]+)['\"]",
286 *re_hre = "\\bhref\\s*=\\s*['\"]\\s*([^'\"\\s]+)\\s*['\"]"; 286 *re_hre = "\\bhref\\s*=\\s*['\"]\\s*([^'\"\\s]+)\\s*['\"]";
287 pcre_matches_t m1(3), m2(3); 287 pcre_matches_t m1(3), m2(3);
288 pcre_t bre(re_bre,PCRE_CASELESS); 288 pcre_t bre(re_bre,PCRE_CASELESS);
289 if(bre.exec(html,m1)>0) 289 if(bre.exec(html,m1)>0)
290 html.erase(m1.begin(0)); 290 html.erase(m1.begin(0));
291 pcre_t hdre(re_hdre,PCRE_CASELESS); 291 pcre_t hdre(re_hdre,PCRE_CASELESS);
292 if(hdre.exec(html,m1)<=0) 292 if(hdre.exec(html,m1)<=0)
293 throw bad_input(OPKELE_CP_ "failed to find <head>"); 293 throw bad_input(OPKELE_CP_ "failed to find <head>");
294 html.erase(0,m1.end(0)+1); 294 html.erase(0,m1.end(0)+1);
295 pcre_t lre(re_lre,PCRE_CASELESS), rre(re_rre,PCRE_CASELESS), hre(re_hre,PCRE_CASELESS); 295 pcre_t lre(re_lre,PCRE_CASELESS), rre(re_rre,PCRE_CASELESS), hre(re_hre,PCRE_CASELESS);
296 bool gotit = false; 296 bool gotit = false;
297 while( (!gotit) && lre.exec(html,m1)>=2 ) { 297 while( (!gotit) && lre.exec(html,m1)>=2 ) {
298 static const char *whitespace = " \t"; 298 static const char *whitespace = " \t";
299 string attrs(html,m1.begin(1),m1.length(1)); 299 string attrs(html,m1.begin(1),m1.length(1));
300 html.erase(0,m1.end(0)+1); 300 html.erase(0,m1.end(0)+1);
301 if(!( rre.exec(attrs,m1)>=2 && hre.exec(attrs,m2)>=2 )) 301 if(!( rre.exec(attrs,m1)>=2 && hre.exec(attrs,m2)>=2 ))
302 continue; 302 continue;
303 string rels(attrs,m1.begin(1),m1.length(1)); 303 string rels(attrs,m1.begin(1),m1.length(1));
304 for(string::size_type ns = rels.find_first_not_of(whitespace); 304 for(string::size_type ns = rels.find_first_not_of(whitespace);
305 ns!=string::npos; 305 ns!=string::npos;
306 ns=rels.find_first_not_of(whitespace,ns)) { 306 ns=rels.find_first_not_of(whitespace,ns)) {
307 string::size_type s = rels.find_first_of(whitespace,ns); 307 string::size_type s = rels.find_first_of(whitespace,ns);
308 string rel; 308 string rel;
309 if(s==string::npos) { 309 if(s==string::npos) {
310 rel.assign(rels,ns,string::npos); 310 rel.assign(rels,ns,string::npos);
311 ns=string::npos; 311 ns=string::npos;
312 }else{ 312 }else{
313 rel.assign(rels,ns,s-ns); 313 rel.assign(rels,ns,s-ns);
314 ns=s; 314 ns=s;
315 } 315 }
316 if(rel=="openid.server") { 316 if(rel=="openid.server") {
317 server.assign(attrs,m2.begin(1),m2.length(1)); 317 server.assign(attrs,m2.begin(1),m2.length(1));
318 if(!delegate.empty()) { 318 if(!delegate.empty()) {
319 gotit = true; 319 gotit = true;
320 break; 320 break;
321 } 321 }
322 }else if(rel=="openid.delegate") { 322 }else if(rel=="openid.delegate") {
323 delegate.assign(attrs,m2.begin(1),m2.length(1)); 323 delegate.assign(attrs,m2.begin(1),m2.length(1));
324 if(!server.empty()) { 324 if(!server.empty()) {
325 gotit = true; 325 gotit = true;
326 break; 326 break;
327 } 327 }
328 } 328 }
329 } 329 }
330 } 330 }
331 if(server.empty()) 331 if(server.empty())
332 throw failed_assertion(OPKELE_CP_ "The location has no openid.server declaration"); 332 throw failed_assertion(OPKELE_CP_ "The location has no openid.server declaration");
333 } 333 }
334 334
335 assoc_t consumer_t::find_assoc(const string& /* server */) { 335 assoc_t consumer_t::find_assoc(const string& /* server */) {
336 throw failed_lookup(OPKELE_CP_ "no find_assoc() provided"); 336 throw failed_lookup(OPKELE_CP_ "no find_assoc() provided");
337 } 337 }
338 338
339 string consumer_t::normalize(const string& url) { 339 string consumer_t::normalize(const string& url) {
340 string rv = url; 340 string rv = url;
341 // strip leading and trailing spaces 341 // strip leading and trailing spaces
342 string::size_type i = rv.find_first_not_of(" \t\r\n"); 342 string::size_type i = rv.find_first_not_of(" \t\r\n");
343 if(i==string::npos) 343 if(i==string::npos)
344 throw bad_input(OPKELE_CP_ "empty URL"); 344 throw bad_input(OPKELE_CP_ "empty URL");
345 if(i) 345 if(i)
346 rv.erase(0,i); 346 rv.erase(0,i);
347 i = rv.find_last_not_of(" \t\r\n"); 347 i = rv.find_last_not_of(" \t\r\n");
348 assert(i!=string::npos); 348 assert(i!=string::npos);
349 if(i<(rv.length()-1)) 349 if(i<(rv.length()-1))
350 rv.erase(i+1); 350 rv.erase(i+1);
351 // add missing http:// 351 // add missing http://
352 i = rv.find("://"); 352 i = rv.find("://");
353 if(i==string::npos) { // primitive. but do we need more? 353 if(i==string::npos) { // primitive. but do we need more?
354 rv.insert(0,"http://"); 354 rv.insert(0,"http://");
355 i = sizeof("http://")-1; 355 i = sizeof("http://")-1;
356 }else{ 356 }else{
357 i += sizeof("://")-1; 357 i += sizeof("://")-1;
358 } 358 }
359 string::size_type qm = rv.find('?',i); 359 string::size_type qm = rv.find('?',i);
360 string::size_type sl = rv.find('/',i); 360 string::size_type sl = rv.find('/',i);
361 if(qm!=string::npos) { 361 if(qm!=string::npos) {
362 if(sl==string::npos || sl>qm) 362 if(sl==string::npos || sl>qm)
363 rv.insert(qm,1,'/'); 363 rv.insert(qm,1,'/');
364 }else{ 364 }else{
365 if(sl==string::npos) 365 if(sl==string::npos)
366 rv += '/'; 366 rv += '/';
367 } 367 }
368 return rv; 368 return rv;
369 } 369 }
370 370
371 string consumer_t::canonicalize(const string& url) { 371 string consumer_t::canonicalize(const string& url) {
372 string rv = normalize(url); 372 string rv = normalize(url);
373 curl_t curl = curl_t::easy_init(); 373 curl_t curl = curl_t::easy_init();
374 if(!curl) 374 if(!curl)
375 throw exception_curl(OPKELE_CP_ "failed to initialize curl()"); 375 throw exception_curl(OPKELE_CP_ "failed to initialize curl()");
376 string html; 376 string html;
377 CURLcode r; 377 CURLcode r;
378 (r=curl.misc_sets()) 378 (r=curl.misc_sets())
379 || (r=curl.easy_setopt(CURLOPT_URL,rv.c_str())) 379 || (r=curl.easy_setopt(CURLOPT_URL,rv.c_str()))
380 || (r=curl.easy_setopt(CURLOPT_NOBODY,1)) 380 || (r=curl.easy_setopt(CURLOPT_NOBODY,1))
381 ; 381 ;
382 if(r) 382 if(r)
383 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 383 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
384 r = curl.easy_perform(); 384 r = curl.easy_perform();
385 if(r) 385 if(r)
386 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 386 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
387 const char *eu = 0; 387 const char *eu = 0;
388 r = curl.easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); 388 r = curl.easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu);
389 if(r) 389 if(r)
390 throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); 390 throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r);
391 rv = eu; 391 rv = eu;
392 return normalize(rv); 392 return normalize(rv);
393 } 393 }
394 394
395} 395}