summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--include/opkele/basic_op.h2
-rw-r--r--include/opkele/expat.h16
-rw-r--r--include/opkele/iterator.h19
-rw-r--r--include/opkele/types.h1
-rw-r--r--lib/basic_op.cc29
-rw-r--r--lib/basic_rp.cc10
-rw-r--r--lib/discovery.cc2
-rw-r--r--lib/expat.cc1
-rw-r--r--lib/extension.cc2
-rw-r--r--lib/openid_message.cc32
-rw-r--r--lib/prequeue_rp.cc4
-rw-r--r--lib/sreg.cc6
-rw-r--r--lib/verify_op.cc4
-rw-r--r--test/OP.cc16
-rw-r--r--test/RP.cc6
-rw-r--r--test/kingate_openid_message.h4
16 files changed, 77 insertions, 77 deletions
diff --git a/include/opkele/basic_op.h b/include/opkele/basic_op.h
index 0326508..12306dd 100644
--- a/include/opkele/basic_op.h
+++ b/include/opkele/basic_op.h
@@ -1,250 +1,252 @@
#ifndef __OPKELE_BASIC_OP_H
#define __OPKELE_BASIC_OP_H
#include <string>
#include <opkele/types.h>
#include <opkele/extension.h>
namespace opkele {
using std::string;
/**
* Implementation of basic OP functionality
*/
class basic_OP {
public:
/**
* The request mode for the request being processed
*/
mode_t mode;
/**
* association used in transaction. reset in case of dumb operation
*/
assoc_t assoc;
/**
* true if the request is openid2 request
*/
bool openid2;
/**
* The return_to RP endpoint
*/
string return_to;
/**
* The realm we authenticate for
*/
string realm;
/**
* Claimed identifier
*/
string claimed_id;
/**
* The OP-Local identifier
*/
string identity;
/**
* The invalidate handle for the reply request
*/
string invalidate_handle;
+ virtual ~basic_OP() { }
+
void reset_vars();
/**
* @name Request information access
* Setting and retrieval of the information pertaining to the request being processed
* @{
*/
/**
* Check if the RP expects us to get back to them.
* @return true if RP supplied return_to URL
*/
bool has_return_to() const;
/**
* Find out where the RP is waiting for us.
* @return the return_to URL supplied
* @throw no_return_to if no return_to is supplied with the request
*/
const string& get_return_to() const;
/**
* Find out what realm we are authenticating user for
* @return the realm
*/
const string& get_realm() const;
/**
* Check if request is about identity
* @return true if so
*/
bool has_identity() const;
/**
* Get claimed identifier supplied with the request
* @return claimed identifier
* @throw non_identity if request is not about identity
*/
const string& get_claimed_id() const;
/**
* Get the identity (OP-Local identifier) being confirmed
* @return identity
* @throw non_identity if request is not about identity
*/
const string& get_identity() const;
/**
* Is identifier supposed to be selected on our side?
* @return true if identity is a special identifier select URI
*/
bool is_id_select() const;
/**
* Select the identity for identifier select request
* @param cid claimed identifier
* @param lid local identifier
*/
void select_identity(const string& cid,const string& lid);
/**
* Set claimed identifier (for instance if it's supposed to have
* fragment part)
* @param cid claimed identifier
*/
void set_claimed_id(const string& cid);
/**
* @}
*/
/** @name OpenID operations
* @{
*/
/**
* Establish association with RP
* @param oum reply message
* @param inm request message
*/
basic_openid_message& associate(
basic_openid_message& oum,
const basic_openid_message& inm);
/**
* Parse the checkid_* request. The function parses input message,
* retrieves the information needed for further processing,
* verifies what can be verified at this stage.
* @param inm incoming OpenID message
* @param ext extension/chain of extensions supported
*/
void checkid_(const basic_openid_message& inm,extension_t *ext=0);
/**
* Build and sign a positive assertion message
* @param om outpu OpenID message
* @param ext extension/chain of extensions supported
* @return reference to om
*/
basic_openid_message& id_res(basic_openid_message& om,
extension_t *ext=0);
/**
* Build a 'cancel' negative assertion
* @param om output OpenID message
* @return reference to om
*/
basic_openid_message& cancel(basic_openid_message& om);
/**
* Build an 'error' reply
* @param om output OpenID message
* @param error a human-readable message indicating the cause
* @param contact contact address for the server administrator (can be empty)
* @param reference a reference token (can be empty)
* @return reference to om
*/
basic_openid_message& error(basic_openid_message& om,
const string& error,const string& contact,
const string& reference );
/**
* Build a setup_needed reply to checkid_immediate request
* @param oum output OpenID message
* @param inm incoming OpenID request being processed
* @return reference to oum
*/
basic_openid_message& setup_needed(
basic_openid_message& oum,const basic_openid_message& inm);
/**
* Process check_authentication request
* @param oum output OpenID message
* @param inm incoming request
* @return reference to oum
*/
basic_openid_message& check_authentication(
basic_openid_message& oum,const basic_openid_message& inm);
/**
* @}
*/
/**
* Verify return_to url. The default implementation checks whether
* return_to URI matches the realm
* @throw bad_realm in case of invalid realm
* @throw bad_return_to if return_to doesn't match the realm
* @see verify_OP::verify_return_to()
*/
virtual void verify_return_to();
/**
* @name Global persistent store API
* These functions are related to the associations with RPs storage
* and retrieval and nonce management.
* @{
*/
/**
* Allocate association.
* @param type association type
* @param kl association key length
* @param sl true if the association is stateless
* @return association object
*/
virtual assoc_t alloc_assoc(const string& type,size_t kl,bool sl) = 0;
/**
* Retrieve valid unexpired association
* @param handle association handle
* @return association object
*/
virtual assoc_t retrieve_assoc(const string& handle) = 0;
/**
* Allocate nonce.
* @param nonce input-output parameter containing timestamp part of
* the nonce on input
* @param sl true if the nonce is
* @return reference to nonce
* @throw failed_lookup if no such valid unexpired association
* could be retrieved
*/
virtual string& alloc_nonce(string& nonce) = 0;
/**
* Check nonce validity
* @param nonce nonce to check
* @return true if nonce found and isn't yet invalidated
*/
virtual bool check_nonce(const string& nonce) = 0;
/**
* Invalidate nonce
* @param nonce nonce to check
*/
virtual void invalidate_nonce(const string& nonce) = 0;
/**
* @}
*/
/**
* @name Site particulars API
* @{
*/
/**
* Query the absolute URL of the op endpoint
* @return fully qualified url of the OP endpoint
*/
virtual const string get_op_endpoint() const = 0;
/**
* @}
*/
};
}
#endif /* __OPKELE_BASIC_OP_H */
diff --git a/include/opkele/expat.h b/include/opkele/expat.h
index 60c41ac..3ab1630 100644
--- a/include/opkele/expat.h
+++ b/include/opkele/expat.h
@@ -1,91 +1,91 @@
#ifndef __OPKELE_EXPAT_H
#define __OPKELE_EXPAT_H
#include <cassert>
#include <expat.h>
namespace opkele {
namespace util {
class expat_t {
public:
XML_Parser _x;
expat_t() : _x(0) { }
expat_t(XML_Parser x) : _x(x) { }
virtual ~expat_t() throw();
expat_t& operator=(XML_Parser x);
operator const XML_Parser(void) const { return _x; }
operator XML_Parser(void) { return _x; }
inline bool parse(const char *s,int len,bool final=false) {
assert(_x);
return XML_Parse(_x,s,len,final);
}
- virtual void start_element(const XML_Char *n,const XML_Char **a) { }
- virtual void end_element(const XML_Char *n) { }
+ virtual void start_element(const XML_Char * /* n */,const XML_Char ** /* a */) { }
+ virtual void end_element(const XML_Char * /* n */) { }
void set_element_handler();
- virtual void character_data(const XML_Char *s,int l) { }
+ virtual void character_data(const XML_Char * /* s */,int /* l */) { }
void set_character_data_handler();
- virtual void processing_instruction(const XML_Char *t,const XML_Char *d) { }
+ virtual void processing_instruction(const XML_Char * /* t */,const XML_Char * /* d */) { }
void set_processing_instruction_handler();
- virtual void comment(const XML_Char *d) { }
+ virtual void comment(const XML_Char * /* d */) { }
void set_comment_handler();
virtual void start_cdata_section() { }
virtual void end_cdata_section() { }
void set_cdata_section_handler();
- virtual void default_handler(const XML_Char *s,int l) { }
+ virtual void default_handler(const XML_Char * /* s */,int /* l */) { }
void set_default_handler();
void set_default_handler_expand();
- virtual void start_namespace_decl(const XML_Char *p,const XML_Char *u) { }
- virtual void end_namespace_decl(const XML_Char *p) { }
+ virtual void start_namespace_decl(const XML_Char * /* p */,const XML_Char * /* u */) { }
+ virtual void end_namespace_decl(const XML_Char * /* p */) { }
void set_namespace_decl_handler();
inline enum XML_Error get_error_code() {
assert(_x); return XML_GetErrorCode(_x); }
static inline const XML_LChar *error_string(XML_Error c) {
return XML_ErrorString(c); }
inline long get_current_byte_index() {
assert(_x); return XML_GetCurrentByteIndex(_x); }
inline int get_current_line_number() {
assert(_x); return XML_GetCurrentLineNumber(_x); }
inline int get_current_column_number() {
assert(_x); return XML_GetCurrentColumnNumber(_x); }
inline void set_user_data() {
assert(_x); XML_SetUserData(_x,this); }
inline bool set_base(const XML_Char *b) {
assert(_x); return XML_SetBase(_x,b); }
inline const XML_Char *get_base() {
assert(_x); return XML_GetBase(_x); }
inline int get_specified_attribute_count() {
assert(_x); return XML_GetSpecifiedAttributeCount(_x); }
inline bool set_param_entity_parsing(enum XML_ParamEntityParsing c) {
assert(_x); return XML_SetParamEntityParsing(_x,c); }
inline static XML_Parser parser_create(const XML_Char *e=0) {
return XML_ParserCreate(e); }
inline static XML_Parser parser_create_ns(const XML_Char *e=0,XML_Char s='\t') {
return XML_ParserCreateNS(e,s); }
};
}
}
#endif /* __OPKELE_EXPAT_H */
diff --git a/include/opkele/iterator.h b/include/opkele/iterator.h
index 812a786..28c1c83 100644
--- a/include/opkele/iterator.h
+++ b/include/opkele/iterator.h
@@ -1,217 +1,216 @@
#ifndef __OPKELE_ITERATOR_H
#define __OPKELE_ITERATOR_H
#include <cassert>
#include <iterator>
namespace opkele {
namespace util {
using std::iterator;
using std::forward_iterator_tag;
using std::output_iterator_tag;
template <typename T>
class basic_output_iterator_proxy_impl : public iterator<output_iterator_tag,T,void,T*,T&> {
public:
virtual ~basic_output_iterator_proxy_impl() { }
virtual basic_output_iterator_proxy_impl<T>* dup() const = 0;
basic_output_iterator_proxy_impl<T>& operator*() { return *this; };
virtual basic_output_iterator_proxy_impl<T>& operator=(const T& x) = 0;
};
template<typename IT,typename T=typename IT::value_type>
class output_iterator_proxy_impl : public basic_output_iterator_proxy_impl<T> {
public:
IT i;
- output_iterator_proxy_impl(const IT& i) : i(i) { }
+ output_iterator_proxy_impl(const IT& _i) : i(_i) { }
basic_output_iterator_proxy_impl<T>* dup() const {
return new output_iterator_proxy_impl<IT,T>(i); }
basic_output_iterator_proxy_impl<T>& operator=(const T& x) {
- (*i) = x;
- }
+ (*i) = x; return *this; }
};
template<typename T>
class output_iterator_proxy : public iterator<output_iterator_tag,T,void,T*,T&> {
public:
basic_output_iterator_proxy_impl<T> *I;
template<typename IT>
output_iterator_proxy(const IT& i)
: I(new output_iterator_proxy_impl<IT,T>(i)) { }
output_iterator_proxy(const output_iterator_proxy<T>& x)
: I(x.I->dup()) { }
~output_iterator_proxy() { delete I; }
output_iterator_proxy& operator=(const output_iterator_proxy<T>& x) {
delete I; I = x.I->dup(); }
output_iterator_proxy& operator*() { return *this; }
output_iterator_proxy& operator=(const T& x) {
- (**I) = x; }
+ (**I) = x; return *this; }
output_iterator_proxy& operator++() { return *this; }
output_iterator_proxy& operator++(int) { return *this; }
};
template <typename T,typename TR=T&,typename TP=T*>
class basic_forward_iterator_proxy_impl : public iterator<forward_iterator_tag,T,void,TP,TR> {
public:
virtual ~basic_forward_iterator_proxy_impl() { }
virtual basic_forward_iterator_proxy_impl<T,TR,TP>* dup() const = 0;
virtual bool operator==(const basic_forward_iterator_proxy_impl<T,TR,TP>& x) const = 0;
virtual bool operator!=(const basic_forward_iterator_proxy_impl<T,TR,TP>& x) const {
return !((*this)==x); }
virtual TR operator*() const = 0;
virtual TP operator->() const = 0;
virtual void advance() = 0;
};
template <typename IT>
class forward_iterator_proxy_impl : public basic_forward_iterator_proxy_impl<typename IT::value_type,typename IT::reference,typename IT::pointer> {
public:
IT i;
- forward_iterator_proxy_impl(const IT& i) : i(i) { }
+ forward_iterator_proxy_impl(const IT& _i) : i(_i) { }
virtual basic_forward_iterator_proxy_impl<typename IT::value_type,typename IT::reference,typename IT::pointer>* dup() const {
return new forward_iterator_proxy_impl<IT>(i); }
virtual bool operator==(const basic_forward_iterator_proxy_impl<typename IT::value_type,typename IT::reference,typename IT::pointer>& x) const {
return i==static_cast<const forward_iterator_proxy_impl<IT>*>(&x)->i; }
virtual bool operator!=(const basic_forward_iterator_proxy_impl<typename IT::value_type,typename IT::reference,typename IT::pointer>& x) const {
return i!=static_cast<const forward_iterator_proxy_impl<IT>*>(&x)->i; }
virtual typename IT::reference operator*() const { return *i; }
virtual typename IT::pointer operator->() const { return i.operator->(); }
virtual void advance() { ++i; }
};
template<typename T,typename TR=T&,typename TP=T*>
class forward_iterator_proxy : public iterator<forward_iterator_tag,T,void,TP,TR> {
public:
basic_forward_iterator_proxy_impl<T,TR,TP> *I;
template<typename IT>
forward_iterator_proxy(const IT& i)
: I(new forward_iterator_proxy_impl<IT>(i)) { }
forward_iterator_proxy(const forward_iterator_proxy<T,TR,TP>& x)
: I(x.I->dup()) { }
~forward_iterator_proxy() { delete I; }
forward_iterator_proxy& operator=(const forward_iterator_proxy<T,TR,TP>& x) {
delete I; I = x.I->dup(); }
bool operator==(const forward_iterator_proxy<T,TR,TP>& x) const {
return (*I)==(*(x.I)); }
bool operator!=(const forward_iterator_proxy<T,TR,TP>& x) const {
return (*I)!=(*(x.I)); }
TR operator*() const {
return **I; }
TP operator->() const {
return I->operator->(); }
forward_iterator_proxy<T,TR,TP>& operator++() {
I->advance(); return *this; }
forward_iterator_proxy<T,TR,TP>& operator++(int) {
forward_iterator_proxy<T,TR,TP> rv(*this);
I->advance(); return rv; }
};
template<typename IT>
class basic_filterator : public iterator<
typename IT::iterator_category,
typename IT::value_type,
typename IT::difference_type,
typename IT::pointer,
typename IT::reference> {
public:
IT it;
IT ei;
bool empty;
basic_filterator() : empty(true) { }
- basic_filterator(const IT& bi,const IT& ei)
- : it(bi), ei(ei) { empty = (bi==ei); }
+ basic_filterator(const IT& _bi,const IT& _ei)
+ : it(_bi), ei(_ei) { empty = (it==ei); }
basic_filterator(const basic_filterator<IT>& x)
: it(x.it), ei(x.ei), empty(x.empty) { }
virtual ~basic_filterator() { }
bool operator==(const basic_filterator<IT>& x) const {
return empty?x.empty:(it==x.it); }
bool operator!=(const basic_filterator<IT>& x) const {
return empty!=x.empty || it!=x.it; }
typename IT::reference operator*() const {
assert(!empty);
return *it; }
typename IT::pointer operator->() const {
assert(!empty);
return it.operator->(); }
basic_filterator<IT>& operator++() {
bool found = false;
for(++it;!(it==ei || (found=is_interesting()));++it);
if(!found) empty=true;
return *this;
}
basic_filterator<IT> operator++(int) {
basic_filterator<IT> rv(*this);
++(*this);
return rv;
}
void prepare() {
bool found = false;
for(;!(it==ei || (found=is_interesting()));++it);
if(!found) empty = true;
}
virtual bool is_interesting() const = 0;
};
template<typename IT,typename T=typename IT::value_type::first_type,typename TR=T&,typename TP=T*>
class map_keys_iterator : public iterator<
typename IT::iterator_category,
T,void,TP,TR> {
public:
typedef map_keys_iterator<IT,T,TR,TP> self_type;
IT it;
IT ei;
bool empty;
map_keys_iterator() : empty(true) { }
- map_keys_iterator(const IT& bi,
- const IT& ei)
- : it(bi), ei(ei) { empty = (bi==ei); }
+ map_keys_iterator(const IT& _bi,
+ const IT& _ei)
+ : it(_bi), ei(_ei) { empty = (it==ei); }
map_keys_iterator(const self_type& x)
: it(x.it), ei(x.ei), empty(x.empty) { }
bool operator==(const self_type& x) const {
return empty?x.empty:(it==x.it); }
bool operator!=(const self_type& x) const {
return empty!=x.empty || it!=x.it; }
TR operator*() const {
assert(!empty);
return it->first; }
TP operator->() const {
assert(!empty);
return &(it->first); }
self_type& operator++() {
assert(!empty);
empty=((++it)==ei); return *this; }
self_type operator++(int) {
self_type rv(*this);
++(*this); return rv; }
};
}
}
#endif /* __OPKELE_ITERATOR_H */
diff --git a/include/opkele/types.h b/include/opkele/types.h
index 6ab51ef..a3b657d 100644
--- a/include/opkele/types.h
+++ b/include/opkele/types.h
@@ -1,229 +1,230 @@
#ifndef __OPKELE_TYPES_H
#define __OPKELE_TYPES_H
/**
* @file
* @brief various types declarations
*/
#include <cstring>
#include <ostream>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <list>
#include <opkele/iterator.h>
#include <opkele/tr1-mem.h>
namespace opkele {
using std::vector;
using std::string;
using std::map;
using std::ostream;
using std::multimap;
using std::set;
using std::list;
using std::iterator;
using std::forward_iterator_tag;
/**
* the OpenID operation mode
*/
typedef enum _mode_t {
mode_unknown = 0,
mode_associate,
mode_checkid_immediate,
mode_checkid_setup,
mode_check_association
} mode_t;
/**
* the association secret container
*/
class secret_t : public vector<unsigned char> {
public:
/**
* xor the secret and hmac together and encode, using base64
* @param key_d pointer to the message digest
* @param rv reference to the return value
*/
void enxor_to_base64(const unsigned char *key_d,string& rv) const;
/**
* decode base64-encoded secret and xor it with the message digest
* @param key_d pointer to the message digest
* @param b64 base64-encoded secret value
*/
void enxor_from_base64(const unsigned char *key_d,const string& b64);
/**
* plainly encode to base64 representation
* @param rv reference to the return value
*/
void to_base64(string& rv) const;
/**
* decode cleartext secret from base64
* @param b64 base64-encoded representation of the secret value
*/
void from_base64(const string& b64);
};
/**
* Interface to the association.
*/
class association_t {
public:
virtual ~association_t() { }
/**
* retrieve the server with which association was established.
* @return server name
*/
virtual string server() const = 0;
/**
* retrieve the association handle.
* @return handle
*/
virtual string handle() const = 0;
/**
* retrieve the association type.
* @return association type
*/
virtual string assoc_type() const = 0;
/**
* retrieve the association secret.
* @return association secret
*/
virtual secret_t secret() const = 0;
/**
* retrieve the number of seconds the association expires in.
* @return seconds till expiration
*/
virtual int expires_in() const = 0;
/**
* check whether the association is stateless.
* @return true if stateless
*/
virtual bool stateless() const = 0;
/**
* check whether the association is expired.
* @return true if expired
*/
virtual bool is_expired() const = 0;
};
/**
* the shared_ptr<> for association_t object type
*/
typedef tr1mem::shared_ptr<association_t> assoc_t;
class basic_openid_message {
public:
typedef list<string> fields_t;
typedef util::forward_iterator_proxy<
string,const string&,const string*
> fields_iterator;
basic_openid_message() { }
+ virtual ~basic_openid_message() { }
basic_openid_message(const basic_openid_message& x);
void copy_to(basic_openid_message& x) const;
virtual bool has_field(const string& n) const = 0;
virtual const string& get_field(const string& n) const = 0;
virtual bool has_ns(const string& uri) const;
virtual string get_ns(const string& uri) const;
virtual fields_iterator fields_begin() const = 0;
virtual fields_iterator fields_end() const = 0;
virtual string append_query(const string& url) const;
virtual string query_string() const;
virtual void reset_fields();
virtual void set_field(const string& n,const string& v);
virtual void reset_field(const string& n);
virtual void from_keyvalues(const string& kv);
virtual void to_keyvalues(ostream& o) const;
virtual void to_htmlhiddens(ostream& o) const;
void add_to_signed(const string& fields);
string find_ns(const string& uri,const char *pfx) const;
string allocate_ns(const string& uri,const char *pfx);
};
class openid_message_t : public basic_openid_message, public map<string,string> {
public:
openid_message_t() { }
openid_message_t(const basic_openid_message& x)
: basic_openid_message(x) { }
void copy_to(basic_openid_message& x) const;
bool has_field(const string& n) const;
const string& get_field(const string& n) const;
virtual fields_iterator fields_begin() const;
virtual fields_iterator fields_end() const;
void reset_fields();
void set_field(const string& n,const string& v);
void reset_field(const string& n);
};
/**
* request/response parameters map
*/
class params_t : public openid_message_t {
public:
/**
* check whether the parameter is present.
* @param n the parameter name
* @return true if yes
*/
bool has_param(const string& n) const {
return has_field(n); }
/**
* retrieve the parameter (const version)
* @param n the parameter name
* @return the parameter value
* @throw failed_lookup if there is no such parameter
*/
const string& get_param(const string& n) const {
return get_field(n); }
/**
* parse the OpenID key/value data.
* @param kv the OpenID key/value data
*/
void parse_keyvalues(const string& kv) {
from_keyvalues(kv); }
string append_query(const string& url,const char *prefix="openid.") const;
};
struct openid_endpoint_t {
string uri;
string claimed_id;
string local_id;
openid_endpoint_t() { }
openid_endpoint_t(const string& u,const string& cid,const string& lid)
: uri(u), claimed_id(cid), local_id(lid) { }
bool operator==(const openid_endpoint_t& x) const {
return uri==x.uri && local_id==x.local_id; }
bool operator<(const openid_endpoint_t& x) const {
int c;
return (c=strcmp(uri.c_str(),x.uri.c_str()))
? (c<0) : (strcmp(local_id.c_str(),x.local_id.c_str())<0); }
};
}
#endif /* __OPKELE_TYPES_H */
diff --git a/lib/basic_op.cc b/lib/basic_op.cc
index 18446dc..2d82147 100644
--- a/lib/basic_op.cc
+++ b/lib/basic_op.cc
@@ -1,330 +1,327 @@
#include <time.h>
#include <cassert>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <opkele/data.h>
#include <opkele/basic_op.h>
#include <opkele/exception.h>
#include <opkele/util.h>
#include <opkele/uris.h>
namespace opkele {
void basic_OP::reset_vars() {
assoc.reset();
return_to.clear(); realm.clear();
claimed_id.clear(); identity.clear();
invalidate_handle.clear();
}
bool basic_OP::has_return_to() const {
return !return_to.empty();
}
const string& basic_OP::get_return_to() const {
if(return_to.empty())
throw no_return_to(OPKELE_CP_ "No return_to URL provided with request");
return return_to;
}
const string& basic_OP::get_realm() const {
assert(!realm.empty());
return realm;
}
bool basic_OP::has_identity() const {
return !identity.empty();
}
const string& basic_OP::get_claimed_id() const {
if(claimed_id.empty())
throw non_identity(OPKELE_CP_ "attempting to retrieve claimed_id of non-identity related request");
assert(!identity.empty());
return claimed_id;
}
const string& basic_OP::get_identity() const {
if(identity.empty())
throw non_identity(OPKELE_CP_ "attempting to retrieve identity of non-identity related request");
assert(!claimed_id.empty());
return identity;
}
bool basic_OP::is_id_select() const {
return identity==IDURI_SELECT20;
}
void basic_OP::select_identity(const string& c,const string& i) {
claimed_id = c; identity = i;
}
void basic_OP::set_claimed_id(const string& c) {
claimed_id = c;
}
basic_openid_message& basic_OP::associate(
basic_openid_message& oum,
const basic_openid_message& inm) try {
assert(inm.get_field("mode")=="associate");
util::dh_t dh;
util::bignum_t c_pub;
unsigned char key_digest[SHA256_DIGEST_LENGTH];
size_t d_len = 0;
- enum {
- sess_cleartext, sess_dh_sha1, sess_dh_sha256
- } st = sess_cleartext;
string sts = inm.get_field("session_type");
string ats = inm.get_field("assoc_type");
if(sts=="DH-SHA1" || sts=="DH-SHA256") {
if(!(dh = DH_new()))
throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
c_pub = util::base64_to_bignum(inm.get_field("dh_consumer_public"));
try { dh->p = util::base64_to_bignum(inm.get_field("dh_modulus"));
}catch(failed_lookup&) {
dh->p = util::dec_to_bignum(data::_default_p); }
try { dh->g = util::base64_to_bignum(inm.get_field("dh_gen"));
}catch(failed_lookup&) {
dh->g = util::dec_to_bignum(data::_default_g); }
if(!DH_generate_key(dh))
throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
vector<unsigned char> ck(DH_size(dh)+1);
unsigned char *ckptr = &(ck.front())+1;
int cklen = DH_compute_key(ckptr,c_pub,dh);
if(cklen<0)
throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
if(cklen && (*ckptr)&0x80) {
(*(--ckptr)) = 0; ++cklen; }
if(sts=="DH-SHA1") {
SHA1(ckptr,cklen,key_digest); d_len = SHA_DIGEST_LENGTH;
}else if(sts=="DH-SHA256") {
SHA256(ckptr,cklen,key_digest); d_len = SHA256_DIGEST_LENGTH;
}else
throw internal_error(OPKELE_CP_ "I thought I knew the session type");
}else
throw unsupported(OPKELE_CP_ "Unsupported session_type");
- assoc_t assoc;
+ assoc_t a;
if(ats=="HMAC-SHA1")
- assoc = alloc_assoc(ats,SHA_DIGEST_LENGTH,true);
+ a = alloc_assoc(ats,SHA_DIGEST_LENGTH,true);
else if(ats=="HMAC-SHA256")
- assoc = alloc_assoc(ats,SHA256_DIGEST_LENGTH,true);
+ a = alloc_assoc(ats,SHA256_DIGEST_LENGTH,true);
else
throw unsupported(OPKELE_CP_ "Unsupported assoc_type");
oum.reset_fields();
oum.set_field("ns",OIURI_OPENID20);
- oum.set_field("assoc_type",assoc->assoc_type());
- oum.set_field("assoc_handle",assoc->handle());
+ oum.set_field("assoc_type",a->assoc_type());
+ oum.set_field("assoc_handle",a->handle());
oum.set_field("expires_in",util::long_to_string(assoc->expires_in()));
- secret_t secret = assoc->secret();
+ secret_t secret = a->secret();
if(sts=="DH-SHA1" || sts=="DH-SHA256") {
if(d_len != secret.size())
throw bad_input(OPKELE_CP_ "Association secret and session MAC are not of the same size");
oum.set_field("session_type",sts);
oum.set_field("dh_server_public",util::bignum_to_base64(dh->pub_key));
string b64; secret.enxor_to_base64(key_digest,b64);
oum.set_field("enc_mac_key",b64);
}else /* TODO: support cleartext over encrypted connection */
throw unsupported(OPKELE_CP_ "Unsupported session type");
return oum;
} catch(unsupported& u) {
oum.reset_fields();
oum.set_field("ns",OIURI_OPENID20);
oum.set_field("error",u.what());
oum.set_field("error_code","unsupported-type");
oum.set_field("session_type","DH-SHA256");
oum.set_field("assoc_type","HMAC-SHA256");
return oum;
}
void basic_OP::checkid_(const basic_openid_message& inm,
extension_t *ext) {
reset_vars();
- string mode = inm.get_field("mode");
- if(mode=="checkid_setup")
+ string modestr = inm.get_field("mode");
+ if(modestr=="checkid_setup")
mode = mode_checkid_setup;
- else if(mode=="checkid_immediate")
+ else if(modestr=="checkid_immediate")
mode = mode_checkid_immediate;
else
throw bad_input(OPKELE_CP_ "Invalid checkid_* mode");
try {
assoc = retrieve_assoc(invalidate_handle=inm.get_field("assoc_handle"));
invalidate_handle.clear();
}catch(failed_lookup&) { }
try {
openid2 = (inm.get_field("ns")==OIURI_OPENID20);
}catch(failed_lookup&) { openid2 = false; }
try {
return_to = inm.get_field("return_to");
}catch(failed_lookup&) { }
if(openid2) {
try {
realm = inm.get_field("realm");
}catch(failed_lookup&) {
try {
realm = inm.get_field("trust_root");
}catch(failed_lookup&) {
if(return_to.empty())
throw bad_input(OPKELE_CP_
"Both realm and return_to are unset");
realm = return_to;
}
}
}else{
try {
realm = inm.get_field("trust_root");
}catch(failed_lookup&) {
if(return_to.empty())
throw bad_input(OPKELE_CP_
"Both realm and return_to are unset");
realm = return_to;
}
}
try {
identity = inm.get_field("identity");
try {
claimed_id = inm.get_field("claimed_id");
}catch(failed_lookup&) {
if(openid2)
throw bad_input(OPKELE_CP_
"claimed_id and identity must be either both present or both absent");
claimed_id = identity;
}
}catch(failed_lookup&) {
if(openid2 && inm.has_field("claimed_id"))
throw bad_input(OPKELE_CP_
"claimed_id and identity must be either both present or both absent");
}
verify_return_to();
if(ext) ext->op_checkid_hook(inm);
}
basic_openid_message& basic_OP::id_res(basic_openid_message& om,
extension_t *ext) {
assert(!return_to.empty());
assert(!is_id_select());
if(!assoc) {
assoc = alloc_assoc("HMAC-SHA256",SHA256_DIGEST_LENGTH,true);
}
time_t now = time(0);
struct tm gmt; gmtime_r(&now,&gmt);
char w3timestr[24];
if(!strftime(w3timestr,sizeof(w3timestr),"%Y-%m-%dT%H:%M:%SZ",&gmt))
throw failed_conversion(OPKELE_CP_
"Failed to build time string for nonce" );
om.set_field("ns",OIURI_OPENID20);
om.set_field("mode","id_res");
om.set_field("op_endpoint",get_op_endpoint());
string ats = "ns,mode,op_endpoint,return_to,response_nonce,"
"assoc_handle,signed";
if(!identity.empty()) {
om.set_field("identity",identity);
om.set_field("claimed_id",claimed_id);
ats += ",identity,claimed_id";
}
om.set_field("return_to",return_to);
string nonce = w3timestr;
om.set_field("response_nonce",alloc_nonce(nonce));
if(!invalidate_handle.empty()) {
om.set_field("invalidate_handle",invalidate_handle);
ats += ",invalidate_handle";
}
om.set_field("assoc_handle",assoc->handle());
om.add_to_signed(ats);
if(ext) ext->op_id_res_hook(om);
om.set_field("sig",util::base64_signature(assoc,om));
return om;
}
basic_openid_message& basic_OP::cancel(basic_openid_message& om) {
assert(!return_to.empty());
om.set_field("ns",OIURI_OPENID20);
om.set_field("mode","cancel");
return om;
}
basic_openid_message& basic_OP::error(basic_openid_message& om,
- const string& error,const string& contact,
+ const string& err,const string& contact,
const string& reference ) {
assert(!return_to.empty());
om.set_field("ns",OIURI_OPENID20);
om.set_field("mode","error");
- om.set_field("error",error);
- om.set_field("contact",contact);
- om.set_field("reference",reference);
+ om.set_field("error",err);
+ if(!contact.empty()) om.set_field("contact",contact);
+ if(!reference.empty()) om.set_field("reference",reference);
return om;
}
basic_openid_message& basic_OP::setup_needed(
basic_openid_message& oum,const basic_openid_message& inm) {
assert(mode==mode_checkid_immediate);
assert(!return_to.empty());
if(openid2) {
oum.set_field("ns",OIURI_OPENID20);
oum.set_field("mode","setup_needed");
}else{
oum.set_field("mode","id_res");
static const string setupmode = "checkid_setup";
oum.set_field("user_setup_url",
util::change_mode_message_proxy(inm,setupmode)
.append_query(get_op_endpoint()));
}
return oum;
}
basic_openid_message& basic_OP::check_authentication(
basic_openid_message& oum,
const basic_openid_message& inm) try {
assert(inm.get_field("mode")=="check_authentication");
oum.reset_fields();
oum.set_field("ns",OIURI_OPENID20);
bool o2;
try {
o2 = (inm.get_field("ns")==OIURI_OPENID20);
}catch(failed_lookup&) { o2 = false; }
string nonce;
if(o2) {
try {
if(!check_nonce(nonce = inm.get_field("response_nonce")))
throw failed_check_authentication(OPKELE_CP_ "Invalid nonce");
}catch(failed_lookup&) {
throw failed_check_authentication(OPKELE_CP_ "No nonce provided with check_authentication request");
}
}
try {
assoc = retrieve_assoc(inm.get_field("assoc_handle"));
if(!assoc->stateless())
throw failed_check_authentication(OPKELE_CP_ "Will not do check_authentication on a stateful handle");
}catch(failed_lookup&) {
throw failed_check_authentication(OPKELE_CP_ "No assoc_handle or invalid assoc_handle specified with check_authentication request");
}
static const string idresmode = "id_res";
try {
if(util::base64_signature(assoc,util::change_mode_message_proxy(inm,idresmode))!=inm.get_field("sig"))
throw failed_check_authentication(OPKELE_CP_ "Signature mismatch");
}catch(failed_lookup&) {
throw failed_check_authentication(OPKELE_CP_ "failed to calculate signature");
}
oum.set_field("is_valid","true");
try {
string h = inm.get_field("invalidate_handle");
try {
assoc_t ih = retrieve_assoc(h);
}catch(invalid_handle& ih) {
oum.set_field("invalidate_handle",h);
}catch(failed_lookup& ih) {
oum.set_field("invalidate_handle",h);
}
}catch(failed_lookup&) { }
if(o2) {
assert(!nonce.empty());
invalidate_nonce(nonce);
}
return oum;
}catch(failed_check_authentication& ) {
oum.set_field("is_valid","false");
return oum;
}
void basic_OP::verify_return_to() {
if(realm.find('#')!=string::npos)
throw opkele::bad_realm(OPKELE_CP_ "authentication realm contains URI fragment");
if(!util::uri_matches_realm(return_to,realm))
throw bad_return_to(OPKELE_CP_ "return_to URL doesn't match realm");
}
}
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc
index bd45d99..a0ad130 100644
--- a/lib/basic_rp.cc
+++ b/lib/basic_rp.cc
@@ -1,297 +1,299 @@
+#include <cassert>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <opkele/basic_rp.h>
#include <opkele/exception.h>
#include <opkele/uris.h>
#include <opkele/data.h>
#include <opkele/util.h>
#include <opkele/curl.h>
namespace opkele {
static void dh_get_secret(
secret_t& secret, const basic_openid_message& om,
const char *exp_assoc, const char *exp_sess,
util::dh_t& dh,
size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*),
size_t exp_s_len) try {
if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess)
throw bad_input(OPKELE_CP_ "Unexpected associate response");
util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public"));
vector<unsigned char> ck(DH_size(dh)+1);
unsigned char *ckptr = &(ck.front())+1;
int cklen = DH_compute_key(ckptr,s_pub,dh);
if(cklen<0)
throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
if(cklen && (*ckptr)&0x80) {
(*(--ckptr))=0; ++cklen; }
- unsigned char key_digest[d_len];
+ assert(d_len<=SHA256_DIGEST_LENGTH);
+ unsigned char key_digest[SHA256_DIGEST_LENGTH];
secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key"));
if(secret.size()!=exp_s_len)
throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type");
}catch(opkele::failed_lookup& ofl) {
throw bad_input(OPKELE_CP_ "Incoherent response from OP");
} OPKELE_RETHROW
static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) {
util::curl_pick_t curl = util::curl_pick_t::easy_init();
if(!curl)
throw exception_curl(OPKELE_CP_ "failed to initialize curl");
string request = inm.query_string();
CURLcode r;
(r=curl.misc_sets())
|| (r=curl.easy_setopt(CURLOPT_URL,OP.c_str()))
|| (r=curl.easy_setopt(CURLOPT_POST,1))
|| (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data()))
|| (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length()))
|| (r=curl.set_write());
if(r)
throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
if( (r=curl.easy_perform()) )
throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
oum.from_keyvalues(curl.response);
}
assoc_t basic_RP::associate(const string& OP) {
util::dh_t dh = DH_new();
if(!dh)
throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
dh->p = util::dec_to_bignum(data::_default_p);
dh->g = util::dec_to_bignum(data::_default_g);
if(!DH_generate_key(dh))
throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
openid_message_t req;
req.set_field("ns",OIURI_OPENID20);
req.set_field("mode","associate");
req.set_field("dh_modulus",util::bignum_to_base64(dh->p));
req.set_field("dh_gen",util::bignum_to_base64(dh->g));
req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key));
openid_message_t res;
req.set_field("assoc_type","HMAC-SHA256");
req.set_field("session_type","DH-SHA256");
secret_t secret;
int expires_in;
try {
direct_request(res,req,OP);
dh_get_secret( secret, res,
"HMAC-SHA256", "DH-SHA256",
dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH );
expires_in = util::string_to_long(res.get_field("expires_in"));
- }catch(exception& e) {
+ }catch(exception&) {
try {
req.set_field("assoc_type","HMAC-SHA1");
req.set_field("session_type","DH-SHA1");
direct_request(res,req,OP);
dh_get_secret( secret, res,
"HMAC-SHA1", "DH-SHA1",
dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH );
expires_in = util::string_to_long(res.get_field("expires_in"));
- }catch(bad_input& e) {
+ }catch(bad_input&) {
throw dumb_RP(OPKELE_CP_ "OP failed to supply an association");
}
}
return store_assoc(
OP, res.get_field("assoc_handle"),
res.get_field("assoc_type"), secret,
expires_in );
}
basic_openid_message& basic_RP::checkid_(
basic_openid_message& rv,
mode_t mode,
const string& return_to,const string& realm,
extension_t *ext) {
rv.reset_fields();
rv.set_field("ns",OIURI_OPENID20);
if(mode==mode_checkid_immediate)
rv.set_field("mode","checkid_immediate");
else if(mode==mode_checkid_setup)
rv.set_field("mode","checkid_setup");
else
throw bad_input(OPKELE_CP_ "unknown checkid_* mode");
if(realm.empty() && return_to.empty())
throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty");
if(!realm.empty()) {
rv.set_field("realm",realm);
rv.set_field("trust_root",realm);
}
if(!return_to.empty())
rv.set_field("return_to",return_to);
const openid_endpoint_t& ep = get_endpoint();
rv.set_field("claimed_id",ep.claimed_id);
rv.set_field("identity",ep.local_id);
try {
rv.set_field("assoc_handle",find_assoc(ep.uri)->handle());
}catch(dumb_RP& drp) {
}catch(failed_lookup& fl) {
try {
rv.set_field("assoc_handle",associate(ep.uri)->handle());
}catch(dumb_RP& drp) { }
} OPKELE_RETHROW
if(ext) ext->rp_checkid_hook(rv);
return rv;
}
class signed_part_message_proxy : public basic_openid_message {
public:
const basic_openid_message& x;
set<string> signeds;
signed_part_message_proxy(const basic_openid_message& xx) : x(xx) {
const string& slist = x.get_field("signed");
string::size_type p = 0;
while(true) {
string::size_type co = slist.find(',',p);
string f = (co==string::npos)
?slist.substr(p):slist.substr(p,co-p);
signeds.insert(f);
if(co==string::npos) break;
p = co+1;
}
}
bool has_field(const string& n) const {
return signeds.find(n)!=signeds.end() && x.has_field(n); }
const string& get_field(const string& n) const {
if(signeds.find(n)==signeds.end())
throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed");
return x.get_field(n); }
fields_iterator fields_begin() const {
return signeds.begin(); }
fields_iterator fields_end() const {
return signeds.end(); }
};
static void parse_query(const string& u,string::size_type q,
map<string,string>& p) {
if(q==string::npos)
return;
assert(u[q]=='?');
++q;
string::size_type l = u.size();
while(q<l) {
string::size_type eq = u.find('=',q);
string::size_type am = u.find('&',q);
if(am==string::npos) {
if(eq==string::npos) {
p[""] = u.substr(q);
}else{
p[u.substr(q,eq-q)] = u.substr(eq+1);
}
break;
}else{
if(eq==string::npos || eq>am) {
p[""] = u.substr(q,eq-q);
}else{
p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1);
}
q = ++am;
}
}
}
void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) {
bool o2 = om.has_field("ns")
&& om.get_field("ns")==OIURI_OPENID20;
if( (!o2) && om.has_field("user_setup_url"))
throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",
om.get_field("user_setup_url"));
string m = om.get_field("mode");
if(o2 && m=="setup_needed")
throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided");
if(m=="cancel")
throw id_res_cancel(OPKELE_CP_ "authentication cancelled");
bool go_dumb=false;
try {
string OP = o2
?om.get_field("op_endpoint")
:get_endpoint().uri;
assoc_t assoc = retrieve_assoc(
OP,om.get_field("assoc_handle"));
if(om.get_field("sig")!=util::base64_signature(assoc,om))
throw id_res_mismatch(OPKELE_CP_ "signature mismatch");
}catch(dumb_RP& drp) {
go_dumb=true;
}catch(failed_lookup& e) {
go_dumb=true;
} OPKELE_RETHROW
if(go_dumb) {
try {
string OP = o2
?om.get_field("op_endpoint")
:get_endpoint().uri;
check_authentication(OP,om);
}catch(failed_check_authentication& fca) {
throw id_res_failed(OPKELE_CP_ "failed to check_authentication()");
} OPKELE_RETHROW
}
signed_part_message_proxy signeds(om);
if(o2) {
check_nonce(om.get_field("op_endpoint"),
om.get_field("response_nonce"));
static const char *mustsign[] = {
"op_endpoint", "return_to", "response_nonce", "assoc_handle",
"claimed_id", "identity" };
- for(int ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) {
+ for(size_t ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) {
if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms]))
throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs");
}
if( (
(om.has_field("claimed_id")?1:0)
^
(om.has_field("identity")?1:0)
)&1 )
throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent");
string turl = util::rfc_3986_normalize_uri(get_this_url());
util::strip_uri_fragment_part(turl);
string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to"));
util::strip_uri_fragment_part(rurl);
string::size_type
tq = turl.find('?'), rq = rurl.find('?');
if(
((tq==string::npos)?turl:turl.substr(0,tq))
!=
((rq==string::npos)?rurl:rurl.substr(0,rq))
)
throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url");
map<string,string> tp; parse_query(turl,tq,tp);
map<string,string> rp; parse_query(rurl,rq,rp);
for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) {
map<string,string>::const_iterator tpi = tp.find(rpi->first);
if(tpi==tp.end())
throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request");
if(tpi->second!=rpi->second)
throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request");
}
if(om.has_field("claimed_id")) {
verify_OP(
om.get_field("op_endpoint"),
om.get_field("claimed_id"),
om.get_field("identity") );
}
}
if(ext) ext->rp_id_res_hook(om,signeds);
}
void basic_RP::check_authentication(const string& OP,
const basic_openid_message& om){
openid_message_t res;
static const string checkauthmode = "check_authentication";
direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP);
if(res.has_field("is_valid")) {
if(res.get_field("is_valid")=="true") {
if(res.has_field("invalidate_handle"))
invalidate_assoc(OP,res.get_field("invalidate_handle"));
return;
}
}
throw failed_check_authentication(
OPKELE_CP_ "failed to verify response");
}
}
diff --git a/lib/discovery.cc b/lib/discovery.cc
index 6f58339..6f9926c 100644
--- a/lib/discovery.cc
+++ b/lib/discovery.cc
@@ -1,577 +1,577 @@
#include <list>
#include <opkele/curl.h>
#include <opkele/expat.h>
#include <opkele/uris.h>
#include <opkele/discovery.h>
#include <opkele/exception.h>
#include <opkele/util.h>
#include <opkele/tidy.h>
#include <opkele/debug.h>
#include "config.h"
#define XRDS_HEADER "X-XRDS-Location"
#define CT_HEADER "Content-Type"
namespace opkele {
using std::list;
using xrd::XRD_t;
using xrd::service_t;
/* TODO: the whole discovery thing needs cleanup and optimization due to
* many changes of concept. */
static const char *whitespace = " \t\r\n";
static const char *i_leaders = "=@+$!(";
static const size_t max_html = 16384;
static const struct service_type_t {
const char *uri;
const char *forceid;
} op_service_types[] = {
{ STURI_OPENID20_OP, IDURI_SELECT20 },
{ STURI_OPENID20, 0 },
{ STURI_OPENID11, 0 },
{ STURI_OPENID10, 0 }
};
enum {
st_index_1 = 2, st_index_2 = 1
};
static inline bool is_qelement(const XML_Char *n,const char *qen) {
return !strcasecmp(n,qen);
}
static inline bool is_element(const XML_Char *n,const char *en) {
if(!strcasecmp(n,en)) return true;
int nl = strlen(n), enl = strlen(en);
if( (nl>=(enl+1)) && n[nl-enl-1]=='\t'
&& !strcasecmp(&n[nl-enl],en) )
return true;
return false;
}
static long element_priority(const XML_Char **a) {
for(;*a;++a)
if(!strcasecmp(*(a++),"priority")) {
long rv;
return (sscanf(*a,"%ld",&rv)==1)?rv:-1;
}
return -1;
}
/* TODO: ideally all attributes should be
* retrieved in one run */
static const char *element_attr(const XML_Char **a, const char *at) {
for(;*a;++a)
if(!strcasecmp(*(a++),at)) {
return *a;
}
return 0;
}
class idigger_t : public util::curl_t, public util::expat_t {
public:
string xri_proxy;
enum {
xmode_html = 1, xmode_xrd = 2, xmode_cid = 4,
xmode_noredirs = 8
};
int xmode;
string xrds_location;
string http_content_type;
service_t html_openid1;
service_t html_openid2;
string cdata_buf;
long status_code;
string status_string;
typedef list<string> pt_stack_t;
pt_stack_t pt_stack;
int skipping;
bool parser_choked;
string save_html;
XRD_t *xrd;
service_t *xrd_service;
string* cdata;
idigger_t()
: util::curl_t(easy_init()),
util::expat_t(0),
xri_proxy(XRI_PROXY_URL) {
CURLcode r;
(r=misc_sets())
|| (r=set_write())
|| (r=set_header())
;
if(r)
throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
}
~idigger_t() throw() { }
void yadiscover(endpoint_discovery_iterator oi,const string& yurl,const char **types,bool redirs) {
idiscovery_t idis;
idis.xri_identity = false;
discover_at(idis,yurl,xmode_html|xmode_xrd|(redirs?0:xmode_noredirs));
if(!xrds_location.empty()) {
idis.clear();
discover_at(idis,xrds_location,xmode_xrd);
}
idis.normalized_id = idis.canonicalized_id = yurl;
service_type_t st;
for(st.uri=*types;*types;st.uri=*(++types))
queue_endpoints(oi,idis,&st);
}
string discover(endpoint_discovery_iterator& oi,const string& identity) {
string rv;
idiscovery_t idis;
string::size_type fsc = identity.find_first_not_of(whitespace);
if(fsc==string::npos)
throw bad_input(OPKELE_CP_ "whitespace-only identity");
string::size_type lsc = identity.find_last_not_of(whitespace);
assert(lsc!=string::npos);
if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1))
fsc += sizeof("xri://")-1;
if((fsc+1)>=lsc)
throw bad_input(OPKELE_CP_ "not a character of importance in identity");
string id(identity,fsc,lsc-fsc+1);
idis.clear();
if(strchr(i_leaders,id[0])) {
/* TODO: further normalize xri identity? Like folding case
* or whatever... */
rv = id;
set<string> cids;
for(const struct service_type_t *st=op_service_types;
st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st) {
idis.clear();
discover_at( idis,
xri_proxy + util::url_encode(id)+
"?_xrd_t="+util::url_encode(st->uri)+
"&_xrd_r=application/xrd%2Bxml"
";sep=true;refs=true",
xmode_xrd );
if(status_code==241) continue;
if(status_code!=100)
throw failed_xri_resolution(OPKELE_CP_
"XRI resolution failed with '"+status_string+"' message"
", while looking for SEP with type '"+st->uri+"'", status_code);
if(idis.xrd.canonical_ids.empty())
throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID found for XRI identity found");
string cid = idis.xrd.canonical_ids.begin()->second;
if(cids.find(cid)==cids.end()) {
cids.insert(cid);
idis.clear();
discover_at( idis,
xri_proxy + util::url_encode(id)+
"?_xrd_t="+util::url_encode(st->uri)+
"&_xrd_r=application/xrd%2Bxml"
";sep=true;refs=true",
xmode_xrd );
if(status_code==241) continue;
if(status_code!=100)
throw failed_xri_resolution(OPKELE_CP_
"XRI resolution failed with '"+status_string+"' message"
", while looking for SEP with type '"+st->uri+"'"
" on canonical id", status_code);
}
idis.canonicalized_id = cid;
idis.normalized_id = rv; idis.xri_identity = true;
queue_endpoints(oi,idis,st);
}
}else{
idis.xri_identity = false;
if(id.find("://")==string::npos)
id.insert(0,"http://");
string::size_type fp = id.find('#');
if(fp!=string::npos) {
string::size_type qp = id.find('?');
if(qp==string::npos || qp<fp)
id.erase(fp);
else if(qp>fp)
id.erase(fp,qp-fp);
}
rv = idis.normalized_id = util::rfc_3986_normalize_uri(id);
discover_at(idis,id,xmode_html|xmode_xrd);
const char * eu = 0;
CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu);
if(r)
throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r);
string cid = util::strip_uri_fragment_part( idis.canonicalized_id = util::rfc_3986_normalize_uri(eu) );
if(xrds_location.empty()) {
html2xrd(oi,idis);
}else{
idis.clear();
idis.canonicalized_id = cid;
discover_at(idis,xrds_location,xmode_xrd);
if(idis.xrd.empty())
html2xrd(oi,idis);
else{
for(const service_type_t *st=op_service_types;
st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st)
queue_endpoints(oi,idis,st);
}
}
}
return rv;
}
void discover_at(idiscovery_t& idis,const string& url,int xm) {
CURLcode r = easy_setopt(CURLOPT_MAXREDIRS, (xm&xmode_noredirs)?0:5);
if(r)
throw exception_curl(OPKELE_CP_ "failed to set curly maxredirs option");
if( (r=easy_setopt(CURLOPT_URL,url.c_str())) )
throw exception_curl(OPKELE_CP_ "failed to set curly urlie",r);
http_content_type.clear();
xmode = xm;
prepare_to_parse();
if(xmode&xmode_html) {
xrds_location.clear();
save_html.clear();
save_html.reserve(max_html);
}
xrd = &idis.xrd;
r = easy_perform();
if(r && r!=CURLE_WRITE_ERROR)
throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
if(!parser_choked) {
parse(0,0,true);
}else{
/* TODO: do not bother if we've seen xml */
try {
util::tidy_doc_t td = util::tidy_doc_t::create();
if(!td)
throw exception_tidy(OPKELE_CP_ "failed to create htmltidy document");
#ifndef NDEBUG
td.opt_set(TidyQuiet,false);
td.opt_set(TidyShowWarnings,false);
#endif /* NDEBUG */
td.opt_set(TidyForceOutput,true);
td.opt_set(TidyXhtmlOut,true);
td.opt_set(TidyDoctypeMode,TidyDoctypeOmit);
td.opt_set(TidyMark,false);
if(td.parse_string(save_html)<=0)
throw exception_tidy(OPKELE_CP_ "tidy failed to parse document");
if(td.clean_and_repair()<=0)
throw exception_tidy(OPKELE_CP_ "tidy failed to clean and repair");
util::tidy_buf_t tide;
if(td.save_buffer(tide)<=0)
throw exception_tidy(OPKELE_CP_ "tidy failed to save buffer");
prepare_to_parse();
parse(tide.c_str(),tide.size(),true);
}catch(exception_tidy& et) { }
}
save_html.clear();
}
void prepare_to_parse() {
(*(expat_t*)this) = parser_create_ns();
set_user_data(); set_element_handler();
set_character_data_handler();
if(xmode&xmode_html) {
html_openid1.clear(); html_openid2.clear();
parser_choked = false;
}
cdata = 0; xrd_service = 0; skipping = 0;
pt_stack.clear();
status_code = 100; status_string.clear();
}
void html2xrd(endpoint_discovery_iterator& oi,idiscovery_t& id) {
XRD_t& x = id.xrd;
if(!html_openid2.uris.empty()) {
html_openid2.types.insert(STURI_OPENID20);
x.services.add(-1,html_openid2);
queue_endpoints(oi,id,&op_service_types[st_index_2]);
}
if(!html_openid1.uris.empty()) {
html_openid1.types.insert(STURI_OPENID11);
x.services.add(-1,html_openid1);
queue_endpoints(oi,id,&op_service_types[st_index_1]);
}
}
size_t write(void *p,size_t s,size_t nm) {
/* TODO: limit total size */
size_t bytes = s*nm;
const char *inbuf = (const char*)p;
if(xmode&xmode_html) {
size_t mbts = save_html.capacity()-save_html.size();
size_t bts = 0;
if(mbts>0) {
bts = (bytes>mbts)?mbts:bytes;
save_html.append(inbuf,bts);
}
if(skipping<0) return bts;
}
if(skipping<0) return 0;
bool rp = parse(inbuf,bytes,false);
if(!rp) {
parser_choked = true;
skipping = -1;
if(!(xmode&xmode_html))
bytes = 0;
}
return bytes;
}
size_t header(void *p,size_t s,size_t nm) {
size_t bytes = s*nm;
const char *h = (const char*)p;
const char *colon = (const char*)memchr(p,':',bytes);
const char *space = (const char*)memchr(p,' ',bytes);
if(space && ( (!colon) || space<colon ) ) {
xrds_location.clear(); http_content_type.clear();
}else if(colon) {
const char *hv = ++colon;
- int hnl = colon-h;
+ size_t hnl = colon-h;
int rb;
for(rb = bytes-hnl-1;rb>0 && isspace(*hv);++hv,--rb);
while(rb>0 && isspace(hv[rb-1])) --rb;
if(rb) {
if( (hnl>=sizeof(XRDS_HEADER))
&& !strncasecmp(h,XRDS_HEADER":",
sizeof(XRDS_HEADER)) ) {
xrds_location.assign(hv,rb);
}else if( (hnl>=sizeof(CT_HEADER))
&& !strncasecmp(h,CT_HEADER":",
sizeof(CT_HEADER)) ) {
const char *sc = (const char*)memchr(
hv,';',rb);
http_content_type.assign(hv,sc?(sc-hv):rb);
}
}
}
return curl_t::header(p,s,nm);
}
void start_element(const XML_Char *n,const XML_Char **a) {
if(skipping<0) return;
if(skipping) {
if(xmode&xmode_html)
html_start_element(n,a);
++skipping; return;
}
if(pt_stack.empty()) {
if(is_qelement(n,NSURI_XRDS "\tXRDS"))
return;
if(is_qelement(n,NSURI_XRD "\tXRD")) {
assert(xrd);
xrd->clear();
pt_stack.push_back(n);
}else if(xmode&xmode_html) {
html_start_element(n,a);
}else{
skipping = -1;
}
}else{
int pt_s = pt_stack.size();
if(pt_s==1) {
if(is_qelement(n,NSURI_XRD "\tCanonicalID")) {
assert(xrd);
cdata = &(xrd->canonical_ids.add(element_priority(a),string()));
}else if(is_qelement(n,NSURI_XRD "\tLocalID")) {
assert(xrd);
cdata = &(xrd->local_ids.add(element_priority(a),string()));
}else if(is_qelement(n,NSURI_XRD "\tProviderID")) {
assert(xrd);
cdata = &(xrd->provider_id);
}else if(is_qelement(n,NSURI_XRD "\tService")) {
assert(xrd);
xrd_service = &(xrd->services.add(element_priority(a),
service_t()));
pt_stack.push_back(n);
}else if(is_qelement(n,NSURI_XRD "\tStatus")) {
for(;*a;) {
if(!strcasecmp(*(a++),"code")) {
if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) {
cdata = &status_string;
pt_stack.push_back(n);
break;
}
}else
++a;
}
}else if(is_qelement(n,NSURI_XRD "\tExpires")) {
assert(xrd);
cdata_buf.clear();
cdata = &cdata_buf;
}else if(xmode&xmode_html) {
html_start_element(n,a);
}else{
skipping = 1;
}
}else if(pt_s==2) {
if(is_qelement(pt_stack.back().c_str(), NSURI_XRD "\tService")) {
if(is_qelement(n,NSURI_XRD "\tType")) {
assert(xrd); assert(xrd_service);
cdata_buf.clear();
cdata = &cdata_buf;
}else if(is_qelement(n,NSURI_XRD "\tURI")) {
assert(xrd); assert(xrd_service);
const char *append = element_attr(a,"append");
xrd::uri_t& uri = xrd_service->uris.add(element_priority(a),xrd::uri_t("",append?append:""));
cdata = &uri.uri;
}else if(is_qelement(n,NSURI_XRD "\tLocalID")
|| is_qelement(n,NSURI_OPENID10 "\tDelegate") ) {
assert(xrd); assert(xrd_service);
cdata = &(xrd_service->local_ids.add(element_priority(a),string()));
}else if(is_qelement(n,NSURI_XRD "\tProviderID")) {
assert(xrd); assert(xrd_service);
cdata = &(xrd_service->provider_id);
}else{
skipping = 1;
}
}else
skipping = 1;
}else if(xmode&xmode_html) {
html_start_element(n,a);
}else{
skipping = 1;
}
}
}
void end_element(const XML_Char *n) {
if(skipping<0) return;
if(skipping) {
--skipping; return;
}
if(is_qelement(n,NSURI_XRD "\tType")) {
assert(xrd); assert(xrd_service); assert(cdata==&cdata_buf);
xrd_service->types.insert(cdata_buf);
}else if(is_qelement(n,NSURI_XRD "\tService")) {
assert(xrd); assert(xrd_service);
assert(!pt_stack.empty());
assert(pt_stack.back()==(NSURI_XRD "\tService"));
pt_stack.pop_back();
xrd_service = 0;
}else if(is_qelement(n,NSURI_XRD "\tStatus")) {
assert(xrd);
if(is_qelement(pt_stack.back().c_str(),n)) {
assert(cdata==&status_string);
pt_stack.pop_back();
if(status_code!=100)
skipping = -1;
}
}else if(is_qelement(n,NSURI_XRD "\tExpires")) {
assert(xrd);
xrd->expires = util::w3c_to_time(cdata_buf);
}else if((xmode&xmode_html) && is_element(n,"head")) {
skipping = -1;
}
cdata = 0;
}
void character_data(const XML_Char *s,int l) {
if(skipping) return;
if(cdata) cdata->append(s,l);
}
void html_start_element(const XML_Char *n,const XML_Char **a) {
if(is_element(n,"meta")) {
bool heq = false;
string l;
for(;*a;a+=2) {
if(!( strcasecmp(a[0],"http-equiv")
|| strcasecmp(a[1],XRDS_HEADER) ))
heq = true;
else if(!strcasecmp(a[0],"content"))
l.assign(a[1]);
}
if(heq)
xrds_location = l;
}else if(is_element(n,"link")) {
string rels;
string href;
for(;*a;a+=2) {
if( !strcasecmp(a[0],"rel") ) {
rels.assign(a[1]);
}else if( !strcasecmp(a[0],"href") ) {
const char *ns = a[1];
for(;*ns && isspace(*ns);++ns);
href.assign(ns);
string::size_type lns=href.find_last_not_of(whitespace);
href.erase(lns+1);
}
}
for(string::size_type ns=rels.find_first_not_of(whitespace);
ns!=string::npos; ns=rels.find_first_not_of(whitespace,ns)) {
string::size_type s = rels.find_first_of(whitespace,ns);
string rel;
if(s==string::npos) {
rel.assign(rels,ns,string::npos);
ns = string::npos;
}else{
rel.assign(rels,ns,s-ns);
ns = s;
}
if(rel=="openid.server")
html_openid1.uris.add(-1,xrd::uri_t(href));
else if(rel=="openid.delegate")
html_openid1.local_ids.add(-1,href);
else if(rel=="openid2.provider")
html_openid2.uris.add(-1,xrd::uri_t(href));
else if(rel=="openid2.local_id")
html_openid2.local_ids.add(-1,href);
}
}else if(is_element(n,"body")) {
skipping = -1;
}
}
void queue_endpoints(endpoint_discovery_iterator& oi,
const idiscovery_t &id,
const service_type_t *st) {
openid_endpoint_t ep;
ep.claimed_id = id.canonicalized_id;
for(xrd::services_t::const_iterator isvc=id.xrd.services.begin();
isvc!=id.xrd.services.end(); ++isvc) {
const xrd::service_t svc = isvc->second;
if(svc.types.find(st->uri)==svc.types.end()) continue;
for(xrd::uris_t::const_iterator iu=svc.uris.begin();iu!=svc.uris.end();++iu) {
ep.uri = iu->second.uri;
if(id.xri_identity) {
if(iu->second.append=="qxri") {
ep.uri += id.normalized_id;
} /* TODO: else handle other append attribute values */
}
if(st->forceid) {
ep.local_id = ep.claimed_id = st->forceid;
*(oi++) = ep;
}else{
if(svc.local_ids.empty()) {
ep.local_id = ep.claimed_id;
*(oi++) = ep;
}else{
for(xrd::local_ids_t::const_iterator ilid=svc.local_ids.begin();
ilid!=svc.local_ids.end(); ++ilid) {
ep.local_id = ilid->second;
*(oi++) = ep;
}
}
}
}
}
}
};
string idiscover(endpoint_discovery_iterator oi,const string& identity) {
idigger_t idigger;
return idigger.discover(oi,identity);
}
void yadiscover(endpoint_discovery_iterator oi,const string& yurl,const char **types,bool redirs) try {
idigger_t idigger;
idigger.yadiscover(oi,yurl,types,redirs);
}catch(exception_curl& ec) {
if(redirs || ec._error!=CURLE_TOO_MANY_REDIRECTS)
throw;
}
}
diff --git a/lib/expat.cc b/lib/expat.cc
index fa6fdde..c4dab7e 100644
--- a/lib/expat.cc
+++ b/lib/expat.cc
@@ -1,96 +1,97 @@
#include <opkele/expat.h>
namespace opkele {
namespace util {
expat_t::~expat_t() throw() {
if(_x)
XML_ParserFree(_x);
}
expat_t& expat_t::operator=(XML_Parser x) {
if(_x)
XML_ParserFree(_x);
_x = x;
+ return *this;
}
static void _start_element(void* ud,const XML_Char *n,const XML_Char **a) {
((expat_t*)ud)->start_element(n,a);
}
static void _end_element(void *ud,const XML_Char *n) {
((expat_t*)ud)->end_element(n);
}
void expat_t::set_element_handler() {
assert(_x);
XML_SetElementHandler(_x,_start_element,_end_element);
}
static void _character_data(void *ud,const XML_Char *s,int l) {
((expat_t*)ud)->character_data(s,l);
}
void expat_t::set_character_data_handler() {
assert(_x);
XML_SetCharacterDataHandler(_x,_character_data);
}
static void _processing_instruction(void *ud,const XML_Char *t,const XML_Char *d) {
((expat_t*)ud)->processing_instruction(t,d);
}
void expat_t::set_processing_instruction_handler() {
assert(_x);
XML_SetProcessingInstructionHandler(_x,_processing_instruction);
}
static void _comment(void *ud,const XML_Char *d) {
((expat_t*)ud)->comment(d);
}
void expat_t::set_comment_handler() {
assert(_x);
XML_SetCommentHandler(_x,_comment);
}
static void _start_cdata_section(void *ud) {
((expat_t*)ud)->start_cdata_section();
}
static void _end_cdata_section(void *ud) {
((expat_t*)ud)->end_cdata_section();
}
void expat_t::set_cdata_section_handler() {
assert(_x);
XML_SetCdataSectionHandler(_x,_start_cdata_section,_end_cdata_section);
}
static void _default_handler(void *ud,const XML_Char *s,int l) {
((expat_t*)ud)->default_handler(s,l);
}
void expat_t::set_default_handler() {
assert(_x);
XML_SetDefaultHandler(_x,_default_handler);
}
void expat_t::set_default_handler_expand() {
assert(_x);
XML_SetDefaultHandlerExpand(_x,_default_handler);
}
static void _start_namespace_decl(void *ud,const XML_Char *p,const XML_Char *u) {
((expat_t*)ud)->start_namespace_decl(p,u);
}
static void _end_namespace_decl(void *ud,const XML_Char *p) {
((expat_t*)ud)->end_namespace_decl(p);
}
void expat_t::set_namespace_decl_handler() {
assert(_x);
XML_SetNamespaceDeclHandler(_x,_start_namespace_decl,_end_namespace_decl);
}
}
}
diff --git a/lib/extension.cc b/lib/extension.cc
index f7aaea5..0f121ca 100644
--- a/lib/extension.cc
+++ b/lib/extension.cc
@@ -1,26 +1,26 @@
#include <opkele/exception.h>
#include <opkele/extension.h>
namespace opkele {
void extension_t::rp_checkid_hook(basic_openid_message&) {
throw not_implemented(OPKELE_CP_ "RP checkid_* hook not implemented"); }
void extension_t::rp_id_res_hook(const basic_openid_message&,
const basic_openid_message&) {
throw not_implemented(OPKELE_CP_ "RP id_res hook not implemented"); }
void extension_t::op_checkid_hook(const basic_openid_message&) {
throw not_implemented(OPKELE_CP_ "OP checkid_* hook not implemented"); }
- void extension_t::op_id_res_hook(basic_openid_message& om) {
+ void extension_t::op_id_res_hook(basic_openid_message&) {
throw not_implemented(OPKELE_CP_ "OP id_res hook not implemented"); }
void extension_t::checkid_hook(basic_openid_message&) {
throw not_implemented(OPKELE_CP_ "deprecated consumer checkid_* hook not implemented"); }
void extension_t::id_res_hook(const basic_openid_message&,
const basic_openid_message&) {
throw not_implemented(OPKELE_CP_ "deprecated consumer id_res hook not implemented"); }
void extension_t::checkid_hook(const basic_openid_message&,basic_openid_message&) {
throw not_implemented(OPKELE_CP_ "deprecated server checkid hook not implemented"); }
}
diff --git a/lib/openid_message.cc b/lib/openid_message.cc
index fdb4b04..521ea85 100644
--- a/lib/openid_message.cc
+++ b/lib/openid_message.cc
@@ -1,265 +1,265 @@
#include <cassert>
#include <opkele/types.h>
#include <opkele/exception.h>
#include <opkele/util.h>
#include <opkele/debug.h>
#include "config.h"
namespace opkele {
using std::input_iterator_tag;
using std::unary_function;
struct __om_copier : public unary_function<const string&,void> {
public:
const basic_openid_message& from;
basic_openid_message& to;
- __om_copier(basic_openid_message& to,const basic_openid_message& from)
- : from(from), to(to) {
+ __om_copier(basic_openid_message& t,const basic_openid_message& f)
+ : from(f), to(t) {
to.reset_fields();
}
result_type operator()(argument_type f) {
to.set_field(f,from.get_field(f)); }
};
basic_openid_message::basic_openid_message(const basic_openid_message& x) {
x.copy_to(*this);
}
void basic_openid_message::copy_to(basic_openid_message& x) const {
for_each(fields_begin(),fields_end(),
__om_copier(x,*this) );
}
struct __om_ns_finder : public unary_function<const string&,bool> {
public:
const basic_openid_message& om;
const string& uri;
- __om_ns_finder(const basic_openid_message& om,
- const string& uri) : om(om), uri(uri) { }
+ __om_ns_finder(const basic_openid_message& m,
+ const string& u) : om(m), uri(u) { }
result_type operator()(argument_type f) {
return
(!strncmp(f.c_str(),"ns.",sizeof("ns.")-1))
&& om.get_field(f)==uri ;
}
};
bool basic_openid_message::has_ns(const string& uri) const {
fields_iterator ei = fields_end();
fields_iterator i = find_if(fields_begin(),fields_end(),
__om_ns_finder(*this,uri));
return !(i==ei);
}
string basic_openid_message::get_ns(const string& uri) const {
fields_iterator ei = fields_end();
fields_iterator i = find_if(fields_begin(),fields_end(),
__om_ns_finder(*this,uri));
if(i==ei)
throw failed_lookup(OPKELE_CP_ string("failed to find namespace ")+uri);
return i->substr(3);
}
struct __om_query_builder : public unary_function<const string&,void> {
public:
const basic_openid_message& om;
- string& rv;
bool first;
+ string& rv;
- __om_query_builder(string& rv,const basic_openid_message& om)
- : om(om), first(true), rv(rv) {
+ __om_query_builder(string& r,const basic_openid_message& m)
+ : om(m), first(true), rv(r) {
for_each(om.fields_begin(),om.fields_end(),*this);
}
- __om_query_builder(string& rv,const basic_openid_message& om,const string& url)
- : om(om), first(true), rv(rv) {
- rv = url;
+ __om_query_builder(string& r,const basic_openid_message& m,const string& u)
+ : om(m), first(true), rv(r) {
+ rv = u;
if(rv.find('?')==string::npos)
rv += '?';
else
first = false;
for_each(om.fields_begin(),om.fields_end(),*this);
}
result_type operator()(argument_type f) {
if(first)
first = false;
else
rv += '&';
rv += "openid."; rv+= f;
rv += '=';
rv += util::url_encode(om.get_field(f));
}
};
string basic_openid_message::append_query(const string& url) const {
string rv;
return __om_query_builder(rv,*this,url).rv;
}
string basic_openid_message::query_string() const {
string rv;
return __om_query_builder(rv,*this).rv;
}
void basic_openid_message::reset_fields() {
throw not_implemented(OPKELE_CP_ "reset_fields() not implemented");
}
- void basic_openid_message::set_field(const string& n,const string& v) {
+ void basic_openid_message::set_field(const string&,const string&) {
throw not_implemented(OPKELE_CP_ "set_field() not implemented");
}
- void basic_openid_message::reset_field(const string& n) {
+ void basic_openid_message::reset_field(const string&) {
throw not_implemented(OPKELE_CP_ "reset_field() not implemented");
}
void basic_openid_message::from_keyvalues(const string& kv) {
reset_fields();
string::size_type p = 0;
while(true) {
string::size_type co = kv.find(':',p);
if(co==string::npos)
break;
#ifndef POSTELS_LAW
string::size_type nl = kv.find('\n',co+1);
if(nl==string::npos)
throw bad_input(OPKELE_CP_ "malformed input");
if(nl>co)
insert(value_type(kv.substr(p,co-p),kv.substr(co+1,nl-co-1)));
p = nl+1;
#else /* POSTELS_LAW */
string::size_type lb = kv.find_first_of("\r\n",co+1);
if(lb==string::npos) {
set_field(kv.substr(p,co-p),kv.substr(co+1));
break;
}
if(lb>co)
set_field(kv.substr(p,co-p),kv.substr(co+1,lb-co-1));
string::size_type nolb = kv.find_first_not_of("\r\n",lb);
if(nolb==string::npos)
break;
p = nolb;
#endif /* POSTELS_LAW */
}
}
struct __om_kv_outputter : public unary_function<const string&,void> {
public:
const basic_openid_message& om;
ostream& os;
- __om_kv_outputter(const basic_openid_message& om,ostream& os)
- : om(om), os(os) { }
+ __om_kv_outputter(const basic_openid_message& m,ostream& s)
+ : om(m), os(s) { }
result_type operator()(argument_type f) {
os << f << ':' << om.get_field(f) << '\n';
}
};
void basic_openid_message::to_keyvalues(ostream& o) const {
for_each(fields_begin(),fields_end(),__om_kv_outputter(*this,o));
}
struct __om_html_outputter : public unary_function<const string&,void> {
public:
const basic_openid_message& om;
ostream& os;
- __om_html_outputter(const basic_openid_message& om,ostream& os)
- : om(om), os(os) { }
+ __om_html_outputter(const basic_openid_message& m,ostream& s)
+ : om(m), os(s) { }
result_type operator()(argument_type f) {
os <<
"<input type=\"hidden\""
" name=\"" << util::attr_escape(f) << "\""
" value=\"" << util::attr_escape(om.get_field(f)) << "\" />";
}
};
void basic_openid_message::to_htmlhiddens(ostream& o) const {
for_each(fields_begin(),fields_end(),__om_html_outputter(*this,o));
}
void basic_openid_message::add_to_signed(const string& fields) {
string::size_type fnc = fields.find_first_not_of(",");
if(fnc==string::npos)
throw bad_input(OPKELE_CP_ "Trying to add nothing in particular to the list of signed fields");
string signeds;
try {
signeds = get_field("signed");
string::size_type lnc = signeds.find_last_not_of(",");
if(lnc==string::npos)
signeds.assign(fields,fnc,fields.size()-fnc);
else{
string::size_type ss = signeds.size();
if(lnc==(ss-1)) {
signeds+= ',';
signeds.append(fields,fnc,fields.size()-fnc);
}else{
if(lnc<(ss-2))
signeds.replace(lnc+2,ss-lnc-2,
fields,fnc,fields.size()-fnc);
else
signeds.append(fields,fnc,fields.size()-fnc);
}
}
}catch(failed_lookup&) {
signeds.assign(fields,fnc,fields.size()-fnc);
}
set_field("signed",signeds);
}
string basic_openid_message::find_ns(const string& uri,const char *pfx) const {
if(has_field("ns"))
return get_ns(uri);
return pfx;
}
string basic_openid_message::allocate_ns(const string& uri,const char *pfx) {
if(!has_field("ns"))
return pfx;
if(has_ns(uri))
throw bad_input(OPKELE_CP_ "OpenID message already contains namespace");
string rv = pfx;
if(has_field("ns."+rv)) {
string::reference c=rv[rv.length()];
for(c='a';c<='z' && has_field("ns."+rv);++c);
if(c=='z')
throw exception(OPKELE_CP_ "Failed to allocate namespace");
}
set_field("ns."+rv,uri);
return rv;
}
void openid_message_t::copy_to(basic_openid_message& x) const {
x.reset_fields();
for(const_iterator i=begin();i!=end();++i)
x.set_field(i->first,i->second);
}
bool openid_message_t::has_field(const string& n) const {
return find(n)!=end();
}
const string& openid_message_t::get_field(const string& n) const {
const_iterator i=find(n);
if(i==end())
throw failed_lookup(OPKELE_CP_ n+": no such field");
return i->second;
}
openid_message_t::fields_iterator openid_message_t::fields_begin() const {
return util::map_keys_iterator<const_iterator,string,const string&,const string*>(begin(),end());
}
openid_message_t::fields_iterator openid_message_t::fields_end() const {
return util::map_keys_iterator<const_iterator,string,const string&,const string*>(end(),end());
}
void openid_message_t::reset_fields() {
clear();
}
void openid_message_t::set_field(const string& n,const string& v) {
(*this)[n]=v;
}
void openid_message_t::reset_field(const string& n) {
erase(n);
}
}
diff --git a/lib/prequeue_rp.cc b/lib/prequeue_rp.cc
index e242f87..3aa960f 100644
--- a/lib/prequeue_rp.cc
+++ b/lib/prequeue_rp.cc
@@ -1,81 +1,81 @@
#include <iostream>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <opkele/exception.h>
#include <opkele/prequeue_rp.h>
#include <opkele/discovery.h>
#include <opkele/uris.h>
#include <opkele/data.h>
#include <opkele/util.h>
#include <opkele/curl.h>
#include <opkele/debug.h>
namespace opkele {
class __OP_verifier_good_input : public exception {
public:
__OP_verifier_good_input(OPKELE_E_PARS)
: exception(OPKELE_E_CONS) { }
};
class OP_verifier : public iterator<output_iterator_tag,openid_endpoint_t,void> {
public:
const string& OP;
const string& id;
OP_verifier(const string& o,const string& i)
: OP(o), id(i) { }
OP_verifier& operator*() { return *this; }
OP_verifier& operator=(const openid_endpoint_t& oep) {
if(oep.uri==OP) {
if(oep.claimed_id==IDURI_SELECT20
|| oep.local_id==IDURI_SELECT20 )
throw bad_input(OPKELE_CP_ "claimed_id is an OP-Id");
if(oep.local_id==id)
throw __OP_verifier_good_input(OPKELE_CP_ "Found corresponding endpoint");
}
return *this;
}
OP_verifier& operator++() { return *this; }
OP_verifier& operator++(int) { return *this; }
};
void prequeue_RP::verify_OP(const string& OP,const string& claimed_id,const string& identity) const {
try {
idiscover(OP_verifier(OP,identity),claimed_id);
throw id_res_unauthorized(OPKELE_CP_
"OP is not authorized to make an assertion regarding the identity");
}catch(__OP_verifier_good_input& ovgi) {
}
}
class endpoint_queuer : public iterator<output_iterator_tag,openid_endpoint_t,void> {
public:
prequeue_RP& rp;
- endpoint_queuer(prequeue_RP& rp) : rp(rp) { }
+ endpoint_queuer(prequeue_RP& r) : rp(r) { }
endpoint_queuer& operator*() { return *this; }
endpoint_queuer& operator=(const openid_endpoint_t& oep) {
rp.queue_endpoint(oep); return *this; }
endpoint_queuer& operator++() { return *this; }
endpoint_queuer& operator++(int) { return *this; }
};
void prequeue_RP::initiate(const string& usi) {
begin_queueing();
set_normalized_id( idiscover(endpoint_queuer(*this),usi) );
end_queueing();
}
- void prequeue_RP::set_normalized_id(const string& nid) {
+ void prequeue_RP::set_normalized_id(const string&) {
}
const string prequeue_RP::get_normalized_id() const {
throw not_implemented(OPKELE_CP_ "get_normalized_id() is not implemented");
}
}
diff --git a/lib/sreg.cc b/lib/sreg.cc
index b40cd45..0bd4d2e 100644
--- a/lib/sreg.cc
+++ b/lib/sreg.cc
@@ -1,160 +1,158 @@
#include <opkele/exception.h>
#include <opkele/sreg.h>
#include <opkele/uris.h>
#include <algorithm>
namespace opkele {
using std::find;
static const struct _sreg_field {
const char *fieldname;
sreg_t::fieldbit_t fieldbit;
} fields[] = {
{ "nickname", sreg_t::field_nickname },
{ "email", sreg_t::field_email },
{ "fullname", sreg_t::field_fullname },
{ "dob", sreg_t::field_dob },
{ "gender", sreg_t::field_gender },
{ "postcode", sreg_t::field_postcode },
{ "country", sreg_t::field_country },
{ "language", sreg_t::field_language },
{ "timezone", sreg_t::field_timezone }
};
# define fields_BEGIN fields
# define fields_END &fields[sizeof(fields)/sizeof(*fields)]
typedef const struct _sreg_field *fields_iterator;
bool operator==(const struct _sreg_field& fd,const string& fn) {
return fd.fieldname==fn;
}
void sreg_t::rp_checkid_hook(basic_openid_message& om) {
string fr, fo;
for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
if(f->fieldbit&fields_required) {
if(!fr.empty()) fr+=",";
fr += f->fieldname;
}
if(f->fieldbit&fields_optional) {
if(!fo.empty()) fo+=",";
fo += f->fieldname;
}
}
string pfx = om.allocate_ns(OIURI_SREG11,"sreg");
if(!fr.empty()) om.set_field(pfx+".required",fr);
if(!fo.empty()) om.set_field(pfx+".optional",fo);
if(!policy_url.empty()) om.set_field(pfx+".policy_url",policy_url);
}
void sreg_t::checkid_hook(basic_openid_message& om) {
rp_checkid_hook(om); }
void sreg_t::rp_id_res_hook(const basic_openid_message& om,
const basic_openid_message& sp) {
clear();
string pfx;
try {
pfx = om.find_ns(OIURI_SREG11,"sreg");
- }catch(failed_lookup& fl) {
+ }catch(failed_lookup&) {
try {
pfx = om.find_ns(OIURI_SREG10,"sreg");
- }catch(failed_lookup& fl) {
- return;
- }
+ }catch(failed_lookup&) { return; }
}
pfx += '.';
for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
string fn = pfx; fn+=f->fieldname;
if(!sp.has_field(fn)) continue;
has_fields |= f->fieldbit;
response[f->fieldbit]=sp.get_field(fn);
}
}
void sreg_t::id_res_hook(const basic_openid_message& om,
const basic_openid_message& sp) {
rp_id_res_hook(om,sp); }
const string& sreg_t::get_field(fieldbit_t fb) const {
response_t::const_iterator i = response.find(fb);
if(i==response.end())
throw failed_lookup(OPKELE_CP_ "no field data available");
return i->second;
}
void sreg_t::set_field(fieldbit_t fb,const string& fv) {
response[fb] = fv;
has_fields |= fb;
}
void sreg_t::reset_field(fieldbit_t fb) {
has_fields &= ~fb;
response.erase(fb);
}
void sreg_t::clear() {
has_fields = 0; response.clear();
}
static long fields_list_to_bitmask(string& fl) {
long rv = 0;
while(!fl.empty()) {
string::size_type co = fl.find(',');
string fn;
if(co==string::npos) {
fn = fl; fl.erase();
}else{
fn = fl.substr(0,co); fl.erase(0,co+1);
}
fields_iterator f = find(fields_BEGIN,fields_END,fn);
if(f!=fields_END)
rv |= f->fieldbit;
}
return rv;
}
void sreg_t::op_checkid_hook(const basic_openid_message& inm) {
string ins = inm.find_ns(OIURI_SREG11,"sreg");
fields_optional = 0; fields_required = 0; policy_url.erase();
fields_response = 0;
try {
string fl = inm.get_field(ins+".required");
fields_required = fields_list_to_bitmask(fl);
}catch(failed_lookup&) { }
try {
string fl = inm.get_field(ins+".optional");
fields_optional = fields_list_to_bitmask(fl);
}catch(failed_lookup&) { }
try {
policy_url = inm.get_field(ins+".policy_url");
}catch(failed_lookup&) { }
}
void sreg_t::op_id_res_hook(basic_openid_message& oum) {
string ons = oum.allocate_ns(OIURI_SREG11,"sreg");
fields_response &= has_fields;
string signeds = "ns."+ons;
for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
if(!(f->fieldbit&fields_response)) continue;
signeds +=',';
string pn = ons; pn += '.'; pn += f->fieldname;
signeds += pn;
oum.set_field(pn,get_field(f->fieldbit));
}
oum.add_to_signed(signeds);
}
void sreg_t::checkid_hook(const basic_openid_message& inm,
basic_openid_message& oum) {
op_checkid_hook(inm);
setup_response(inm,oum);
op_id_res_hook(oum);
}
void sreg_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) {
setup_response();
}
void sreg_t::setup_response() {
fields_response = (fields_required|fields_optional)&has_fields;
}
}
diff --git a/lib/verify_op.cc b/lib/verify_op.cc
index ab21b4f..c493c12 100644
--- a/lib/verify_op.cc
+++ b/lib/verify_op.cc
@@ -1,53 +1,53 @@
#include <opkele/verify_op.h>
#include <opkele/discovery.h>
#include <opkele/exception.h>
#include <opkele/util.h>
#include <opkele/uris.h>
namespace opkele {
using std::output_iterator_tag;
class __RP_verifier_good_input : public exception {
public:
__RP_verifier_good_input(OPKELE_E_PARS)
: exception(OPKELE_E_CONS) { }
};
class RP_verifier : public iterator<output_iterator_tag,openid_endpoint_t,void> {
public:
- int seen;
const string& return_to;
+ int seen;
RP_verifier(const string& rt)
: return_to(rt), seen(0) { }
RP_verifier& operator*() { return *this; }
RP_verifier& operator=(const openid_endpoint_t& oep) {
if(util::uri_matches_realm(return_to,oep.uri))
throw __RP_verifier_good_input(OPKELE_CP_ "Found matching realm");
return *this;
}
RP_verifier& operator++() { ++seen; return *this; }
- RP_verifier& operator++(int) { +seen; return *this; }
+ RP_verifier& operator++(int) { ++seen; return *this; }
};
void verify_OP::verify_return_to() {
basic_OP::verify_return_to();
try {
RP_verifier rpv(return_to);
string drealm = realm;
string::size_type csss = drealm.find("://*.");
if(csss==4 || csss==5)
drealm.replace(csss+3,1,"www");
const char *rtt[] = { STURI_OPENID20_RT, 0 };
yadiscover(rpv,drealm,rtt,false);
if(rpv.seen)
throw bad_return_to(OPKELE_CP_ "return_to URL doesn't match any found while doing discovery on RP");
}catch(__RP_verifier_good_input&) {
}catch(bad_return_to& brt) {
throw;
}catch(exception_network&) { }
}
}
diff --git a/test/OP.cc b/test/OP.cc
index 851d831..6012b2e 100644
--- a/test/OP.cc
+++ b/test/OP.cc
@@ -1,415 +1,415 @@
#include <uuid/uuid.h>
#include <iostream>
#include <cassert>
#include <string>
#include <ext/algorithm>
using namespace std;
#include <kingate/exception.h>
#include <kingate/plaincgi.h>
#include <kingate/cgi_gateway.h>
#include <opkele/exception.h>
#include <opkele/util.h>
#include <opkele/uris.h>
#include <opkele/extension.h>
#include <opkele/association.h>
#include <opkele/debug.h>
#include <opkele/verify_op.h>
#include <opkele/sreg.h>
#include "sqlite.h"
#include "kingate_openid_message.h"
static const string get_self_url(const kingate::cgi_gateway& gw) {
bool s = gw.has_meta("SSL_PROTOCOL_VERSION");
string rv = s?"https://":"http://";
rv += gw.http_request_header("Host");
const string& port = gw.get_meta("SERVER_PORT");
if( port!=(s?"443":"80") ) {
rv += ':'; rv += port;
}
rv += gw.get_meta("REQUEST_URI");
string::size_type q = rv.find('?');
if(q!=string::npos)
rv.erase(q);
return rv;
}
class opdb_t : public sqlite3_t {
public:
opdb_t()
: sqlite3_t("/tmp/OP.db") {
assert(_D);
char **resp; int nr,nc; char *errm;
if(sqlite3_get_table(
_D, "SELECT a_op FROM assoc LIMIT 0",
&resp,&nr,&nc,&errm)!=SQLITE_OK) {
extern const char *__OP_db_bootstrap;
DOUT_("Bootstrapping DB");
if(sqlite3_exec(_D,__OP_db_bootstrap,NULL,NULL,&errm)!=SQLITE_OK)
throw opkele::exception(OPKELE_CP_ string("Failed to boostrap SQLite database: ")+errm);
}else
sqlite3_free_table(resp);
}
};
class example_op_t : public opkele::verify_OP {
public:
kingate::cgi_gateway& gw;
opdb_t db;
kingate::cookie htc;
- example_op_t(kingate::cgi_gateway& gw)
- : gw(gw) {
+ example_op_t(kingate::cgi_gateway& g)
+ : gw(g) {
try {
htc = gw.cookies.get_cookie("htop_session");
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"SELECT 1 FROM ht_sessions WHERE hts_id=%Q",
htc.get_value().c_str());
sqlite3_table_t T; int nr,nc;
db.get_table(S,T,&nr,&nc);
if(nr<1)
throw kingate::exception_notfound(CODEPOINT,"forcing cookie generation");
}catch(kingate::exception_notfound& kenf) {
uuid_t uuid; uuid_generate(uuid);
htc = kingate::cookie("htop_session",opkele::util::encode_base64(uuid,sizeof(uuid)));
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"INSERT INTO ht_sessions (hts_id) VALUES (%Q)",
htc.get_value().c_str());
db.exec(S);
}
}
void set_authorized(bool a) {
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"UPDATE ht_sessions"
" SET authorized=%d"
" WHERE hts_id=%Q",
(int)a,htc.get_value().c_str());
db.exec(S);
}
bool get_authorized() {
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"SELECT authorized"
" FROM ht_sessions"
" WHERE hts_id=%Q",
htc.get_value().c_str());
sqlite3_table_t T; int nr,nc;
db.get_table(S,T,&nr,&nc);
assert(nr==1); assert(nc=1);
return opkele::util::string_to_long(T.get(1,0,nc));
}
ostream& cookie_header(ostream& o) const {
o << "Set-Cookie: " << htc.set_cookie_header() << "\n";
return o;
}
opkele::assoc_t alloc_assoc(const string& type,size_t klength,bool sl) {
uuid_t uuid; uuid_generate(uuid);
string a_handle = opkele::util::encode_base64(uuid,sizeof(uuid));
opkele::secret_t a_secret;
generate_n(
back_insert_iterator<opkele::secret_t>(a_secret),klength,
rand );
string ssecret; a_secret.to_base64(ssecret);
time_t now = time(0);
int expires_in = sl?3600*2:3600*24*7*2;
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"INSERT INTO assoc"
" (a_handle,a_type,a_ctime,a_etime,a_secret,a_stateless)"
" VALUES ("
" %Q,%Q,datetime('now'),"
" datetime('now','+%d seconds'),"
" %Q,%d );",
a_handle.c_str(), type.c_str(),
expires_in,
ssecret.c_str(), sl );
db.exec(S);
return opkele::assoc_t(new opkele::association(
"",
a_handle, type, a_secret,
now+expires_in, sl ));
}
opkele::assoc_t retrieve_assoc(const string& h) {
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"SELECT"
" a_handle,a_type,a_secret,a_stateless,"
" strftime('%%s',a_etime) AS a_etime,"
" a_itime"
" FROM assoc"
" WHERE a_handle=%Q AND a_itime IS NULL"
" AND datetime('now') < a_etime"
" LIMIT 1",
h.c_str() );
sqlite3_table_t T;
int nr,nc;
db.get_table(S,T,&nr,&nc);
if(nr<1)
throw opkele::failed_lookup(OPKELE_CP_
"couldn't retrieve valid unexpired assoc");
assert(nr==1); assert(nc==6);
opkele::secret_t secret; opkele::util::decode_base64(T.get(1,2,nc),secret);
return opkele::assoc_t(new opkele::association(
"", h, T.get(1,1,nc), secret,
strtol(T.get(1,4,nc),0,0),
strtol(T.get(1,3,nc),0,0) ));
}
string& alloc_nonce(string& nonce) {
uuid_t uuid; uuid_generate(uuid);
nonce += opkele::util::encode_base64(uuid,sizeof(uuid));
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"INSERT INTO nonces"
" (n_once) VALUES (%Q)",
nonce.c_str() );
db.exec(S);
return nonce;
}
bool check_nonce(const string& nonce) {
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"SELECT 1"
" FROM nonces"
" WHERE n_once=%Q AND n_itime IS NULL",
nonce.c_str());
sqlite3_table_t T;
int nr,nc;
db.get_table(S,T,&nr,&nc);
return nr>=1;
}
void invalidate_nonce(const string& nonce) {
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"UPDATE nonces"
" SET n_itime=datetime('now')"
" WHERE n_once=%Q",
nonce.c_str());
db.exec(S);
}
const string get_op_endpoint() const {
return get_self_url(gw);
}
};
-int main(int argc,char *argv[]) {
+int main(int,char **) {
try {
kingate::plaincgi_interface ci;
kingate::cgi_gateway gw(ci);
string op;
try { op = gw.get_param("op"); }catch(kingate::exception_notfound&) { }
string message;
if(op=="set_password") {
example_op_t OP(gw);
string password = gw.get_param("password");
sqlite3_mem_t<char*>
Sget = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1");
sqlite3_table_t T; int nr,nc;
OP.db.get_table(Sget,T,&nr,&nc);
if(nr>=1)
throw opkele::exception(OPKELE_CP_ "Password already set");
sqlite3_mem_t<char*>
Sset = sqlite3_mprintf(
"INSERT INTO setup (s_password) VALUES (%Q)",
password.c_str());
OP.db.exec(Sset);
op.clear();
message = "password set";
}else if(op=="login") {
example_op_t OP(gw);
string password = gw.get_param("password");
sqlite3_mem_t<char*>
Sget = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1");
sqlite3_table_t T; int nr,nc;
OP.db.get_table(Sget,T,&nr,&nc);
if(nr<1)
throw opkele::exception(OPKELE_CP_ "no password set");
if(password!=T.get(1,0,nc))
throw opkele::exception(OPKELE_CP_ "wrong password");
OP.set_authorized(true);
op.clear();
message = "logged in";
OP.cookie_header(cout);
}else if(op=="logout") {
example_op_t OP(gw);
OP.set_authorized(false);
op.clear();
message = "logged out";
}
- string om;
- try { om = gw.get_param("openid.mode"); }catch(kingate::exception_notfound&) { }
+ string omode;
+ try { omode = gw.get_param("openid.mode"); }catch(kingate::exception_notfound&) { }
if(op=="xrds") {
cout <<
"Content-type: application/xrds+xml\n\n"
"<?xml version='1.0' encoding='utf-8'?>"
"<xrds:XRDS xmlns:xrds='xri://$xrds' xmlns='xri://$xrd*($v*2.0)'>"
"<XRD>"
"<Service>"
"<Type>" STURI_OPENID20 "</Type>"
"<URI>" << get_self_url(gw) << "</URI>"
"</Service>";
if(gw.has_param("idsel")){
cout <<
"<Service>"
"<Type>" STURI_OPENID20_OP "</Type>"
"<URI>" << get_self_url(gw) << "</URI>";
}
cout <<
"</XRD>"
"</xrds:XRDS>";
}else if(op=="id_res" || op=="cancel") {
kingate_openid_message_t inm(gw);
example_op_t OP(gw);
if(gw.get_param("hts_id")!=OP.htc.get_value())
throw opkele::exception(OPKELE_CP_ "toying around, huh?");
opkele::sreg_t sreg;
OP.checkid_(inm,sreg);
OP.cookie_header(cout);
opkele::openid_message_t om;
if(op=="id_res") {
if(!OP.get_authorized())
throw opkele::exception(OPKELE_CP_ "not logged in");
if(OP.is_id_select()) {
OP.select_identity( get_self_url(gw), get_self_url(gw) );
}
sreg.set_field(opkele::sreg_t::field_nickname,"anonymous");
sreg.set_field(opkele::sreg_t::field_fullname,"Ann O'Nymus");
sreg.set_field(opkele::sreg_t::field_gender,"F");
sreg.setup_response();
cout <<
"Status: 302 Going back to RP with id_res\n"
"Location: " << OP.id_res(om,sreg).append_query(OP.get_return_to())
<< "\n\n";
}else{
cout <<
"Status: 302 Going back to RP with cancel\n"
"Location: " << OP.cancel(om).append_query(OP.get_return_to())
<< "\n\n";
}
om.to_keyvalues(clog);
- }else if(om=="associate") {
+ }else if(omode=="associate") {
kingate_openid_message_t inm(gw);
opkele::openid_message_t oum;
example_op_t OP(gw);
OP.associate(oum,inm);
cout << "Content-type: text/plain\n\n";
oum.to_keyvalues(cout);
- }else if(om=="checkid_setup") {
+ }else if(omode=="checkid_setup") {
kingate_openid_message_t inm(gw);
example_op_t OP(gw);
OP.checkid_(inm,0);
OP.cookie_header(cout) <<
"Content-type: text/html\n"
"\n"
"<html>"
"<head>"
"<title>test OP: confirm authentication</title>"
"</head>"
"<body>"
"realm: " << OP.get_realm() << "<br/>"
"return_to: " << OP.get_return_to() << "<br/>"
"claimed_id: " << OP.get_claimed_id() << "<br/>"
"identity: " << OP.get_identity() << "<br/>";
if(OP.is_id_select()) {
OP.select_identity( get_self_url(gw), get_self_url(gw) );
cout <<
"selected claimed_id: " << OP.get_claimed_id() << "<br/>"
"selected identity: " << OP.get_identity() << "<br/>";
}
cout <<
"<form method='post'>";
inm.to_htmlhiddens(cout);
cout <<
"<input type='hidden' name='hts_id'"
" value='" << opkele::util::attr_escape(OP.htc.get_value()) << "'/>"
"<input type='submit' name='op' value='id_res'/>"
"<input type='submit' name='op' value='cancel'/>"
"</form>"
"</body>"
"</html>";
- }else if(om=="check_authentication") {
+ }else if(omode=="check_authentication") {
kingate_openid_message_t inm(gw);
example_op_t OP(gw);
opkele::openid_message_t oum;
OP.check_authentication(oum,inm);
cout << "Content-type: text/plain\n\n";
oum.to_keyvalues(cout);
oum.to_keyvalues(clog);
}else{
example_op_t OP(gw);
string idsel;
if(gw.has_param("idsel"))
idsel = "&idsel=idsel";
OP.cookie_header(cout) <<
"Content-type: text/html\n"
"X-XRDS-Location: " << get_self_url(gw) << "?op=xrds" << idsel << "\n"
"\n"
"<html>"
"<head>"
"<title>test OP</title>"
"<link rel='openid.server' href='" << get_self_url(gw) << "'/>"
"</head>"
"<body>"
"test openid 2.0 endpoint"
"<br/>"
"<a href='" << get_self_url(gw) << "?op=xrds" << idsel << "'>XRDS document</a>"
"<br/>"
"<h1>" << message << "</h1>";
sqlite3_mem_t<char*>
S = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1");
sqlite3_table_t T; int nr,nc;
OP.db.get_table(S,T,&nr,&nc);
if(nr<1) {
cout <<
"<form method='post'>"
"set password "
"<input type='hidden' name='op' value='set_password'/>"
"<input type='password' name='password' value=''/>"
"<input type='submit' name='submit' value='submit'/>"
"</form>";
}else if(OP.get_authorized()) {
cout <<
"<br/>"
"<a href='" << get_self_url(gw) << "?op=logout'>logout</a>";
}else{
cout <<
"<form method='post'>"
"login "
"<input type='hidden' name='op' value='login'/>"
"<input type='password' name='password' value=''/>"
"<input type='submit' name='submit' value='submit'/>"
"</form>";
}
cout << "</body>";
}
#ifdef OPKELE_HAVE_KONFORKA
}catch(konforka::exception& e) {
#else
}catch(std::exception& e){
#endif
DOUT_("Oops: " << e.what());
cout << "Content-Type: text/plain\n\n"
"Exception:\n"
" what: " << e.what() << endl;
#ifdef OPKELE_HAVE_KONFORKA
cout << " where: " << e.where() << endl;
if(!e._seen.empty()) {
cout << " seen:" << endl;
for(list<konforka::code_point>::const_iterator
i=e._seen.begin();i!=e._seen.end();++i) {
cout << " " << i->c_str() << endl;
}
}
#endif
}
}
diff --git a/test/RP.cc b/test/RP.cc
index e9744a4..99a792c 100644
--- a/test/RP.cc
+++ b/test/RP.cc
@@ -1,430 +1,430 @@
#include <uuid/uuid.h>
#include <iostream>
#include <cassert>
#include <stdexcept>
#include <string>
#include <set>
#include <iterator>
using namespace std;
#include <kingate/exception.h>
#include <kingate/plaincgi.h>
#include <kingate/cgi_gateway.h>
#include <opkele/exception.h>
#include <opkele/types.h>
#include <opkele/util.h>
#include <opkele/uris.h>
#include <opkele/discovery.h>
#include <opkele/association.h>
#include <opkele/sreg.h>
using namespace opkele;
#include <opkele/prequeue_rp.h>
#include <opkele/debug.h>
#include "sqlite.h"
#include "kingate_openid_message.h"
#undef DUMB_RP
#ifdef DUMB_RP
# define DUMBTHROW throw opkele::dumb_RP(OPKELE_CP_ "This RP is dumb")
#else
# define DUMBTHROW (void)0
#endif
class rpdb_t : public sqlite3_t {
public:
rpdb_t()
: sqlite3_t("/tmp/RP.db") {
assert(_D);
char **resp; int nrow,ncol; char *errm;
if(sqlite3_get_table(
_D,"SELECT a_op FROM assoc LIMIT 0",
&resp,&nrow,&ncol,&errm)!=SQLITE_OK) {
extern const char *__RP_db_bootstrap;
DOUT_("Bootstrapping DB");
if(sqlite3_exec(_D,__RP_db_bootstrap,NULL,NULL,&errm)!=SQLITE_OK)
throw opkele::exception(OPKELE_CP_ string("Failed to bootstrap SQLite database: ")+errm);
}else
sqlite3_free_table(resp);
}
};
class example_rp_t : public opkele::prequeue_RP {
public:
mutable rpdb_t db;
kingate::cookie htc;
long as_id;
int ordinal;
kingate::cgi_gateway& gw;
- example_rp_t(kingate::cgi_gateway& gw)
- : ordinal(0), have_eqtop(false), gw(gw), as_id(-1) {
+ example_rp_t(kingate::cgi_gateway& g)
+ : as_id(-1), ordinal(0), gw(g), have_eqtop(false) {
try {
htc = gw.cookies.get_cookie("ht_session");
as_id = opkele::util::string_to_long(gw.get_param("asid"));
}catch(kingate::exception_notfound& kenf) {
uuid_t uuid; uuid_generate(uuid);
htc = kingate::cookie("ht_session",util::encode_base64(uuid,sizeof(uuid)));
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"INSERT INTO ht_sessions (hts_id) VALUES (%Q)",
htc.get_value().c_str());
db.exec(S);
}
}
/* Global persistent store */
opkele::assoc_t store_assoc(
const string& OP,const string& handle,
const string& type,const secret_t& secret,
int expires_in) {
DUMBTHROW;
DOUT_("Storing '" << handle << "' assoc with '" << OP << "'");
time_t exp = time(0)+expires_in;
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"INSERT INTO assoc"
" (a_op,a_handle,a_type,a_ctime,a_etime,a_secret)"
" VALUES ("
" %Q,%Q,%Q,"
" datetime('now'), datetime('now','+%d seconds'),"
" %Q"
" );", OP.c_str(), handle.c_str(), type.c_str(),
expires_in,
util::encode_base64(&(secret.front()),secret.size()).c_str() );
db.exec(S);
return opkele::assoc_t(new opkele::association(
OP, handle, type, secret, exp, false ));
}
opkele::assoc_t find_assoc(
const string& OP) {
DUMBTHROW;
DOUT_("Looking for an assoc with '" << OP << '\'');
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"SELECT"
" a_op,a_handle,a_type,a_secret,"
" strftime('%%s',a_etime) AS a_etime"
" FROM assoc"
" WHERE a_op=%Q AND a_itime IS NULL AND NOT a_stateless"
" AND ( a_etime > datetime('now','-30 seconds') )"
" LIMIT 1",
OP.c_str());
sqlite3_table_t T;
int nr,nc;
db.get_table(S,T,&nr,&nc);
if(nr<1)
throw opkele::failed_lookup(OPKELE_CP_ "Couldn't find unexpired handle");
assert(nr==1);
assert(nc==5);
secret_t secret;
util::decode_base64(T.get(1,3,nc),secret);
DOUT_(" found '" << T.get(1,1,nc) << '\'');
return opkele::assoc_t(new opkele::association(
T.get(1,0,nc), T.get(1,1,nc), T.get(1,2,nc),
secret, strtol(T.get(1,4,nc),0,0), false ));
}
opkele::assoc_t retrieve_assoc(
const string& OP,const string& handle) {
DUMBTHROW;
DOUT_("Retrieving assoc '" << handle << "' with '" << OP << '\'');
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"SELECT"
" a_op,a_handle,a_type,a_secret,"
" strftime('%%s',a_etime) AS a_etime"
" FROM assoc"
" WHERE a_op=%Q AND a_handle=%Q"
" AND a_itime IS NULL AND NOT a_stateless"
" LIMIT 1",
OP.c_str(),handle.c_str());
sqlite3_table_t T;
int nr,nc;
db.get_table(S,T,&nr,&nc);
if(nr<1)
throw opkele::failed_lookup(OPKELE_CP_ "couldn't retrieve valid association");
assert(nr==1); assert(nc==5);
secret_t secret; util::decode_base64(T.get(1,3,nc),secret);
DOUT_(" found. type=" << T.get(1,2,nc) << '\'');
return opkele::assoc_t(new opkele::association(
T.get(1,0,nc), T.get(1,1,nc), T.get(1,2,nc),
secret, strtol(T.get(1,4,nc),0,0), false ));
}
void invalidate_assoc(
const string& OP,const string& handle) {
DUMBTHROW;
DOUT_("Invalidating assoc '" << handle << "' with '" << OP << '\'');
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"UPDATE assoc SET a_itime=datetime('now')"
" WHERE a_op=%Q AND a_handle=%Q",
OP.c_str(), handle.c_str() );
db.exec(S);
}
void check_nonce(const string& OP,const string& nonce) {
DOUT_("Checking nonce '" << nonce << "' from '" << OP << '\'');
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"SELECT 1 FROM nonces WHERE n_op=%Q AND n_once=%Q",
OP.c_str(), nonce.c_str());
sqlite3_table_t T;
int nr,nc;
db.get_table(S,T,&nr,&nc);
if(nr)
throw opkele::id_res_bad_nonce(OPKELE_CP_ "already seen that nonce");
sqlite3_mem_t<char*>
SS = sqlite3_mprintf(
"INSERT INTO nonces (n_op,n_once) VALUES (%Q,%Q)",
OP.c_str(), nonce.c_str());
db.exec(SS);
}
/* Session perisistent store */
void begin_queueing() {
assert(as_id>=0);
DOUT_("Resetting queue for session '" << htc.get_value() << "'/" << as_id);
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"DELETE FROM endpoints_queue"
" WHERE as_id=%ld",
as_id);
db.exec(S);
}
void queue_endpoint(const opkele::openid_endpoint_t& ep) {
assert(as_id>=0);
DOUT_("Queueing endpoint " << ep.claimed_id << " : " << ep.local_id << " @ " << ep.uri);
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"INSERT INTO endpoints_queue"
" (as_id,eq_ctime,eq_ordinal,eq_uri,eq_claimed_id,eq_local_id)"
" VALUES (%ld,strftime('%%s','now'),%d,%Q,%Q,%Q)",
as_id,ordinal++,
ep.uri.c_str(),ep.claimed_id.c_str(),ep.local_id.c_str());
db.exec(S);
}
mutable openid_endpoint_t eqtop;
mutable bool have_eqtop;
const openid_endpoint_t& get_endpoint() const {
assert(as_id>=0);
if(!have_eqtop) {
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"SELECT"
" eq_uri, eq_claimed_id, eq_local_id"
" FROM endpoints_queue"
" JOIN auth_sessions USING(as_id)"
" WHERE hts_id=%Q AND as_id=%ld"
" ORDER BY eq_ctime,eq_ordinal"
" LIMIT 1",htc.get_value().c_str(),as_id);
sqlite3_table_t T; int nr,nc;
db.get_table(S,T,&nr,&nc);
if(nr<1)
throw opkele::exception(OPKELE_CP_ "No more endpoints queued");
assert(nr==1); assert(nc==3);
eqtop.uri = T.get(1,0,nc);
eqtop.claimed_id = T.get(1,1,nc);
eqtop.local_id = T.get(1,2,nc);
have_eqtop = true;
}
return eqtop;
}
void next_endpoint() {
assert(as_id>=0);
get_endpoint();
have_eqtop = false;
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"DELETE FROM endpoints_queue"
" WHERE as_id=%ld AND eq_uri=%Q AND eq_local_id=%Q",
htc.get_value().c_str(),as_id,
eqtop.uri.c_str());
db.exec(S);
}
mutable string _cid;
mutable string _nid;
void set_claimed_id(const string& cid) {
assert(as_id>=0);
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"UPDATE auth_sessions"
" SET as_claimed_id=%Q"
" WHERE hts_id=%Q and as_id=%ld",
cid.c_str(),
htc.get_value().c_str(),as_id);
db.exec(S);
_cid = cid;
}
const string get_claimed_id() const {
assert(as_id>=0);
if(_cid.empty()) {
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"SELECT as_claimed_id"
" FROM"
" auth_sessions"
" WHERE"
" hts_id=%Q AND as_id=%ld",
htc.get_value().c_str(),as_id);
sqlite3_table_t T; int nr,nc;
db.get_table(S,T,&nr,&nc);
assert(nr==1); assert(nc==1);
_cid = T.get(1,0,nc);
}
return _cid;
}
void set_normalized_id(const string& nid) {
assert(as_id>=0);
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"UPDATE auth_sessions"
" SET as_normalized_id=%Q"
" WHERE hts_id=%Q and as_id=%ld",
nid.c_str(),
htc.get_value().c_str(),as_id);
db.exec(S);
_nid = nid;
}
const string get_normalized_id() const {
assert(as_id>=0);
if(_nid.empty()) {
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"SELECT as_normalized_id"
" FROM"
" auth_sessions"
" WHERE"
" hts_id=%Q AND as_id=%ld",
htc.get_value().c_str(),as_id);
sqlite3_table_t T; int nr,nc;
db.get_table(S,T,&nr,&nc);
assert(nr==1); assert(nc==1);
_nid = T.get(1,0,nc);
}
return _nid;
}
const string get_this_url() const {
bool s = gw.has_meta("SSL_PROTOCOL_VERSION");
string rv = s?"https://":"http://";
rv += gw.http_request_header("Host");
const string& port = gw.get_meta("SERVER_PORT");
if( port!=(s?"443":"80") ) {
rv += ':'; rv += port;
}
rv += gw.get_meta("REQUEST_URI");
return rv;
}
void initiate(const string& usi) {
allocate_asid();
prequeue_RP::initiate(usi);
}
string get_self_url() const {
string rv = get_this_url();
string::size_type q = rv.find('?');
if(q!=string::npos)
rv.erase(q);
return rv;
}
void allocate_asid() {
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"INSERT INTO auth_sessions (hts_id)"
" VALUES (%Q)",
htc.get_value().c_str());
db.exec(S);
as_id = sqlite3_last_insert_rowid(db);
DOUT_("Allocated authentication session id "<<as_id);
assert(as_id>=0);
}
#ifdef DUMB_RP
virtual assoc_t associate(const string& OP) {
DUMBTHROW;
}
#endif
};
-int main(int argc,char *argv[]) {
+int main(int,char **) {
try {
kingate::plaincgi_interface ci;
kingate::cgi_gateway gw(ci);
string op;
try { op = gw.get_param("op"); }catch(kingate::exception_notfound&) { }
if(op=="initiate") {
example_rp_t rp(gw);
string usi = gw.get_param("openid_identity");
rp.initiate(usi);
opkele::sreg_t sreg(opkele::sreg_t::fields_NONE,opkele::sreg_t::fields_ALL);
opkele::openid_message_t cm;
string loc;
cout <<
"Set-Cookie: " << rp.htc.set_cookie_header() << "\n"
"Status: 302 Going to OP\n"
"Location: " << (
loc = rp.checkid_(cm,opkele::mode_checkid_setup,
rp.get_self_url()+
"?op=confirm&asid="+opkele::util::long_to_string(rp.as_id),
rp.get_self_url(),&sreg).append_query(rp.get_endpoint().uri)
)
<< "\n\n";
DOUT_("Going to " << loc);
}else if(op=="confirm") {
kingate_openid_message_t om(gw);
example_rp_t rp(gw);
opkele::sreg_t sreg(opkele::sreg_t::fields_NONE,opkele::sreg_t::fields_ALL);
rp.id_res(om,&sreg);
cout <<
"Content-Type: text/plain\n\n";
for(opkele::basic_openid_message::fields_iterator i=om.fields_begin();
i!=om.fields_end();++i) {
cout << *i << '=' << om.get_field(*i) << endl;
}
cout << endl
<< "SREG fields: " << sreg.has_fields << endl;
}else{
cout <<
"Content-type: text/html\n\n"
"<html>"
"<head><title>test RP</title></head>"
"<body>"
"<form action='' method='post'>"
"<input type='hidden' name='op' value='initiate' />"
"<input type='text' name='openid_identity'/>"
"<input type='submit' name='submit' value='submit' />"
"</form>"
"<br/><br/>"
"<a href='?op=initiate&amp;openid_identity=www.myopenid.com&amp;dummy=" << time(0) << "'>login with myopenid.com account</a>"
"<br/>"
"</body"
"</html>"
;
}
#ifdef OPKELE_HAVE_KONFORKA
}catch(konforka::exception& e) {
#else
}catch(std::exception& e){
#endif
DOUT_("Oops: " << e.what());
cout << "Content-Type: text/plain\n\n"
"Exception:\n"
" what: " << e.what() << endl;
#ifdef OPKELE_HAVE_KONFORKA
cout << " where: " << e.where() << endl;
if(!e._seen.empty()) {
cout << " seen:" << endl;
for(list<konforka::code_point>::const_iterator
i=e._seen.begin();i!=e._seen.end();++i) {
cout << " " << i->c_str() << endl;
}
}
#endif
}
}
diff --git a/test/kingate_openid_message.h b/test/kingate_openid_message.h
index 37dcdfa..7029ff7 100644
--- a/test/kingate_openid_message.h
+++ b/test/kingate_openid_message.h
@@ -1,109 +1,109 @@
template<typename IT>
class join_iterator : public iterator<
input_iterator_tag,typename IT::value_type,
void,typename IT::pointer,typename IT::reference> {
public:
typedef pair<IT,IT> range_t;
typedef list<range_t> ranges_t;
ranges_t ranges;
join_iterator() { }
bool cleanup() {
bool rv = false;
while(!(ranges.empty() || ranges.front().first!=ranges.front().second)) {
ranges.pop_front(); rv = true;
}
return rv;
}
join_iterator<IT>& add_range(const IT& b,const IT& e) {
ranges.push_back(typename ranges_t::value_type(b,e));
cleanup();
return *this;
}
bool operator==(const join_iterator<IT>& x) const {
return ranges==x.ranges; }
bool operator!=(const join_iterator<IT>& x) const {
return ranges!=x.ranges; }
typename IT::reference operator*() const {
assert(!ranges.empty());
assert(ranges.front().first!=ranges.front().second);
return *ranges.front().first; }
typename IT::pointer operator->() const {
assert(!ranges.empty());
assert(ranges.front().first!=ranges.front().second);
return ranges.front().first.operator->(); }
join_iterator<IT>& operator++() {
cleanup();
if(ranges.empty()) return *this;
do {
++ranges.front().first;
}while(cleanup() && !ranges.empty());
return *this;
}
join_iterator<IT> operator++(int) {
join_iterator<IT> rv(*this);
++(*this); return rv; }
};
template<typename IT>
class cut_prefix_filterator : public opkele::util::basic_filterator<IT> {
public:
string pfx;
mutable string tmp;
cut_prefix_filterator() { }
- cut_prefix_filterator(const IT& bi,const IT&ei,const string& pfx)
- : opkele::util::basic_filterator<IT>(bi,ei), pfx(pfx) {
+ cut_prefix_filterator(const IT& _bi,const IT&_ei,const string& p)
+ : opkele::util::basic_filterator<IT>(_bi,_ei), pfx(p) {
this->prepare();
}
bool is_interesting() const {
return pfx.length()==0 || !strncmp(this->it->c_str(),pfx.c_str(),pfx.length());
}
typename IT::reference operator*() const {
assert(!this->empty);
tmp = *this->it; tmp.erase(0,pfx.length());
return tmp; }
typename IT::pointer operator->() const {
assert(!this->empty);
return &this->operator*(); }
};
class kingate_openid_message_t : public opkele::basic_openid_message {
typedef join_iterator<kingate::cgi_gateway::params_t::const_iterator> jitterator;
typedef opkele::util::map_keys_iterator<
jitterator,
fields_iterator::value_type,
fields_iterator::reference,
fields_iterator::pointer> keys_iterator;
typedef cut_prefix_filterator<keys_iterator> pfilterator;
public:
const kingate::cgi_gateway& gw;
kingate_openid_message_t(const kingate::cgi_gateway& g) : gw(g) { }
bool has_field(const string& n) const {
return gw.has_param("openid."+n); }
const string& get_field(const string& n) const try {
return gw.get_param("openid."+n); }catch(kingate::exception_notfound& nf) {
throw opkele::failed_lookup(OPKELE_CP_ nf.what()); }
fields_iterator fields_begin() const {
return
pfilterator( keys_iterator(
jitterator()
.add_range( gw.get.begin(), gw.get.end() )
.add_range( gw.post.begin(), gw.post.end() ),
jitterator()
), keys_iterator(), "openid." );
}
fields_iterator fields_end() const {
return pfilterator();
}
};