author | Michael Krelin <hacker@klever.net> | 2007-12-30 21:08:55 (UTC) |
---|---|---|
committer | Michael Krelin <hacker@klever.net> | 2008-01-04 18:22:43 (UTC) |
commit | b7cec6ccef5d4178cf02c4cc240fd39ab7c8fe97 (patch) (unidiff) | |
tree | c898f449b4238a239f491a62bd77ac7c75663bb1 | |
parent | 4b75ac58aa9baaf611df8ef694fd585529823c66 (diff) | |
download | libopkele-b7cec6ccef5d4178cf02c4cc240fd39ab7c8fe97.zip libopkele-b7cec6ccef5d4178cf02c4cc240fd39ab7c8fe97.tar.gz libopkele-b7cec6ccef5d4178cf02c4cc240fd39ab7c8fe97.tar.bz2 |
parse ProviderID while doing discovery
Signed-off-by: Michael Krelin <hacker@klever.net>
-rw-r--r-- | include/opkele/types.h | 4 | ||||
-rw-r--r-- | lib/discovery.cc | 6 |
2 files changed, 10 insertions, 0 deletions
diff --git a/include/opkele/types.h b/include/opkele/types.h index d959021..4e1415f 100644 --- a/include/opkele/types.h +++ b/include/opkele/types.h | |||
@@ -1,242 +1,246 @@ | |||
1 | #ifndef __OPKELE_TYPES_H | 1 | #ifndef __OPKELE_TYPES_H |
2 | #define __OPKELE_TYPES_H | 2 | #define __OPKELE_TYPES_H |
3 | 3 | ||
4 | /** | 4 | /** |
5 | * @file | 5 | * @file |
6 | * @brief various types declarations | 6 | * @brief various types declarations |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <ostream> | 9 | #include <ostream> |
10 | #include <vector> | 10 | #include <vector> |
11 | #include <string> | 11 | #include <string> |
12 | #include <map> | 12 | #include <map> |
13 | #include <set> | 13 | #include <set> |
14 | #include <opkele/tr1-mem.h> | 14 | #include <opkele/tr1-mem.h> |
15 | 15 | ||
16 | namespace opkele { | 16 | namespace opkele { |
17 | using std::vector; | 17 | using std::vector; |
18 | using std::string; | 18 | using std::string; |
19 | using std::map; | 19 | using std::map; |
20 | using std::ostream; | 20 | using std::ostream; |
21 | using std::multimap; | 21 | using std::multimap; |
22 | using std::set; | 22 | using std::set; |
23 | 23 | ||
24 | /** | 24 | /** |
25 | * the OpenID operation mode | 25 | * the OpenID operation mode |
26 | */ | 26 | */ |
27 | typedef enum _mode_t { | 27 | typedef enum _mode_t { |
28 | mode_associate, | 28 | mode_associate, |
29 | mode_checkid_immediate, | 29 | mode_checkid_immediate, |
30 | mode_checkid_setup, | 30 | mode_checkid_setup, |
31 | mode_check_association | 31 | mode_check_association |
32 | } mode_t; | 32 | } mode_t; |
33 | 33 | ||
34 | /** | 34 | /** |
35 | * the association secret container | 35 | * the association secret container |
36 | */ | 36 | */ |
37 | class secret_t : public vector<unsigned char> { | 37 | class secret_t : public vector<unsigned char> { |
38 | public: | 38 | public: |
39 | 39 | ||
40 | /** | 40 | /** |
41 | * xor the secret and hmac together and encode, using base64 | 41 | * xor the secret and hmac together and encode, using base64 |
42 | * @param key_d pointer to the message digest | 42 | * @param key_d pointer to the message digest |
43 | * @param rv reference to the return value | 43 | * @param rv reference to the return value |
44 | */ | 44 | */ |
45 | void enxor_to_base64(const unsigned char *key_d,string& rv) const; | 45 | void enxor_to_base64(const unsigned char *key_d,string& rv) const; |
46 | /** | 46 | /** |
47 | * decode base64-encoded secret and xor it with the message digest | 47 | * decode base64-encoded secret and xor it with the message digest |
48 | * @param key_d pointer to the message digest | 48 | * @param key_d pointer to the message digest |
49 | * @param b64 base64-encoded secret value | 49 | * @param b64 base64-encoded secret value |
50 | */ | 50 | */ |
51 | void enxor_from_base64(const unsigned char *key_d,const string& b64); | 51 | void enxor_from_base64(const unsigned char *key_d,const string& b64); |
52 | /** | 52 | /** |
53 | * plainly encode to base64 representation | 53 | * plainly encode to base64 representation |
54 | * @param rv reference to the return value | 54 | * @param rv reference to the return value |
55 | */ | 55 | */ |
56 | void to_base64(string& rv) const; | 56 | void to_base64(string& rv) const; |
57 | /** | 57 | /** |
58 | * decode cleartext secret from base64 | 58 | * decode cleartext secret from base64 |
59 | * @param b64 base64-encoded representation of the secret value | 59 | * @param b64 base64-encoded representation of the secret value |
60 | */ | 60 | */ |
61 | void from_base64(const string& b64); | 61 | void from_base64(const string& b64); |
62 | }; | 62 | }; |
63 | 63 | ||
64 | /** | 64 | /** |
65 | * Interface to the association. | 65 | * Interface to the association. |
66 | */ | 66 | */ |
67 | class association_t { | 67 | class association_t { |
68 | public: | 68 | public: |
69 | 69 | ||
70 | virtual ~association_t() { } | 70 | virtual ~association_t() { } |
71 | 71 | ||
72 | /** | 72 | /** |
73 | * retrieve the server with which association was established. | 73 | * retrieve the server with which association was established. |
74 | * @return server name | 74 | * @return server name |
75 | */ | 75 | */ |
76 | virtual string server() const = 0; | 76 | virtual string server() const = 0; |
77 | /** | 77 | /** |
78 | * retrieve the association handle. | 78 | * retrieve the association handle. |
79 | * @return handle | 79 | * @return handle |
80 | */ | 80 | */ |
81 | virtual string handle() const = 0; | 81 | virtual string handle() const = 0; |
82 | /** | 82 | /** |
83 | * retrieve the association type. | 83 | * retrieve the association type. |
84 | * @return association type | 84 | * @return association type |
85 | */ | 85 | */ |
86 | virtual string assoc_type() const = 0; | 86 | virtual string assoc_type() const = 0; |
87 | /** | 87 | /** |
88 | * retrieve the association secret. | 88 | * retrieve the association secret. |
89 | * @return association secret | 89 | * @return association secret |
90 | */ | 90 | */ |
91 | virtual secret_t secret() const = 0; | 91 | virtual secret_t secret() const = 0; |
92 | /** | 92 | /** |
93 | * retrieve the number of seconds the association expires in. | 93 | * retrieve the number of seconds the association expires in. |
94 | * @return seconds till expiration | 94 | * @return seconds till expiration |
95 | */ | 95 | */ |
96 | virtual int expires_in() const = 0; | 96 | virtual int expires_in() const = 0; |
97 | /** | 97 | /** |
98 | * check whether the association is stateless. | 98 | * check whether the association is stateless. |
99 | * @return true if stateless | 99 | * @return true if stateless |
100 | */ | 100 | */ |
101 | virtual bool stateless() const = 0; | 101 | virtual bool stateless() const = 0; |
102 | /** | 102 | /** |
103 | * check whether the association is expired. | 103 | * check whether the association is expired. |
104 | * @return true if expired | 104 | * @return true if expired |
105 | */ | 105 | */ |
106 | virtual bool is_expired() const = 0; | 106 | virtual bool is_expired() const = 0; |
107 | }; | 107 | }; |
108 | 108 | ||
109 | /** | 109 | /** |
110 | * the shared_ptr<> for association_t object type | 110 | * the shared_ptr<> for association_t object type |
111 | */ | 111 | */ |
112 | typedef tr1mem::shared_ptr<association_t> assoc_t; | 112 | typedef tr1mem::shared_ptr<association_t> assoc_t; |
113 | 113 | ||
114 | /** | 114 | /** |
115 | * request/response parameters map | 115 | * request/response parameters map |
116 | */ | 116 | */ |
117 | class params_t : public map<string,string> { | 117 | class params_t : public map<string,string> { |
118 | public: | 118 | public: |
119 | 119 | ||
120 | /** | 120 | /** |
121 | * check whether the parameter is present. | 121 | * check whether the parameter is present. |
122 | * @param n the parameter name | 122 | * @param n the parameter name |
123 | * @return true if yes | 123 | * @return true if yes |
124 | */ | 124 | */ |
125 | bool has_param(const string& n) const; | 125 | bool has_param(const string& n) const; |
126 | /** | 126 | /** |
127 | * retrieve the parameter (const version) | 127 | * retrieve the parameter (const version) |
128 | * @param n the parameter name | 128 | * @param n the parameter name |
129 | * @return the parameter value | 129 | * @return the parameter value |
130 | * @throw failed_lookup if there is no such parameter | 130 | * @throw failed_lookup if there is no such parameter |
131 | */ | 131 | */ |
132 | const string& get_param(const string& n) const; | 132 | const string& get_param(const string& n) const; |
133 | /** | 133 | /** |
134 | * retrieve the parameter. | 134 | * retrieve the parameter. |
135 | * @param n the parameter name | 135 | * @param n the parameter name |
136 | * @return the parameter value | 136 | * @return the parameter value |
137 | * @throw failed_lookup if there is no such parameter | 137 | * @throw failed_lookup if there is no such parameter |
138 | */ | 138 | */ |
139 | string& get_param(const string& n); | 139 | string& get_param(const string& n); |
140 | 140 | ||
141 | /** | 141 | /** |
142 | * parse the OpenID key/value data. | 142 | * parse the OpenID key/value data. |
143 | * @param kv the OpenID key/value data | 143 | * @param kv the OpenID key/value data |
144 | */ | 144 | */ |
145 | void parse_keyvalues(const string& kv); | 145 | void parse_keyvalues(const string& kv); |
146 | /** | 146 | /** |
147 | * sign the fields. | 147 | * sign the fields. |
148 | * @param secret the secret used for signing | 148 | * @param secret the secret used for signing |
149 | * @param sig reference to the string, containing base64-encoded | 149 | * @param sig reference to the string, containing base64-encoded |
150 | * result | 150 | * result |
151 | * @param slist the comma-separated list of fields to sign | 151 | * @param slist the comma-separated list of fields to sign |
152 | * @param prefix the string to prepend to parameter names | 152 | * @param prefix the string to prepend to parameter names |
153 | */ | 153 | */ |
154 | void sign(secret_t secret,string& sig,const string& slist,const char *prefix=0) const; | 154 | void sign(secret_t secret,string& sig,const string& slist,const char *prefix=0) const; |
155 | 155 | ||
156 | /** | 156 | /** |
157 | * append parameters to the URL as a GET-request parameters. | 157 | * append parameters to the URL as a GET-request parameters. |
158 | * @param url the base URL | 158 | * @param url the base URL |
159 | * @param prefix the string to prepend to parameter names | 159 | * @param prefix the string to prepend to parameter names |
160 | * @return the ready-to-use location | 160 | * @return the ready-to-use location |
161 | */ | 161 | */ |
162 | string append_query(const string& url,const char *prefix = "openid.") const; | 162 | string append_query(const string& url,const char *prefix = "openid.") const; |
163 | 163 | ||
164 | /** | 164 | /** |
165 | * make up a query string suitable for use in GET and POST | 165 | * make up a query string suitable for use in GET and POST |
166 | * requests. | 166 | * requests. |
167 | * @param prefix string to prened to parameter names | 167 | * @param prefix string to prened to parameter names |
168 | * @return query string | 168 | * @return query string |
169 | */ | 169 | */ |
170 | string query_string(const char *prefix = "openid.") const; | 170 | string query_string(const char *prefix = "openid.") const; |
171 | }; | 171 | }; |
172 | 172 | ||
173 | /** | 173 | /** |
174 | * dump the key/value pairs for the parameters to the stream. | 174 | * dump the key/value pairs for the parameters to the stream. |
175 | * @param o output stream | 175 | * @param o output stream |
176 | * @param p the parameters | 176 | * @param p the parameters |
177 | */ | 177 | */ |
178 | ostream& operator << (ostream& o,const params_t& p); | 178 | ostream& operator << (ostream& o,const params_t& p); |
179 | 179 | ||
180 | namespace xrd { | 180 | namespace xrd { |
181 | 181 | ||
182 | struct priority_compare { | 182 | struct priority_compare { |
183 | inline bool operator()(long a,long b) const { | 183 | inline bool operator()(long a,long b) const { |
184 | return (a<0) ? false : (b<0) ? false : (a<b); | 184 | return (a<0) ? false : (b<0) ? false : (a<b); |
185 | } | 185 | } |
186 | }; | 186 | }; |
187 | 187 | ||
188 | template <typename _DT> | 188 | template <typename _DT> |
189 | class priority_map : public multimap<long,_DT,priority_compare> { | 189 | class priority_map : public multimap<long,_DT,priority_compare> { |
190 | typedef multimap<long,_DT,priority_compare> map_type; | 190 | typedef multimap<long,_DT,priority_compare> map_type; |
191 | public: | 191 | public: |
192 | 192 | ||
193 | inline _DT& add(long priority,const _DT& d) { | 193 | inline _DT& add(long priority,const _DT& d) { |
194 | return insert(typename map_type::value_type(priority,d))->second; | 194 | return insert(typename map_type::value_type(priority,d))->second; |
195 | } | 195 | } |
196 | }; | 196 | }; |
197 | 197 | ||
198 | typedef priority_map<string> canonical_ids_t; | 198 | typedef priority_map<string> canonical_ids_t; |
199 | typedef priority_map<string> local_ids_t; | 199 | typedef priority_map<string> local_ids_t; |
200 | typedef set<string> types_t; | 200 | typedef set<string> types_t; |
201 | typedef priority_map<string> uris_t; | 201 | typedef priority_map<string> uris_t; |
202 | 202 | ||
203 | class service_t { | 203 | class service_t { |
204 | public: | 204 | public: |
205 | types_t types; | 205 | types_t types; |
206 | uris_t uris; | 206 | uris_t uris; |
207 | local_ids_t local_ids; | 207 | local_ids_t local_ids; |
208 | string provider_id; | ||
208 | 209 | ||
209 | void clear() { | 210 | void clear() { |
210 | types.clear(); | 211 | types.clear(); |
211 | uris.clear(); local_ids.clear(); | 212 | uris.clear(); local_ids.clear(); |
213 | provider_id.clear(); | ||
212 | } | 214 | } |
213 | }; | 215 | }; |
214 | typedef priority_map<service_t> services_t; | 216 | typedef priority_map<service_t> services_t; |
215 | 217 | ||
216 | class XRD_t { | 218 | class XRD_t { |
217 | public: | 219 | public: |
218 | time_t expires; | 220 | time_t expires; |
219 | 221 | ||
220 | canonical_ids_t canonical_ids; | 222 | canonical_ids_t canonical_ids; |
221 | local_ids_t local_ids; | 223 | local_ids_t local_ids; |
222 | services_t services; | 224 | services_t services; |
225 | string provider_id; | ||
223 | 226 | ||
224 | void clear() { | 227 | void clear() { |
225 | expires = 0; | 228 | expires = 0; |
226 | canonical_ids.clear(); local_ids.clear(); | 229 | canonical_ids.clear(); local_ids.clear(); |
227 | services.clear(); | 230 | services.clear(); |
231 | provider_id.clear(); | ||
228 | } | 232 | } |
229 | bool empty() const { | 233 | bool empty() const { |
230 | return | 234 | return |
231 | canonical_ids.empty() | 235 | canonical_ids.empty() |
232 | && local_ids.empty() | 236 | && local_ids.empty() |
233 | && services.empty(); | 237 | && services.empty(); |
234 | } | 238 | } |
235 | 239 | ||
236 | }; | 240 | }; |
237 | 241 | ||
238 | } | 242 | } |
239 | 243 | ||
240 | } | 244 | } |
241 | 245 | ||
242 | #endif /* __OPKELE_TYPES_H */ | 246 | #endif /* __OPKELE_TYPES_H */ |
diff --git a/lib/discovery.cc b/lib/discovery.cc index bc7d6fb..81727c0 100644 --- a/lib/discovery.cc +++ b/lib/discovery.cc | |||
@@ -1,382 +1,388 @@ | |||
1 | #include <list> | 1 | #include <list> |
2 | #include <opkele/curl.h> | 2 | #include <opkele/curl.h> |
3 | #include <opkele/expat.h> | 3 | #include <opkele/expat.h> |
4 | #include <opkele/uris.h> | 4 | #include <opkele/uris.h> |
5 | #include <opkele/discovery.h> | 5 | #include <opkele/discovery.h> |
6 | #include <opkele/exception.h> | 6 | #include <opkele/exception.h> |
7 | #include <opkele/util.h> | 7 | #include <opkele/util.h> |
8 | 8 | ||
9 | #include "config.h" | 9 | #include "config.h" |
10 | 10 | ||
11 | #define XRDS_HEADER "X-XRDS-Location" | 11 | #define XRDS_HEADER "X-XRDS-Location" |
12 | #define CT_HEADER "Content-Type" | 12 | #define CT_HEADER "Content-Type" |
13 | 13 | ||
14 | namespace opkele { | 14 | namespace opkele { |
15 | using std::list; | 15 | using std::list; |
16 | using xrd::XRD_t; | 16 | using xrd::XRD_t; |
17 | using xrd::service_t; | 17 | using xrd::service_t; |
18 | 18 | ||
19 | static const char *whitespace = " \t\r\n"; | 19 | static const char *whitespace = " \t\r\n"; |
20 | static const char *i_leaders = "=@+$!("; | 20 | static const char *i_leaders = "=@+$!("; |
21 | 21 | ||
22 | static inline bool is_qelement(const XML_Char *n,const char *qen) { | 22 | static inline bool is_qelement(const XML_Char *n,const char *qen) { |
23 | return !strcasecmp(n,qen); | 23 | return !strcasecmp(n,qen); |
24 | } | 24 | } |
25 | static inline bool is_element(const XML_Char *n,const char *en) { | 25 | static inline bool is_element(const XML_Char *n,const char *en) { |
26 | if(!strcasecmp(n,en)) return true; | 26 | if(!strcasecmp(n,en)) return true; |
27 | int nl = strlen(n), enl = strlen(en); | 27 | int nl = strlen(n), enl = strlen(en); |
28 | if( (nl>=(enl+1)) && n[nl-enl-1]=='\t' | 28 | if( (nl>=(enl+1)) && n[nl-enl-1]=='\t' |
29 | && !strcasecmp(&n[nl-enl],en) ) | 29 | && !strcasecmp(&n[nl-enl],en) ) |
30 | return true; | 30 | return true; |
31 | return false; | 31 | return false; |
32 | } | 32 | } |
33 | 33 | ||
34 | static long element_priority(const XML_Char **a) { | 34 | static long element_priority(const XML_Char **a) { |
35 | for(;*a;++a) | 35 | for(;*a;++a) |
36 | if(!strcasecmp(*(a++),"priority")) { | 36 | if(!strcasecmp(*(a++),"priority")) { |
37 | long rv; | 37 | long rv; |
38 | return (sscanf(*a,"%ld",&rv)==1)?rv:-1; | 38 | return (sscanf(*a,"%ld",&rv)==1)?rv:-1; |
39 | } | 39 | } |
40 | return -1; | 40 | return -1; |
41 | } | 41 | } |
42 | 42 | ||
43 | class idigger_t : public util::curl_t, public util::expat_t { | 43 | class idigger_t : public util::curl_t, public util::expat_t { |
44 | public: | 44 | public: |
45 | string xri_proxy; | 45 | string xri_proxy; |
46 | 46 | ||
47 | enum { | 47 | enum { |
48 | xmode_html = 1, xmode_xrd = 2 | 48 | xmode_html = 1, xmode_xrd = 2 |
49 | }; | 49 | }; |
50 | int xmode; | 50 | int xmode; |
51 | 51 | ||
52 | string xrds_location; | 52 | string xrds_location; |
53 | string http_content_type; | 53 | string http_content_type; |
54 | service_t html_openid1; | 54 | service_t html_openid1; |
55 | service_t html_openid2; | 55 | service_t html_openid2; |
56 | string cdata_buf; | 56 | string cdata_buf; |
57 | long status_code; | 57 | long status_code; |
58 | string status_string; | 58 | string status_string; |
59 | 59 | ||
60 | typedef list<string> pt_stack_t; | 60 | typedef list<string> pt_stack_t; |
61 | pt_stack_t pt_stack; | 61 | pt_stack_t pt_stack; |
62 | int skipping; | 62 | int skipping; |
63 | 63 | ||
64 | XRD_t *xrd; | 64 | XRD_t *xrd; |
65 | service_t *xrd_service; | 65 | service_t *xrd_service; |
66 | string* cdata; | 66 | string* cdata; |
67 | 67 | ||
68 | idigger_t() | 68 | idigger_t() |
69 | : util::curl_t(easy_init()), | 69 | : util::curl_t(easy_init()), |
70 | util::expat_t(0), | 70 | util::expat_t(0), |
71 | xri_proxy(XRI_PROXY_URL) { | 71 | xri_proxy(XRI_PROXY_URL) { |
72 | CURLcode r; | 72 | CURLcode r; |
73 | (r=misc_sets()) | 73 | (r=misc_sets()) |
74 | || (r=set_write()) | 74 | || (r=set_write()) |
75 | || (r=set_header()) | 75 | || (r=set_header()) |
76 | ; | 76 | ; |
77 | if(r) | 77 | if(r) |
78 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | 78 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); |
79 | } | 79 | } |
80 | ~idigger_t() throw() { } | 80 | ~idigger_t() throw() { } |
81 | 81 | ||
82 | void discover(idiscovery_t& result,const string& identity) { | 82 | void discover(idiscovery_t& result,const string& identity) { |
83 | result.clear(); | 83 | result.clear(); |
84 | string::size_type fsc = identity.find_first_not_of(whitespace); | 84 | string::size_type fsc = identity.find_first_not_of(whitespace); |
85 | if(fsc==string::npos) | 85 | if(fsc==string::npos) |
86 | throw bad_input(OPKELE_CP_ "whtiespace-only identity"); | 86 | throw bad_input(OPKELE_CP_ "whtiespace-only identity"); |
87 | string::size_type lsc = identity.find_last_not_of(whitespace); | 87 | string::size_type lsc = identity.find_last_not_of(whitespace); |
88 | assert(lsc!=string::npos); | 88 | assert(lsc!=string::npos); |
89 | if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1)) | 89 | if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1)) |
90 | fsc += sizeof("xri://")-1; | 90 | fsc += sizeof("xri://")-1; |
91 | if((fsc+1)>=lsc) | 91 | if((fsc+1)>=lsc) |
92 | throw bad_input(OPKELE_CP_ "not a character of importance in identity"); | 92 | throw bad_input(OPKELE_CP_ "not a character of importance in identity"); |
93 | string id(identity,fsc,lsc-fsc+1); | 93 | string id(identity,fsc,lsc-fsc+1); |
94 | if(strchr(i_leaders,id[0])) { | 94 | if(strchr(i_leaders,id[0])) { |
95 | result.normalized_id = id; | 95 | result.normalized_id = id; |
96 | result.xri_identity = true; | 96 | result.xri_identity = true; |
97 | /* TODO: further canonicalize xri identity? Like folding case or whatever... */ | 97 | /* TODO: further canonicalize xri identity? Like folding case or whatever... */ |
98 | discover_at( | 98 | discover_at( |
99 | result, | 99 | result, |
100 | xri_proxy + util::url_encode(id)+ | 100 | xri_proxy + util::url_encode(id)+ |
101 | "?_xrd_r=application/xrd+xml;sep=false", xmode_xrd); | 101 | "?_xrd_r=application/xrd+xml;sep=false", xmode_xrd); |
102 | if(status_code!=100) | 102 | if(status_code!=100) |
103 | throw failed_xri_resolution(OPKELE_CP_ | 103 | throw failed_xri_resolution(OPKELE_CP_ |
104 | "XRI resolution failed with '"+status_string+"' message",status_code); | 104 | "XRI resolution failed with '"+status_string+"' message",status_code); |
105 | if(result.xrd.canonical_ids.empty()) | 105 | if(result.xrd.canonical_ids.empty()) |
106 | throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID for XRI identity found"); | 106 | throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID for XRI identity found"); |
107 | result.canonicalized_id = result.xrd.canonical_ids.begin()->second; | 107 | result.canonicalized_id = result.xrd.canonical_ids.begin()->second; |
108 | }else{ | 108 | }else{ |
109 | result.xri_identity = false; | 109 | result.xri_identity = false; |
110 | if(id.find("://")==string::npos) | 110 | if(id.find("://")==string::npos) |
111 | id.insert(0,"http://"); | 111 | id.insert(0,"http://"); |
112 | string::size_type fp = id.find('#'); | 112 | string::size_type fp = id.find('#'); |
113 | if(fp!=string::npos) { | 113 | if(fp!=string::npos) { |
114 | string::size_type qp = id.find('?'); | 114 | string::size_type qp = id.find('?'); |
115 | if(qp==string::npos || qp<fp) | 115 | if(qp==string::npos || qp<fp) |
116 | id.erase(fp); | 116 | id.erase(fp); |
117 | else if(qp>fp) | 117 | else if(qp>fp) |
118 | id.erase(fp,qp-fp); | 118 | id.erase(fp,qp-fp); |
119 | } | 119 | } |
120 | result.normalized_id = util::rfc_3986_normalize_uri(id); | 120 | result.normalized_id = util::rfc_3986_normalize_uri(id); |
121 | discover_at(result,id,xmode_html|xmode_xrd); | 121 | discover_at(result,id,xmode_html|xmode_xrd); |
122 | const char * eu = 0; | 122 | const char * eu = 0; |
123 | CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); | 123 | CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); |
124 | if(r) | 124 | if(r) |
125 | throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); | 125 | throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); |
126 | result.canonicalized_id = util::rfc_3986_normalize_uri(eu); /* XXX: strip fragment part? */ | 126 | result.canonicalized_id = util::rfc_3986_normalize_uri(eu); /* XXX: strip fragment part? */ |
127 | if(xrds_location.empty()) { | 127 | if(xrds_location.empty()) { |
128 | html2xrd(result.xrd); | 128 | html2xrd(result.xrd); |
129 | }else{ | 129 | }else{ |
130 | discover_at(result,xrds_location,xmode_xrd); | 130 | discover_at(result,xrds_location,xmode_xrd); |
131 | if(result.xrd.empty()) | 131 | if(result.xrd.empty()) |
132 | html2xrd(result.xrd); | 132 | html2xrd(result.xrd); |
133 | } | 133 | } |
134 | } | 134 | } |
135 | } | 135 | } |
136 | 136 | ||
137 | void discover_at(idiscovery_t& result,const string& url,int xm) { | 137 | void discover_at(idiscovery_t& result,const string& url,int xm) { |
138 | CURLcode r = easy_setopt(CURLOPT_URL,url.c_str()); | 138 | CURLcode r = easy_setopt(CURLOPT_URL,url.c_str()); |
139 | if(r) | 139 | if(r) |
140 | throw exception_curl(OPKELE_CP_ "failed to set culry urlie",r); | 140 | throw exception_curl(OPKELE_CP_ "failed to set culry urlie",r); |
141 | 141 | ||
142 | (*(expat_t*)this) = parser_create_ns(); | 142 | (*(expat_t*)this) = parser_create_ns(); |
143 | set_user_data(); set_element_handler(); | 143 | set_user_data(); set_element_handler(); |
144 | set_character_data_handler(); | 144 | set_character_data_handler(); |
145 | 145 | ||
146 | http_content_type.clear(); | 146 | http_content_type.clear(); |
147 | xmode = xm; | 147 | xmode = xm; |
148 | if(xmode&xmode_html) { | 148 | if(xmode&xmode_html) { |
149 | xrds_location.clear(); | 149 | xrds_location.clear(); |
150 | html_openid1.clear(); html_openid2.clear(); | 150 | html_openid1.clear(); html_openid2.clear(); |
151 | } | 151 | } |
152 | xrd = &result.xrd; | 152 | xrd = &result.xrd; |
153 | cdata = 0; xrd_service = 0; skipping = 0; | 153 | cdata = 0; xrd_service = 0; skipping = 0; |
154 | status_code = 100; status_string.clear(); | 154 | status_code = 100; status_string.clear(); |
155 | 155 | ||
156 | r = easy_perform(); | 156 | r = easy_perform(); |
157 | if(r && r!=CURLE_WRITE_ERROR) | 157 | if(r && r!=CURLE_WRITE_ERROR) |
158 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | 158 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); |
159 | 159 | ||
160 | parse(0,0,true); | 160 | parse(0,0,true); |
161 | } | 161 | } |
162 | 162 | ||
163 | void html2xrd(XRD_t& x) { | 163 | void html2xrd(XRD_t& x) { |
164 | if(!html_openid1.uris.empty()) { | 164 | if(!html_openid1.uris.empty()) { |
165 | html_openid1.types.insert(STURI_OPENID11); | 165 | html_openid1.types.insert(STURI_OPENID11); |
166 | x.services.add(-1,html_openid1); | 166 | x.services.add(-1,html_openid1); |
167 | } | 167 | } |
168 | if(!html_openid2.uris.empty()) { | 168 | if(!html_openid2.uris.empty()) { |
169 | html_openid2.types.insert(STURI_OPENID20); | 169 | html_openid2.types.insert(STURI_OPENID20); |
170 | x.services.add(-1,html_openid2); | 170 | x.services.add(-1,html_openid2); |
171 | } | 171 | } |
172 | } | 172 | } |
173 | 173 | ||
174 | size_t write(void *p,size_t s,size_t nm) { | 174 | size_t write(void *p,size_t s,size_t nm) { |
175 | if(skipping<0) return 0; | 175 | if(skipping<0) return 0; |
176 | /* TODO: limit total size */ | 176 | /* TODO: limit total size */ |
177 | size_t bytes = s*nm; | 177 | size_t bytes = s*nm; |
178 | parse((const char *)p,bytes,false); | 178 | parse((const char *)p,bytes,false); |
179 | return bytes; | 179 | return bytes; |
180 | } | 180 | } |
181 | size_t header(void *p,size_t s,size_t nm) { | 181 | size_t header(void *p,size_t s,size_t nm) { |
182 | size_t bytes = s*nm; | 182 | size_t bytes = s*nm; |
183 | const char *h = (const char*)p; | 183 | const char *h = (const char*)p; |
184 | const char *colon = (const char*)memchr(p,':',bytes); | 184 | const char *colon = (const char*)memchr(p,':',bytes); |
185 | const char *space = (const char*)memchr(p,' ',bytes); | 185 | const char *space = (const char*)memchr(p,' ',bytes); |
186 | if(space && ( (!colon) || space<colon ) ) { | 186 | if(space && ( (!colon) || space<colon ) ) { |
187 | xrds_location.clear(); http_content_type.clear(); | 187 | xrds_location.clear(); http_content_type.clear(); |
188 | }else if(colon) { | 188 | }else if(colon) { |
189 | const char *hv = ++colon; | 189 | const char *hv = ++colon; |
190 | int hnl = colon-h; | 190 | int hnl = colon-h; |
191 | int rb; | 191 | int rb; |
192 | for(rb = bytes-hnl-1;rb>0 && isspace(*hv);++hv,--rb); | 192 | for(rb = bytes-hnl-1;rb>0 && isspace(*hv);++hv,--rb); |
193 | while(rb>0 && isspace(hv[rb-1])) --rb; | 193 | while(rb>0 && isspace(hv[rb-1])) --rb; |
194 | if(rb) { | 194 | if(rb) { |
195 | if( (hnl>=sizeof(XRDS_HEADER)) | 195 | if( (hnl>=sizeof(XRDS_HEADER)) |
196 | && !strncasecmp(h,XRDS_HEADER":", | 196 | && !strncasecmp(h,XRDS_HEADER":", |
197 | sizeof(XRDS_HEADER)) ) { | 197 | sizeof(XRDS_HEADER)) ) { |
198 | xrds_location.assign(hv,rb); | 198 | xrds_location.assign(hv,rb); |
199 | }else if( (hnl>=sizeof(CT_HEADER)) | 199 | }else if( (hnl>=sizeof(CT_HEADER)) |
200 | && !strncasecmp(h,CT_HEADER":", | 200 | && !strncasecmp(h,CT_HEADER":", |
201 | sizeof(CT_HEADER)) ) { | 201 | sizeof(CT_HEADER)) ) { |
202 | const char *sc = (const char*)memchr( | 202 | const char *sc = (const char*)memchr( |
203 | hv,';',rb); | 203 | hv,';',rb); |
204 | http_content_type.assign(hv,sc?(sc-hv):rb); | 204 | http_content_type.assign(hv,sc?(sc-hv):rb); |
205 | } | 205 | } |
206 | } | 206 | } |
207 | } | 207 | } |
208 | return curl_t::header(p,s,nm); | 208 | return curl_t::header(p,s,nm); |
209 | } | 209 | } |
210 | 210 | ||
211 | void start_element(const XML_Char *n,const XML_Char **a) { | 211 | void start_element(const XML_Char *n,const XML_Char **a) { |
212 | if(skipping<0) return; | 212 | if(skipping<0) return; |
213 | if(skipping) { | 213 | if(skipping) { |
214 | if(xmode&xmode_html) | 214 | if(xmode&xmode_html) |
215 | html_start_element(n,a); | 215 | html_start_element(n,a); |
216 | ++skipping; return; | 216 | ++skipping; return; |
217 | } | 217 | } |
218 | if(pt_stack.empty()) { | 218 | if(pt_stack.empty()) { |
219 | if(is_qelement(n,NSURI_XRDS "\tXRDS")) | 219 | if(is_qelement(n,NSURI_XRDS "\tXRDS")) |
220 | return; | 220 | return; |
221 | if(is_qelement(n,NSURI_XRD "\tXRD")) { | 221 | if(is_qelement(n,NSURI_XRD "\tXRD")) { |
222 | assert(xrd); | 222 | assert(xrd); |
223 | xrd->clear(); | 223 | xrd->clear(); |
224 | pt_stack.push_back(n); | 224 | pt_stack.push_back(n); |
225 | }else if(xmode&xmode_html) { | 225 | }else if(xmode&xmode_html) { |
226 | html_start_element(n,a); | 226 | html_start_element(n,a); |
227 | }else{ | 227 | }else{ |
228 | skipping = -1; | 228 | skipping = -1; |
229 | } | 229 | } |
230 | }else{ | 230 | }else{ |
231 | int pt_s = pt_stack.size(); | 231 | int pt_s = pt_stack.size(); |
232 | if(pt_s==1) { | 232 | if(pt_s==1) { |
233 | if(is_qelement(n,NSURI_XRD "\tCanonicalID")) { | 233 | if(is_qelement(n,NSURI_XRD "\tCanonicalID")) { |
234 | assert(xrd); | 234 | assert(xrd); |
235 | cdata = &(xrd->canonical_ids.add(element_priority(a),string())); | 235 | cdata = &(xrd->canonical_ids.add(element_priority(a),string())); |
236 | }else if(is_qelement(n,NSURI_XRD "\tLocalID")) { | 236 | }else if(is_qelement(n,NSURI_XRD "\tLocalID")) { |
237 | assert(xrd); | 237 | assert(xrd); |
238 | cdata = &(xrd->local_ids.add(element_priority(a),string())); | 238 | cdata = &(xrd->local_ids.add(element_priority(a),string())); |
239 | }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { | ||
240 | assert(xrd); | ||
241 | cdata = &(xrd->provider_id); | ||
239 | }else if(is_qelement(n,NSURI_XRD "\tService")) { | 242 | }else if(is_qelement(n,NSURI_XRD "\tService")) { |
240 | assert(xrd); | 243 | assert(xrd); |
241 | xrd_service = &(xrd->services.add(element_priority(a), | 244 | xrd_service = &(xrd->services.add(element_priority(a), |
242 | service_t())); | 245 | service_t())); |
243 | pt_stack.push_back(n); | 246 | pt_stack.push_back(n); |
244 | }else if(is_qelement(n,NSURI_XRD "\tStatus")) { | 247 | }else if(is_qelement(n,NSURI_XRD "\tStatus")) { |
245 | for(;*a;) { | 248 | for(;*a;) { |
246 | if(!strcasecmp(*(a++),"code")) { | 249 | if(!strcasecmp(*(a++),"code")) { |
247 | if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) { | 250 | if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) { |
248 | cdata = &status_string; | 251 | cdata = &status_string; |
249 | pt_stack.push_back(n); | 252 | pt_stack.push_back(n); |
250 | break; | 253 | break; |
251 | } | 254 | } |
252 | } | 255 | } |
253 | } | 256 | } |
254 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { | 257 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { |
255 | assert(xrd); | 258 | assert(xrd); |
256 | cdata_buf.clear(); | 259 | cdata_buf.clear(); |
257 | cdata = &cdata_buf; | 260 | cdata = &cdata_buf; |
258 | }else if(xmode&xmode_html) { | 261 | }else if(xmode&xmode_html) { |
259 | html_start_element(n,a); | 262 | html_start_element(n,a); |
260 | }else{ | 263 | }else{ |
261 | skipping = 1; | 264 | skipping = 1; |
262 | } | 265 | } |
263 | }else if(pt_s==2) { | 266 | }else if(pt_s==2) { |
264 | if(is_qelement(pt_stack.back().c_str(), NSURI_XRD "\tService")) { | 267 | if(is_qelement(pt_stack.back().c_str(), NSURI_XRD "\tService")) { |
265 | if(is_qelement(n,NSURI_XRD "\tType")) { | 268 | if(is_qelement(n,NSURI_XRD "\tType")) { |
266 | assert(xrd); assert(xrd_service); | 269 | assert(xrd); assert(xrd_service); |
267 | cdata_buf.clear(); | 270 | cdata_buf.clear(); |
268 | cdata = &cdata_buf; | 271 | cdata = &cdata_buf; |
269 | }else if(is_qelement(n,NSURI_XRD "\tURI")) { | 272 | }else if(is_qelement(n,NSURI_XRD "\tURI")) { |
270 | assert(xrd); assert(xrd_service); | 273 | assert(xrd); assert(xrd_service); |
271 | cdata = &(xrd_service->uris.add(element_priority(a),string())); | 274 | cdata = &(xrd_service->uris.add(element_priority(a),string())); |
272 | }else if(is_qelement(n,NSURI_XRD "\tLocalID") | 275 | }else if(is_qelement(n,NSURI_XRD "\tLocalID") |
273 | || is_qelement(n,NSURI_OPENID10 "\tDelegate") ) { | 276 | || is_qelement(n,NSURI_OPENID10 "\tDelegate") ) { |
274 | assert(xrd); assert(xrd_service); | 277 | assert(xrd); assert(xrd_service); |
275 | cdata = &(xrd_service->local_ids.add(element_priority(a),string())); | 278 | cdata = &(xrd_service->local_ids.add(element_priority(a),string())); |
279 | }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { | ||
280 | assert(xrd); assert(xrd_service); | ||
281 | cdata = &(xrd_service->provider_id); | ||
276 | }else{ | 282 | }else{ |
277 | skipping = 1; | 283 | skipping = 1; |
278 | } | 284 | } |
279 | }else | 285 | }else |
280 | skipping = 1; | 286 | skipping = 1; |
281 | }else if(xmode&xmode_html) { | 287 | }else if(xmode&xmode_html) { |
282 | html_start_element(n,a); | 288 | html_start_element(n,a); |
283 | }else{ | 289 | }else{ |
284 | skipping = 1; | 290 | skipping = 1; |
285 | } | 291 | } |
286 | } | 292 | } |
287 | } | 293 | } |
288 | void end_element(const XML_Char *n) { | 294 | void end_element(const XML_Char *n) { |
289 | if(skipping<0) return; | 295 | if(skipping<0) return; |
290 | if(skipping) { | 296 | if(skipping) { |
291 | --skipping; return; | 297 | --skipping; return; |
292 | } | 298 | } |
293 | if(is_qelement(n,NSURI_XRD "\tType")) { | 299 | if(is_qelement(n,NSURI_XRD "\tType")) { |
294 | assert(xrd); assert(xrd_service); assert(cdata==&cdata_buf); | 300 | assert(xrd); assert(xrd_service); assert(cdata==&cdata_buf); |
295 | xrd_service->types.insert(cdata_buf); | 301 | xrd_service->types.insert(cdata_buf); |
296 | }else if(is_qelement(n,NSURI_XRD "\tService")) { | 302 | }else if(is_qelement(n,NSURI_XRD "\tService")) { |
297 | assert(xrd); assert(xrd_service); | 303 | assert(xrd); assert(xrd_service); |
298 | assert(!pt_stack.empty()); | 304 | assert(!pt_stack.empty()); |
299 | assert(pt_stack.back()==(NSURI_XRD "\tService")); | 305 | assert(pt_stack.back()==(NSURI_XRD "\tService")); |
300 | pt_stack.pop_back(); | 306 | pt_stack.pop_back(); |
301 | xrd_service = 0; | 307 | xrd_service = 0; |
302 | }else if(is_qelement(n,NSURI_XRD "\tStatus")) { | 308 | }else if(is_qelement(n,NSURI_XRD "\tStatus")) { |
303 | assert(xrd); | 309 | assert(xrd); |
304 | if(is_qelement(pt_stack.back().c_str(),n)) { | 310 | if(is_qelement(pt_stack.back().c_str(),n)) { |
305 | assert(cdata==&status_string); | 311 | assert(cdata==&status_string); |
306 | pt_stack.pop_back(); | 312 | pt_stack.pop_back(); |
307 | if(status_code!=100) | 313 | if(status_code!=100) |
308 | skipping = -1; | 314 | skipping = -1; |
309 | } | 315 | } |
310 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { | 316 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { |
311 | assert(xrd); | 317 | assert(xrd); |
312 | xrd->expires = util::w3c_to_time(cdata_buf); | 318 | xrd->expires = util::w3c_to_time(cdata_buf); |
313 | }else if((xmode&xmode_html) && is_element(n,"head")) { | 319 | }else if((xmode&xmode_html) && is_element(n,"head")) { |
314 | skipping = -1; | 320 | skipping = -1; |
315 | } | 321 | } |
316 | cdata = 0; | 322 | cdata = 0; |
317 | } | 323 | } |
318 | void character_data(const XML_Char *s,int l) { | 324 | void character_data(const XML_Char *s,int l) { |
319 | if(skipping) return; | 325 | if(skipping) return; |
320 | if(cdata) cdata->append(s,l); | 326 | if(cdata) cdata->append(s,l); |
321 | } | 327 | } |
322 | 328 | ||
323 | void html_start_element(const XML_Char *n,const XML_Char **a) { | 329 | void html_start_element(const XML_Char *n,const XML_Char **a) { |
324 | if(is_element(n,"meta")) { | 330 | if(is_element(n,"meta")) { |
325 | bool heq = false; | 331 | bool heq = false; |
326 | string l; | 332 | string l; |
327 | for(;*a;a+=2) { | 333 | for(;*a;a+=2) { |
328 | if(!( strcasecmp(a[0],"http-equiv") | 334 | if(!( strcasecmp(a[0],"http-equiv") |
329 | || strcasecmp(a[1],XRDS_HEADER) )) | 335 | || strcasecmp(a[1],XRDS_HEADER) )) |
330 | heq = true; | 336 | heq = true; |
331 | else if(!strcasecmp(a[0],"content")) | 337 | else if(!strcasecmp(a[0],"content")) |
332 | l.assign(a[1]); | 338 | l.assign(a[1]); |
333 | } | 339 | } |
334 | if(heq) | 340 | if(heq) |
335 | xrds_location = l; | 341 | xrds_location = l; |
336 | }else if(is_element(n,"link")) { | 342 | }else if(is_element(n,"link")) { |
337 | string rels; | 343 | string rels; |
338 | string href; | 344 | string href; |
339 | for(;*a;a+=2) { | 345 | for(;*a;a+=2) { |
340 | if( !strcasecmp(a[0],"rel") ) { | 346 | if( !strcasecmp(a[0],"rel") ) { |
341 | rels.assign(a[1]); | 347 | rels.assign(a[1]); |
342 | }else if( !strcasecmp(a[0],"href") ) { | 348 | }else if( !strcasecmp(a[0],"href") ) { |
343 | const char *ns = a[1]; | 349 | const char *ns = a[1]; |
344 | for(;*ns && isspace(*ns);++ns); | 350 | for(;*ns && isspace(*ns);++ns); |
345 | href.assign(ns); | 351 | href.assign(ns); |
346 | string::size_type lns=href.find_last_not_of(whitespace); | 352 | string::size_type lns=href.find_last_not_of(whitespace); |
347 | href.erase(lns+1); | 353 | href.erase(lns+1); |
348 | } | 354 | } |
349 | } | 355 | } |
350 | for(string::size_type ns=rels.find_first_not_of(whitespace); | 356 | for(string::size_type ns=rels.find_first_not_of(whitespace); |
351 | ns!=string::npos; ns=rels.find_first_not_of(whitespace,ns)) { | 357 | ns!=string::npos; ns=rels.find_first_not_of(whitespace,ns)) { |
352 | string::size_type s = rels.find_first_of(whitespace,ns); | 358 | string::size_type s = rels.find_first_of(whitespace,ns); |
353 | string rel; | 359 | string rel; |
354 | if(s==string::npos) { | 360 | if(s==string::npos) { |
355 | rel.assign(rels,ns,string::npos); | 361 | rel.assign(rels,ns,string::npos); |
356 | ns = string::npos; | 362 | ns = string::npos; |
357 | }else{ | 363 | }else{ |
358 | rel.assign(rels,ns,s-ns); | 364 | rel.assign(rels,ns,s-ns); |
359 | ns = s; | 365 | ns = s; |
360 | } | 366 | } |
361 | if(rel=="openid.server") | 367 | if(rel=="openid.server") |
362 | html_openid1.uris.add(-1,href); | 368 | html_openid1.uris.add(-1,href); |
363 | else if(rel=="openid.delegate") | 369 | else if(rel=="openid.delegate") |
364 | html_openid1.local_ids.add(-1,href); | 370 | html_openid1.local_ids.add(-1,href); |
365 | else if(rel=="openid2.provider") | 371 | else if(rel=="openid2.provider") |
366 | html_openid2.uris.add(-1,href); | 372 | html_openid2.uris.add(-1,href); |
367 | else if(rel=="openid2.local_id") | 373 | else if(rel=="openid2.local_id") |
368 | html_openid2.local_ids.add(-1,href); | 374 | html_openid2.local_ids.add(-1,href); |
369 | } | 375 | } |
370 | }else if(is_element(n,"body")) { | 376 | }else if(is_element(n,"body")) { |
371 | skipping = -1; | 377 | skipping = -1; |
372 | } | 378 | } |
373 | } | 379 | } |
374 | 380 | ||
375 | }; | 381 | }; |
376 | 382 | ||
377 | void idiscover(idiscovery_t& result,const string& identity) { | 383 | void idiscover(idiscovery_t& result,const string& identity) { |
378 | idigger_t idigger; | 384 | idigger_t idigger; |
379 | idigger.discover(result,identity); | 385 | idigger.discover(result,identity); |
380 | } | 386 | } |
381 | 387 | ||
382 | } | 388 | } |