summaryrefslogtreecommitdiffabout
path: root/lib/util.cc
authorMichael Krelin <hacker@klever.net>2007-12-02 21:48:18 (UTC)
committer Michael Krelin <hacker@klever.net>2007-12-02 21:51:08 (UTC)
commit262f1579f0a9138a01f06afea06d00155cefd4b5 (patch) (unidiff)
treefb4db0ee7b679a1957c63abbe6f6af1d2fa82531 /lib/util.cc
parent73d98f3652b498b9a74b183bef395714c7d73fda (diff)
downloadlibopkele-262f1579f0a9138a01f06afea06d00155cefd4b5.zip
libopkele-262f1579f0a9138a01f06afea06d00155cefd4b5.tar.gz
libopkele-262f1579f0a9138a01f06afea06d00155cefd4b5.tar.bz2
first cut on XRI resolver
This commit adds openid service resolver that does discovery using XRI (proxy only), Yadis protocol and html-based discovery. It uses expat as xml parsing engine, which makes it a bit more strict about html it receives, but I think failing to discover links in *severely* broken html is better than misdetecting links, hidden in comments or such. This is highly experimental code and needs more thoughts and testing. Thanks everyone pushing me towards this development. Namely Joseph, John, Gen. Signed-off-by: Michael Krelin <hacker@klever.net>
Diffstat (limited to 'lib/util.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/util.cc16
1 files changed, 12 insertions, 4 deletions
diff --git a/lib/util.cc b/lib/util.cc
index ac70938..69d37b5 100644
--- a/lib/util.cc
+++ b/lib/util.cc
@@ -1,295 +1,303 @@
1#include <errno.h> 1#include <errno.h>
2#include <cassert> 2#include <cassert>
3#include <cctype> 3#include <cctype>
4#include <cstring> 4#include <cstring>
5#include <vector> 5#include <vector>
6#include <string> 6#include <string>
7#include <stack> 7#include <stack>
8#include <openssl/bio.h> 8#include <openssl/bio.h>
9#include <openssl/evp.h> 9#include <openssl/evp.h>
10#include <curl/curl.h> 10#include <curl/curl.h>
11#include "opkele/util.h" 11#include "opkele/util.h"
12#include "opkele/exception.h" 12#include "opkele/exception.h"
13 13
14namespace opkele { 14namespace opkele {
15 using namespace std; 15 using namespace std;
16 16
17 namespace util { 17 namespace util {
18 18
19 /* 19 /*
20 * base64 20 * base64
21 */ 21 */
22 string encode_base64(const void *data,size_t length) { 22 string encode_base64(const void *data,size_t length) {
23 BIO *b64 = 0, *bmem = 0; 23 BIO *b64 = 0, *bmem = 0;
24 try { 24 try {
25 b64 = BIO_new(BIO_f_base64()); 25 b64 = BIO_new(BIO_f_base64());
26 if(!b64) 26 if(!b64)
27 throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 encoder"); 27 throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 encoder");
28 BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); 28 BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
29 bmem = BIO_new(BIO_s_mem()); 29 bmem = BIO_new(BIO_s_mem());
30 BIO_set_flags(b64,BIO_CLOSE); 30 BIO_set_flags(b64,BIO_CLOSE);
31 if(!bmem) 31 if(!bmem)
32 throw exception_openssl(OPKELE_CP_ "failed to BIO_new() memory buffer"); 32 throw exception_openssl(OPKELE_CP_ "failed to BIO_new() memory buffer");
33 BIO_push(b64,bmem); 33 BIO_push(b64,bmem);
34 if(((size_t)BIO_write(b64,data,length))!=length) 34 if(((size_t)BIO_write(b64,data,length))!=length)
35 throw exception_openssl(OPKELE_CP_ "failed to BIO_write()"); 35 throw exception_openssl(OPKELE_CP_ "failed to BIO_write()");
36 if(BIO_flush(b64)!=1) 36 if(BIO_flush(b64)!=1)
37 throw exception_openssl(OPKELE_CP_ "failed to BIO_flush()"); 37 throw exception_openssl(OPKELE_CP_ "failed to BIO_flush()");
38 char *rvd; 38 char *rvd;
39 long rvl = BIO_get_mem_data(bmem,&rvd); 39 long rvl = BIO_get_mem_data(bmem,&rvd);
40 string rv(rvd,rvl); 40 string rv(rvd,rvl);
41 BIO_free_all(b64); 41 BIO_free_all(b64);
42 return rv; 42 return rv;
43 }catch(...) { 43 }catch(...) {
44 if(b64) BIO_free_all(b64); 44 if(b64) BIO_free_all(b64);
45 throw; 45 throw;
46 } 46 }
47 } 47 }
48 48
49 void decode_base64(const string& data,vector<unsigned char>& rv) { 49 void decode_base64(const string& data,vector<unsigned char>& rv) {
50 BIO *b64 = 0, *bmem = 0; 50 BIO *b64 = 0, *bmem = 0;
51 rv.clear(); 51 rv.clear();
52 try { 52 try {
53 bmem = BIO_new_mem_buf((void*)data.data(),data.size()); 53 bmem = BIO_new_mem_buf((void*)data.data(),data.size());
54 if(!bmem) 54 if(!bmem)
55 throw exception_openssl(OPKELE_CP_ "failed to BIO_new_mem_buf()"); 55 throw exception_openssl(OPKELE_CP_ "failed to BIO_new_mem_buf()");
56 b64 = BIO_new(BIO_f_base64()); 56 b64 = BIO_new(BIO_f_base64());
57 if(!b64) 57 if(!b64)
58 throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 decoder"); 58 throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 decoder");
59 BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); 59 BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
60 BIO_push(b64,bmem); 60 BIO_push(b64,bmem);
61 unsigned char tmp[512]; 61 unsigned char tmp[512];
62 size_t rb = 0; 62 size_t rb = 0;
63 while((rb=BIO_read(b64,tmp,sizeof(tmp)))>0) 63 while((rb=BIO_read(b64,tmp,sizeof(tmp)))>0)
64 rv.insert(rv.end(),tmp,&tmp[rb]); 64 rv.insert(rv.end(),tmp,&tmp[rb]);
65 BIO_free_all(b64); 65 BIO_free_all(b64);
66 }catch(...) { 66 }catch(...) {
67 if(b64) BIO_free_all(b64); 67 if(b64) BIO_free_all(b64);
68 throw; 68 throw;
69 } 69 }
70 } 70 }
71 71
72 /* 72 /*
73 * big numerics 73 * big numerics
74 */ 74 */
75 75
76 BIGNUM *base64_to_bignum(const string& b64) { 76 BIGNUM *base64_to_bignum(const string& b64) {
77 vector<unsigned char> bin; 77 vector<unsigned char> bin;
78 decode_base64(b64,bin); 78 decode_base64(b64,bin);
79 BIGNUM *rv = BN_bin2bn(&(bin.front()),bin.size(),0); 79 BIGNUM *rv = BN_bin2bn(&(bin.front()),bin.size(),0);
80 if(!rv) 80 if(!rv)
81 throw failed_conversion(OPKELE_CP_ "failed to BN_bin2bn()"); 81 throw failed_conversion(OPKELE_CP_ "failed to BN_bin2bn()");
82 return rv; 82 return rv;
83 } 83 }
84 84
85 BIGNUM *dec_to_bignum(const string& dec) { 85 BIGNUM *dec_to_bignum(const string& dec) {
86 BIGNUM *rv = 0; 86 BIGNUM *rv = 0;
87 if(!BN_dec2bn(&rv,dec.c_str())) 87 if(!BN_dec2bn(&rv,dec.c_str()))
88 throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()"); 88 throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()");
89 return rv; 89 return rv;
90 } 90 }
91 91
92 string bignum_to_base64(const BIGNUM *bn) { 92 string bignum_to_base64(const BIGNUM *bn) {
93 vector<unsigned char> bin(BN_num_bytes(bn)+1); 93 vector<unsigned char> bin(BN_num_bytes(bn)+1);
94 unsigned char *binptr = &(bin.front())+1; 94 unsigned char *binptr = &(bin.front())+1;
95 int l = BN_bn2bin(bn,binptr); 95 int l = BN_bn2bin(bn,binptr);
96 if(l && (*binptr)&0x80){ 96 if(l && (*binptr)&0x80){
97 (*(--binptr)) = 0; ++l; 97 (*(--binptr)) = 0; ++l;
98 } 98 }
99 return encode_base64(binptr,l); 99 return encode_base64(binptr,l);
100 } 100 }
101 101
102 /* 102 /*
103 * w3c times 103 * w3c times
104 */ 104 */
105 105
106 string time_to_w3c(time_t t) { 106 string time_to_w3c(time_t t) {
107 struct tm tm_t; 107 struct tm tm_t;
108 if(!gmtime_r(&t,&tm_t)) 108 if(!gmtime_r(&t,&tm_t))
109 throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()"); 109 throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()");
110 char rv[25]; 110 char rv[25];
111 if(!strftime(rv,sizeof(rv)-1,"%Y-%m-%dT%H:%M:%SZ",&tm_t)) 111 if(!strftime(rv,sizeof(rv)-1,"%Y-%m-%dT%H:%M:%SZ",&tm_t))
112 throw failed_conversion(OPKELE_CP_ "failed to strftime()"); 112 throw failed_conversion(OPKELE_CP_ "failed to strftime()");
113 return rv; 113 return rv;
114 } 114 }
115 115
116 time_t w3c_to_time(const string& w) { 116 time_t w3c_to_time(const string& w) {
117 struct tm tm_t; 117 struct tm tm_t;
118 memset(&tm_t,0,sizeof(tm_t)); 118 memset(&tm_t,0,sizeof(tm_t));
119 if( 119 if(
120 sscanf( 120 sscanf(
121 w.c_str(), 121 w.c_str(),
122 "%04d-%02d-%02dT%02d:%02d:%02dZ", 122 "%04d-%02d-%02dT%02d:%02d:%02dZ",
123 &tm_t.tm_year,&tm_t.tm_mon,&tm_t.tm_mday, 123 &tm_t.tm_year,&tm_t.tm_mon,&tm_t.tm_mday,
124 &tm_t.tm_hour,&tm_t.tm_min,&tm_t.tm_sec 124 &tm_t.tm_hour,&tm_t.tm_min,&tm_t.tm_sec
125 ) != 6 ) 125 ) != 6 )
126 throw failed_conversion(OPKELE_CP_ "failed to sscanf()"); 126 throw failed_conversion(OPKELE_CP_ "failed to sscanf()");
127 tm_t.tm_mon--; 127 tm_t.tm_mon--;
128 tm_t.tm_year-=1900; 128 tm_t.tm_year-=1900;
129 time_t rv = mktime(&tm_t); 129 time_t rv = mktime(&tm_t);
130 if(rv==(time_t)-1) 130 if(rv==(time_t)-1)
131 throw failed_conversion(OPKELE_CP_ "failed to mktime()"); 131 throw failed_conversion(OPKELE_CP_ "failed to mktime()");
132 return rv; 132 return rv;
133 } 133 }
134 134
135 /* 135 /*
136 * 136 *
137 */ 137 */
138 138
139 string url_encode(const string& str) { 139 string url_encode(const string& str) {
140 char * t = curl_escape(str.c_str(),str.length()); 140 char * t = curl_escape(str.c_str(),str.length());
141 if(!t) 141 if(!t)
142 throw failed_conversion(OPKELE_CP_ "failed to curl_escape()"); 142 throw failed_conversion(OPKELE_CP_ "failed to curl_escape()");
143 string rv(t); 143 string rv(t);
144 curl_free(t); 144 curl_free(t);
145 return rv; 145 return rv;
146 } 146 }
147 147
148 string long_to_string(long l) { 148 string long_to_string(long l) {
149 char rv[32]; 149 char rv[32];
150 int r=snprintf(rv,sizeof(rv),"%ld",l); 150 int r=snprintf(rv,sizeof(rv),"%ld",l);
151 if(r<0 || r>=(int)sizeof(rv)) 151 if(r<0 || r>=(int)sizeof(rv))
152 throw failed_conversion(OPKELE_CP_ "failed to snprintf()"); 152 throw failed_conversion(OPKELE_CP_ "failed to snprintf()");
153 return rv; 153 return rv;
154 } 154 }
155 155
156 long string_to_long(const string& s) { 156 long string_to_long(const string& s) {
157 char *endptr = 0; 157 char *endptr = 0;
158 long rv = strtol(s.c_str(),&endptr,10); 158 long rv = strtol(s.c_str(),&endptr,10);
159 if((!endptr) || endptr==s.c_str()) 159 if((!endptr) || endptr==s.c_str())
160 throw failed_conversion(OPKELE_CP_ "failed to strtol()"); 160 throw failed_conversion(OPKELE_CP_ "failed to strtol()");
161 return rv; 161 return rv;
162 } 162 }
163 163
164 /* 164 /*
165 * Normalize URL according to the rules, described in rfc 3986, section 6 165 * Normalize URL according to the rules, described in rfc 3986, section 6
166 * 166 *
167 * - uppercase hext triplets (e.g. %ab -> %AB) 167 * - uppercase hext triplets (e.g. %ab -> %AB)
168 * - lowercase scheme and host 168 * - lowercase scheme and host
169 * - decode %-encoded characters, specified as unreserved in rfc 3986, section 2.3, 169 * - decode %-encoded characters, specified as unreserved in rfc 3986, section 2.3,
170 * that is - [:alpha:][:digit:]._~- 170 * that is - [:alpha:][:digit:]._~-
171 * - remove dot segments 171 * - remove dot segments
172 * - remove empty and default ports 172 * - remove empty and default ports
173 * - if there's no path component, add '/' 173 * - if there's no path component, add '/'
174 */ 174 */
175 string rfc_3986_normalize_uri(const string& uri) { 175 string rfc_3986_normalize_uri(const string& uri) {
176 static const char *whitespace = " \t\r\n";
176 string rv; 177 string rv;
177 string::size_type colon = uri.find(':'); 178 string::size_type ns = uri.find_first_not_of(whitespace);
179 if(ns==string::npos)
180 throw bad_input(OPKELE_CP_ "Can't normalize empty URI");
181 string::size_type colon = uri.find(':',ns);
178 if(colon==string::npos) 182 if(colon==string::npos)
179 throw bad_input(OPKELE_CP_ "No scheme specified in URI"); 183 throw bad_input(OPKELE_CP_ "No scheme specified in URI");
180 transform( 184 transform(
181 uri.begin(), uri.begin()+colon+1, 185 uri.begin()+ns, uri.begin()+colon+1,
182 back_inserter(rv), ::tolower ); 186 back_inserter(rv), ::tolower );
183 bool s; 187 bool s;
184 if(rv=="http:") 188 if(rv=="http:")
185 s = false; 189 s = false;
186 else if(rv=="https:") 190 else if(rv=="https:")
187 s = true; 191 s = true;
192#ifndef NDEBUG
193 else if(rv=="file:")
194 s = false;
195#endif /* XXX: or try to make tests work some other way */
188 else 196 else
189 throw not_implemented(OPKELE_CP_ "Only http(s) URIs can be normalized here"); 197 throw not_implemented(OPKELE_CP_ "Only http(s) URIs can be normalized here");
190 string::size_type ul = uri.length(); 198 string::size_type ul = uri.find_last_not_of(whitespace)+1;
191 if(ul <= (colon+3)) 199 if(ul <= (colon+3))
192 throw bad_input(OPKELE_CP_ "Unexpected end of URI being normalized encountered"); 200 throw bad_input(OPKELE_CP_ "Unexpected end of URI being normalized encountered");
193 if(uri[colon+1]!='/' || uri[colon+2]!='/') 201 if(uri[colon+1]!='/' || uri[colon+2]!='/')
194 throw bad_input(OPKELE_CP_ "Unexpected input in URI being normalized after scheme component"); 202 throw bad_input(OPKELE_CP_ "Unexpected input in URI being normalized after scheme component");
195 rv += "//"; 203 rv += "//";
196 string::size_type interesting = uri.find_first_of(":/#?",colon+3); 204 string::size_type interesting = uri.find_first_of(":/#?",colon+3);
197 if(interesting==string::npos) { 205 if(interesting==string::npos) {
198 transform( 206 transform(
199 uri.begin()+colon+3,uri.end(), 207 uri.begin()+colon+3,uri.begin()+ul,
200 back_inserter(rv), ::tolower ); 208 back_inserter(rv), ::tolower );
201 rv += '/'; return rv; 209 rv += '/'; return rv;
202 } 210 }
203 transform( 211 transform(
204 uri.begin()+colon+3,uri.begin()+interesting, 212 uri.begin()+colon+3,uri.begin()+interesting,
205 back_inserter(rv), ::tolower ); 213 back_inserter(rv), ::tolower );
206 bool qf = false; 214 bool qf = false;
207 char ic = uri[interesting]; 215 char ic = uri[interesting];
208 if(ic==':') { 216 if(ic==':') {
209 string::size_type ni = uri.find_first_of("/#?%",interesting+1); 217 string::size_type ni = uri.find_first_of("/#?%",interesting+1);
210 const char *nptr = uri.data()+interesting+1; 218 const char *nptr = uri.data()+interesting+1;
211 char *eptr = 0; 219 char *eptr = 0;
212 long port = strtol(nptr,&eptr,10); 220 long port = strtol(nptr,&eptr,10);
213 if( (port>0) && (port<65535) && port!=(s?443:80) ) { 221 if( (port>0) && (port<65535) && port!=(s?443:80) ) {
214 char tmp[6]; 222 char tmp[6];
215 snprintf(tmp,sizeof(tmp),"%ld",port); 223 snprintf(tmp,sizeof(tmp),"%ld",port);
216 rv += ':'; rv += tmp; 224 rv += ':'; rv += tmp;
217 } 225 }
218 if(ni==string::npos) { 226 if(ni==string::npos) {
219 rv += '/'; return rv; 227 rv += '/'; return rv;
220 } 228 }
221 interesting = ni; 229 interesting = ni;
222 }else if(ic!='/') { 230 }else if(ic!='/') {
223 rv += '/'; rv += ic; 231 rv += '/'; rv += ic;
224 qf = true; 232 qf = true;
225 ++interesting; 233 ++interesting;
226 } 234 }
227 string::size_type n = interesting; 235 string::size_type n = interesting;
228 char tmp[3] = { 0,0,0 }; 236 char tmp[3] = { 0,0,0 };
229 stack<string::size_type> psegs; psegs.push(rv.length()); 237 stack<string::size_type> psegs; psegs.push(rv.length());
230 string pseg; 238 string pseg;
231 for(;n<ul;) { 239 for(;n<ul;) {
232 string::size_type unsafe = uri.find_first_of(qf?"%":"%/?#",n); 240 string::size_type unsafe = uri.find_first_of(qf?"%":"%/?#",n);
233 if(unsafe==string::npos) { 241 if(unsafe==string::npos) {
234 pseg.append(uri,n,ul-n-1); n = ul-1; 242 pseg.append(uri,n,ul-n-1); n = ul-1;
235 }else{ 243 }else{
236 pseg.append(uri,n,unsafe-n); 244 pseg.append(uri,n,unsafe-n);
237 n = unsafe; 245 n = unsafe;
238 } 246 }
239 char c = uri[n++]; 247 char c = uri[n++];
240 if(c=='%') { 248 if(c=='%') {
241 if((n+1)>=ul) 249 if((n+1)>=ul)
242 throw bad_input(OPKELE_CP_ "Unexpected end of URI encountered while parsing percent-encoded character"); 250 throw bad_input(OPKELE_CP_ "Unexpected end of URI encountered while parsing percent-encoded character");
243 tmp[0] = uri[n++]; 251 tmp[0] = uri[n++];
244 tmp[1] = uri[n++]; 252 tmp[1] = uri[n++];
245 if(!( isxdigit(tmp[0]) && isxdigit(tmp[1]) )) 253 if(!( isxdigit(tmp[0]) && isxdigit(tmp[1]) ))
246 throw bad_input(OPKELE_CP_ "Invalid percent-encoded character in URI being normalized"); 254 throw bad_input(OPKELE_CP_ "Invalid percent-encoded character in URI being normalized");
247 int cc = strtol(tmp,0,16); 255 int cc = strtol(tmp,0,16);
248 if( isalpha(cc) || isdigit(cc) || strchr("._~-",cc) ) 256 if( isalpha(cc) || isdigit(cc) || strchr("._~-",cc) )
249 pseg += cc; 257 pseg += cc;
250 else{ 258 else{
251 pseg += '%'; 259 pseg += '%';
252 pseg += toupper(tmp[0]); pseg += toupper(tmp[1]); 260 pseg += toupper(tmp[0]); pseg += toupper(tmp[1]);
253 } 261 }
254 }else if(qf) { 262 }else if(qf) {
255 rv += pseg; rv += c; 263 rv += pseg; rv += c;
256 pseg.clear(); 264 pseg.clear();
257 }else if(n>=ul || strchr("?/#",c)) { 265 }else if(n>=ul || strchr("?/#",c)) {
258 if(pseg.empty() || pseg==".") { 266 if(pseg.empty() || pseg==".") {
259 }else if(pseg=="..") { 267 }else if(pseg=="..") {
260 if(psegs.size()>1) { 268 if(psegs.size()>1) {
261 rv.resize(psegs.top()); psegs.pop(); 269 rv.resize(psegs.top()); psegs.pop();
262 } 270 }
263 }else{ 271 }else{
264 psegs.push(rv.length()); 272 psegs.push(rv.length());
265 if(c!='/') { 273 if(c!='/') {
266 pseg += c; 274 pseg += c;
267 qf = true; 275 qf = true;
268 } 276 }
269 rv += '/'; rv += pseg; 277 rv += '/'; rv += pseg;
270 } 278 }
271 if(c=='/' && (n>=ul || strchr("?#",uri[n])) ) { 279 if(c=='/' && (n>=ul || strchr("?#",uri[n])) ) {
272 rv += '/'; 280 rv += '/';
273 if(n<ul) 281 if(n<ul)
274 qf = true; 282 qf = true;
275 }else if(strchr("?#",c)) { 283 }else if(strchr("?#",c)) {
276 if(psegs.size()==1 && psegs.top()==rv.length()) 284 if(psegs.size()==1 && psegs.top()==rv.length())
277 rv += '/'; 285 rv += '/';
278 if(pseg.empty()) 286 if(pseg.empty())
279 rv += c; 287 rv += c;
280 qf = true; 288 qf = true;
281 } 289 }
282 pseg.clear(); 290 pseg.clear();
283 }else{ 291 }else{
284 pseg += c; 292 pseg += c;
285 } 293 }
286 } 294 }
287 if(!pseg.empty()) { 295 if(!pseg.empty()) {
288 rv += '/'; rv += pseg; 296 rv += '/'; rv += pseg;
289 } 297 }
290 return rv; 298 return rv;
291 } 299 }
292 300
293 } 301 }
294 302
295} 303}