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