author | Michael Krelin <hacker@klever.net> | 2007-12-02 21:48:18 (UTC) |
---|---|---|
committer | Michael Krelin <hacker@klever.net> | 2007-12-02 21:51:08 (UTC) |
commit | 262f1579f0a9138a01f06afea06d00155cefd4b5 (patch) (unidiff) | |
tree | fb4db0ee7b679a1957c63abbe6f6af1d2fa82531 /lib/util.cc | |
parent | 73d98f3652b498b9a74b183bef395714c7d73fda (diff) | |
download | libopkele-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>
-rw-r--r-- | lib/util.cc | 16 |
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 | ||
14 | namespace opkele { | 14 | namespace 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 | } |