-rw-r--r-- | configure.ac | 22 | ||||
-rw-r--r-- | include/Makefile.am | 8 | ||||
-rw-r--r-- | include/opkele/discovery.h | 33 | ||||
-rw-r--r-- | include/opkele/exception.h | 19 | ||||
-rw-r--r-- | include/opkele/expat.h | 91 | ||||
-rw-r--r-- | include/opkele/types.h | 63 | ||||
-rw-r--r-- | include/opkele/uris.h | 15 | ||||
-rw-r--r-- | lib/Makefile.am | 8 | ||||
-rw-r--r-- | lib/discovery.cc | 375 | ||||
-rw-r--r-- | lib/expat.cc | 96 | ||||
-rw-r--r-- | lib/util.cc | 30 | ||||
-rw-r--r-- | libopkele.pc.in | 4 | ||||
-rw-r--r-- | test/.gitignore | 1 | ||||
-rw-r--r-- | test/Makefile.am | 5 | ||||
-rw-r--r-- | test/idiscover.cc | 49 |
15 files changed, 800 insertions, 19 deletions
diff --git a/configure.ac b/configure.ac index 48a5efb..1bd1873 100644 --- a/configure.ac +++ b/configure.ac @@ -1,128 +1,150 @@ AC_INIT([libopkele], [0.4], [libopkele-bugs@klever.net]) AC_CONFIG_SRCDIR([include/opkele/opkele-config.h]) AC_CONFIG_HEADERS([config.h include/opkele/acconfig.h]) AM_INIT_AUTOMAKE([dist-bzip2]) AC_PROG_INSTALL AC_PROG_CXX AC_PROG_CC AC_PROG_LIBTOOL PKG_PROG_PKG_CONFIG AC_HEADER_STDC AC_PATH_PROG([XSLTPROC],[xsltproc],[true]) AC_MSG_CHECKING([for source tree version]) if headrev=$(cd $srcdir && git rev-parse --verify HEAD 2>/dev/null) ; then PACKAGE_SRC_VERSION="$(cd $srcdir && git describe --tags $headrev)" test "$PACKAGE_SRC_VERSION" = "$PACKAGE_VERSION" \ -o "${PACKAGE_SRC_VERSION#${PACKAGE_VERSION}-}" != "$PACKAGE_SRC_VERSION" || PACKAGE_SRC_VERSION="${PACKAGE_VERSION}:${PACKAGE_SRC_VERSION}" ( cd $srcdir && git diff-index $headrev | read dirt ) && PACKAGE_SRC_VERSION="${PACKAGE_SRC_VERSION}-dirty" else PACKAGE_SRC_VERSION="$PACKAGE_VERSION" fi AC_MSG_RESULT([$PACKAGE_SRC_VERSION]) AC_SUBST([PACKAGE_SRC_VERSION]) AC_DEFINE_UNQUOTED([PACKAGE_SRC_VERSION],["$PACKAGE_SRC_VERSION"],[more or less precise source tree version]) PKG_CHECK_MODULES([OPENSSL],[openssl],,[ AC_MSG_ERROR([no openssl library found. get one from http://www.openssl.org/]) ]) WANT_KONFORKA="yes" AC_ARG_ENABLE([konforka], AC_HELP_STRING([--disable-konforka],[do not use konforka library (default: use if found)]), [ test "${enableval}" = "no" && WANT_KONFORKA="no" ] ) if test "${WANT_KONFORKA}" = "yes" ; then PKG_CHECK_MODULES([KONFORKA],[konforka],[ AC_SUBST([KONFORKA_CFLAGS]) AC_SUBST([KONFORKA_LIBS]) AC_DEFINE([HAVE_KONFORKA],,[defined in presence of konforka library]) AC_DEFINE([OPKELE_HAVE_KONFORKA],,[defined in presence of konforka library]) AC_SUBST([KONFORKA_KONFORKA],[konforka]) ],[true]) fi WANT_DOXYGEN="yes" AC_ARG_ENABLE([doxygen], AC_HELP_STRING([--disable-doxygen],[do not generate documentation]), [ test "${enableval}" = "no" && WANT_DOXYGEN="no" ] ) if test "${WANT_DOXYGEN}" = "yes" ; then AC_WITH_DOXYGEN AC_WITH_DOT else AM_CONDITIONAL([HAVE_DOXYGEN],[false]) AM_CONDITIONAL([HAVE_DOT],[false]) fi LIBCURL_CHECK_CONFIG(,,,[ AC_MSG_ERROR([no required libcurl library. get one from http://curl.haxx.se/]) ]) +AC_CHECK_HEADER([expat.h],[ + AC_CHECK_LIB([expat],[XML_ParserCreate],[ + EXPAT_LIBS=-lexpat + EXPAT_CFLAGS= + AC_SUBST([EXPAT_LIBS]) + AC_SUBST([EXPAT_CFLAGS]) + ],[ + AC_MSG_ERROR([no required expat library. get one from http://expat.sourceforge.net/]) + ]) +],[ + AC_MSG_ERROR([no required expat library. get one from http://expat.sourceforge.net/]) +]) + if test -n "$PCRE_LIBS" -a -n "$PCRE_CFLAGS" ; then AC_SUBST([PCRE_CFLAGS]) AC_SUBST([PCRE_LIBS]) : else PKG_CHECK_MODULES([PCRE],[libpcre],,[ AC_MSG_ERROR([no libpcre found, go get it at http://www.pcre.org/]) ]) fi curl_ssl_verify_host="true" AC_ARG_ENABLE([ssl-verify-host], AC_HELP_STRING([--disable-ssl-verify-host],[disable cURL cert/host relationships verification]), [ test "${enableval}" = "no" && curl_ssl_verify_host="false" ] ) ${curl_ssl_verify_host} || AC_DEFINE([DISABLE_CURL_SSL_VERIFYHOST],,[defined if cURL is not to verify cert/host]) curl_ssl_verify_peer="true" AC_ARG_ENABLE([ssl-verify-peer], AC_HELP_STRING([--disable-ssl-verify-peer],[disable cURL cert validity verification]), [ test "${enableval}" = "no" && curl_ssl_verify_peer="false" ] ) ${curl_ssl_verify_peer} || AC_DEFINE([DISABLE_CURL_SSL_VERIFYPEER],,[defined if cURL is not to verify cert validity]) postels_law=true AC_ARG_ENABLE([postels-law], AC_HELP_STRING([--disable-postels-law],[Be strict, do not adhere to Postel's Law ("be conservative in what you do, be liberal in what you accept from others", RFC 793)]), [ test "${enableval}" = "no" && postels_law=false ] ) $postels_law && AC_DEFINE([POSTELS_LAW],,[defined if we want to adhere to Postel's Law]) AC_DEFINE_UNQUOTED([OPKELE_SRC_DIR],["$PWD"],[source directory]) nitpick=false AC_ARG_ENABLE([nitpicking], AC_HELP_STRING([--enable-nitpicking],[make compiler somewhat overly fastidious about the code it deals with]), [ test "$enableval" = "no" || nitpick=true ] ) if $nitpick ; then CPP_NITPICK="-pedantic -Wall -Wextra -Wundef -Wshadow \ -Wunsafe-loop-optimizations -Wconversion -Wmissing-format-attribute \ -Wredundant-decls -ansi" # -Wlogical-op -Wmissing-noreturn C_NITPICK="$CPP_NITPICK" CXX_NITPICK="$C_NITPICK" CPPFLAGS="$CPPFLAGS $CPP_NITPICK" CFLAGS="$CFLAGS $C_NITPICK" CXXFLAGS="$CXXFLAGS $CXX_NITPICK" fi +xri_proxy_url="http://beta.xri.net/" +AC_MSG_CHECKING([for XRI resolver proxy]) +AC_ARG_ENABLE([xri-proxy], + AC_HELP_STRING([--with-xri-proxy=url],[set xri proxy for use when resolving xri identities, default is http://xr_proxy_url]), + [ xri_proxy_url="$withval" ] +) +AC_MSG_RESULT([$xri_proxy_url]) +AC_DEFINE_UNQUOTED([XRI_PROXY_URL],["$xri_proxy_url"],[XRI proxy resolver URL]) + AC_CONFIG_FILES([ Makefile libopkele.pc Doxyfile include/Makefile lib/Makefile test/Makefile ]) AC_OUTPUT diff --git a/include/Makefile.am b/include/Makefile.am index b31786d..0c2928d 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,17 +1,19 @@ nobase_include_HEADERS = \ opkele/acconfig.h \ opkele/opkele-config.h \ opkele/types.h \ opkele/association.h \ opkele/exception.h \ opkele/server.h \ opkele/consumer.h \ opkele/extension.h \ opkele/sreg.h \ opkele/extension_chain.h \ opkele/xconsumer.h \ - opkele/xserver.h + opkele/xserver.h \ + opkele/curl.h opkele/expat.h \ + opkele/discovery.h \ + opkele/uris.h EXTRA_DIST = \ opkele/data.h \ - opkele/util.h \ - opkele/curl.h + opkele/util.h diff --git a/include/opkele/discovery.h b/include/opkele/discovery.h new file mode 100644 index 0000000..5d7129b --- a/dev/null +++ b/include/opkele/discovery.h @@ -0,0 +1,33 @@ +#ifndef __OPKELE_DISCOVERY_H +#define __OPKELE_DISCOVERY_H + +#include <string> +#include <opkele/types.h> + +namespace opkele { + using std::string; + + struct idiscovery_t; + + void idiscover(idiscovery_t& result,const string& identity); + + struct idiscovery_t { + string normalized_id; + string canonicalized_id; + xrd::XRD_t xrd; + + idiscovery_t(const string& i) { + idiscover(*this,i); + } + idiscovery_t(const char *i) { + idiscover(*this,i); + } + + void clear() { + normalized_id.clear(); canonicalized_id.clear(); + xrd.clear(); + } + }; +} + +#endif /* __OPKELE_DISCOVERY_H */ diff --git a/include/opkele/exception.h b/include/opkele/exception.h index 2ff44b7..8913665 100644 --- a/include/opkele/exception.h +++ b/include/opkele/exception.h @@ -1,235 +1,254 @@ #ifndef __OPKELE_EXCEPTION_H #define __OPKELE_EXCEPTION_H /** * @file * @brief opkele exceptions */ #include <curl/curl.h> #include <opkele/opkele-config.h> #ifdef OPKELE_HAVE_KONFORKA # include <konforka/exception.h> /** * the exception parameters declaration */ # define OPKELE_E_PARS const string& fi,const string&fu,int l,const string& w /** * the exception parameters list to pass to constructor */ # define OPKELE_E_CONS_ fi,fu,l, /** * the exception codepoint specification */ # define OPKELE_CP_ CODEPOINT, /** * the simple rethrow of konforka-based exception */ # define OPKELE_RETHROW catch(konforka::exception& e) { e.see(CODEPOINT); throw } #else /* OPKELE_HAVE_KONFORKA */ # include <exception> # include <string> /** * the exception parameter declaration */ # define OPKELE_E_PARS const string& w /** * the dummy prefix for exception parameters list to prepend in the absence of * konforka library */ # define OPKELE_E_CONS_ /** * the dummy placeholder for konforka exception codepoint specification */ # define OPKELE_CP_ /** * the dummy define for the konforka-based rethrow of exception */ # define OPKELE_RETHROW #endif /* OPKELE_HAVE_KONFORKA */ /** * the exception parameters list to pass to constructor */ # define OPKELE_E_CONS OPKELE_E_CONS_ w namespace opkele { using std::string; /** * the base opkele exception class */ class exception : public # ifdef OPKELE_HAVE_KONFORKA konforka::exception # else std::exception # endif { public: # ifdef OPKELE_HAVE_KONFORKA explicit exception(const string& fi,const string& fu,int l,const string& w) : konforka::exception(fi,fu,l,w) { } # else /* OPKELE_HAVE_KONFORKA */ string _what; explicit exception(const string& w) : _what(w) { } virtual ~exception() throw(); virtual const char * what() const throw(); # endif /* OPKELE_HAVE_KONFORKA */ }; /** * thrown in case of failed conversion */ class failed_conversion : public exception { public: failed_conversion(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; /** * thrown in case of failed lookup (either parameter or persistent store) */ class failed_lookup : public exception { public: failed_lookup(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; /** * thrown in case of bad input (either local or network) */ class bad_input : public exception { public: bad_input(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; /** * thrown on failed assertion */ class failed_assertion : public exception { public: failed_assertion(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; /** * thrown if the handle being retrieved is invalid */ class invalid_handle : public exception { public: invalid_handle(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; /** * thrown if the handle passed to check_authentication request is not * stateless */ class stateful_handle : public exception { public: stateful_handle(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; /** * thrown if check_authentication request fails */ class failed_check_authentication : public exception { public: failed_check_authentication(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; /** * thrown if the id_res request result is negative */ class id_res_failed : public exception { public: id_res_failed(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; /** * thrown if the user_setup_url is provided with negative response */ class id_res_setup : public id_res_failed { public: string setup_url; id_res_setup(OPKELE_E_PARS,const string& su) : id_res_failed(OPKELE_E_CONS), setup_url(su) { } ~id_res_setup() throw() { } }; /** * thrown in case of signature mismatch */ class id_res_mismatch : public id_res_failed { public: id_res_mismatch(OPKELE_E_PARS) : id_res_failed(OPKELE_E_CONS) { } }; /** * thrown if the association has expired before it could've been verified. */ class id_res_expired_on_delivery : public id_res_failed { public: id_res_expired_on_delivery(OPKELE_E_PARS) : id_res_failed(OPKELE_E_CONS) { } }; /** * openssl malfunction occured */ class exception_openssl : public exception { public: unsigned long _error; string _ssl_string; exception_openssl(OPKELE_E_PARS); ~exception_openssl() throw() { } }; /** * network operation related error occured */ class exception_network : public exception { public: exception_network(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; /** * network operation related error occured, specifically, related to * libcurl */ class exception_curl : public exception_network { public: CURLcode _error; string _curl_string; exception_curl(OPKELE_E_PARS); exception_curl(OPKELE_E_PARS,CURLcode e); ~exception_curl() throw() { } }; /** + * exception thrown in case of failed discovery + */ + class failed_discovery : public exception { + public: + failed_discovery(OPKELE_E_PARS) + : exception(OPKELE_E_CONS) { } + }; + + /** + * unsuccessfull xri resolution + */ + class failed_xri_resolution : public failed_discovery { + public: + long _code; + failed_xri_resolution(OPKELE_E_PARS,long _c=-1) + : failed_discovery(OPKELE_E_CONS), _code(_c) { } + }; + + /** * not implemented (think pure virtual) member function executed, signfies * programmer error */ class not_implemented : public exception { public: not_implemented(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; /** * internal error, indicates internal libopkele problem */ class internal_error : public exception { public: internal_error(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; } #endif /* __OPKELE_EXCEPTION_H */ diff --git a/include/opkele/expat.h b/include/opkele/expat.h new file mode 100644 index 0000000..60c41ac --- a/dev/null +++ b/include/opkele/expat.h @@ -0,0 +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) { } + void set_element_handler(); + + 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) { } + void set_processing_instruction_handler(); + + 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) { } + 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) { } + 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/types.h b/include/opkele/types.h index f732a1e..520618d 100644 --- a/include/opkele/types.h +++ b/include/opkele/types.h @@ -1,172 +1,235 @@ #ifndef __OPKELE_TYPES_H #define __OPKELE_TYPES_H /** * @file * @brief various types declarations */ #include <ostream> #include <vector> #include <string> #include <map> #include <memory> +#include <set> namespace opkele { using std::vector; using std::string; using std::map; using std::ostream; using std::auto_ptr; + using std::multimap; + using std::set; /** * the OpenID operation mode */ typedef enum _mode_t { 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_sha1 pointer to the sha1 digest * @param rv reference to the return value */ void enxor_to_base64(const unsigned char *key_sha1,string& rv) const; /** * decode base64-encoded secret and xor it with the sha1 digest * @param key_sha1 pointer to the message digest * @param b64 base64-encoded secret value */ void enxor_from_base64(const unsigned char *key_sha1,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 auto_ptr<> for association_t object type */ typedef auto_ptr<association_t> assoc_t; /** * request/response parameters map */ class params_t : public map<string,string> { public: /** * check whether the parameter is present. * @param n the parameter name * @return true if yes */ bool has_param(const string& n) const; /** * 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; /** * retrieve the parameter. * @param n the parameter name * @return the parameter value * @throw failed_lookup if there is no such parameter */ string& get_param(const string& n); /** * parse the OpenID key/value data. * @param kv the OpenID key/value data */ void parse_keyvalues(const string& kv); /** * sign the fields. * @param secret the secret used for signing * @param sig reference to the string, containing base64-encoded * result * @param slist the comma-separated list of fields to sign * @param prefix the string to prepend to parameter names */ void sign(secret_t secret,string& sig,const string& slist,const char *prefix=0) const; /** * append parameters to the URL as a GET-request parameters. * @param url the base URL * @param prefix the string to prepend to parameter names * @return the ready-to-use location */ string append_query(const string& url,const char *prefix = "openid.") const; }; /** * dump the key/value pairs for the parameters to the stream. * @param o output stream * @param p the parameters */ ostream& operator << (ostream& o,const params_t& p); + namespace xrd { + + struct priority_compare { + inline bool operator()(long a,long b) const { + return (a<0) ? false : (b<0) ? false : (a<b); + } + }; + + template <typename _DT> + class priority_map : public multimap<long,_DT,priority_compare> { + typedef multimap<long,_DT,priority_compare> map_type; + public: + + inline _DT& add(long priority,const _DT& d) { + return insert(typename map_type::value_type(priority,d))->second; + } + }; + + typedef priority_map<string> canonical_ids_t; + typedef priority_map<string> local_ids_t; + typedef set<string> types_t; + typedef priority_map<string> uris_t; + + class service_t { + public: + types_t types; + uris_t uris; + local_ids_t local_ids; + + void clear() { + types.clear(); + uris.clear(); local_ids.clear(); + } + }; + typedef priority_map<service_t> services_t; + + class XRD_t { + public: + time_t expires; + + canonical_ids_t canonical_ids; + local_ids_t local_ids; + services_t services; + + void clear() { + expires = 0; + canonical_ids.clear(); local_ids.clear(); + services.clear(); + } + bool empty() const { + return + canonical_ids.empty() + && local_ids.empty() + && services.empty(); + } + + }; + + } + } #endif /* __OPKELE_TYPES_H */ diff --git a/include/opkele/uris.h b/include/opkele/uris.h new file mode 100644 index 0000000..a432b13 --- a/dev/null +++ b/include/opkele/uris.h @@ -0,0 +1,15 @@ +#ifndef __OPKELE_URIS_H +#define __OPKELE_URIS_H + +#define NSURI_XRDS "xri://$xrds" +#define NSURI_XRD "xri://$xrd*($v*2.0)" +#define NSURI_OPENID10 "http://openid.net/xmlns/1.0" + +#define STURI_OPENID10 "http://openid.net/signon/1.0" +#define STURI_OPENID11 "http://openid.net/signon/1.1" +#define STURI_OPENID20 "http://specs.openid.net/auth/2.0/signon" +#define STURI_OPENID20_OP "http://specs.openid.net/auth/2.0/server" + +#define IDURI_SELECT20 "http://specs.openid.net/auth/2.0/identifier_select" + +#endif /* __OPKELE_URIS_H */ diff --git a/lib/Makefile.am b/lib/Makefile.am index 0fe705a..185411f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,28 +1,30 @@ lib_LTLIBRARIES = libopkele.la +DEFAULT_INCLUDES = -I${top_builddir} INCLUDES = \ -I${top_srcdir}/include/ \ ${KONFORKA_CFLAGS} \ ${OPENSSL_CFLAGS} \ ${LIBCURL_CPPFLAGS} \ - ${PCRE_CFLAGS} + ${PCRE_CFLAGS} ${EXPAT_CFLAGS} libopkele_la_LIBADD = \ ${LIBCURL} \ - ${PCRE_LIBS} \ + ${PCRE_LIBS} ${EXPAT_LIBS} \ ${OPENSSL_LIBS} \ ${KONFORKA_LIBS} libopkele_la_SOURCES = \ params.cc \ util.cc \ server.cc \ secret.cc \ data.cc \ consumer.cc \ exception.cc \ extension.cc \ sreg.cc \ extension_chain.cc \ - curl.cc + curl.cc expat.cc \ + discovery.cc libopkele_la_LDFLAGS = \ -version-info 2:0:0 diff --git a/lib/discovery.cc b/lib/discovery.cc new file mode 100644 index 0000000..a35ce32 --- a/dev/null +++ b/lib/discovery.cc @@ -0,0 +1,375 @@ +#include <iostream> +using namespace std; +#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 "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; + + static const char *whitespace = " \t\r\n"; + static const char *i_leaders = "=@+$!("; + + 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; + } + + class idigger_t : public util::curl_t, public util::expat_t { + public: + string xri_proxy; + + enum { + xmode_html = 1, xmode_xrd = 2 + }; + 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; + + 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 discover(idiscovery_t& result,const string& identity) { + result.clear(); + string::size_type fsc = identity.find_first_not_of(whitespace); + if(fsc==string::npos) + throw bad_input(OPKELE_CP_ "whtiespace-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); + if(strchr(i_leaders,id[0])) { + result.normalized_id = id; + /* TODO: further canonicalize xri identity? Like folding case or whatever... */ + discover_at( + result, + xri_proxy + util::url_encode(id)+ + "?_xrd_r=application/xrd+xml;sep=false", xmode_xrd); + if(status_code!=100) + throw failed_xri_resolution(OPKELE_CP_ + "XRI resolution failed with '"+status_string+"' message",status_code); + if(result.xrd.canonical_ids.empty()) + throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID for XRI identity found"); + }else{ + 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); + } + result.normalized_id = util::rfc_3986_normalize_uri(id); + discover_at(result,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); + result.canonicalized_id = util::rfc_3986_normalize_uri(eu); /* XXX: strip fragment part? */ + if(xrds_location.empty()) { + html2xrd(result.xrd); + }else{ + discover_at(result,xrds_location,xmode_xrd); + if(result.xrd.empty()) + html2xrd(result.xrd); + } + } + } + + void discover_at(idiscovery_t& result,const string& url,int xm) { + CURLcode r = easy_setopt(CURLOPT_URL,url.c_str()); + if(r) + throw exception_curl(OPKELE_CP_ "failed to set culry urlie",r); + + (*(expat_t*)this) = parser_create_ns(); + set_user_data(); set_element_handler(); + set_character_data_handler(); + + xrds_location.clear(); http_content_type.clear(); + xmode = xm; + if(xmode&xmode_html) { + xrds_location.clear(); + html_openid1.clear(); html_openid2.clear(); + } + xrd = &result.xrd; + cdata = 0; xrd_service = 0; skipping = 0; + status_code = 100; status_string.clear(); + + r = easy_perform(); + if(r && r!=CURLE_WRITE_ERROR) + throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); + + parse(0,0,true); + } + + void html2xrd(XRD_t& x) { + if(!html_openid1.uris.empty()) { + html_openid1.types.insert(STURI_OPENID11); + x.services.add(-1,html_openid1); + } + if(!html_openid2.uris.empty()) { + html_openid2.types.insert(STURI_OPENID20); + x.services.add(-1,html_openid2); + } + } + + size_t write(void *p,size_t s,size_t nm) { + if(skipping<0) return 0; + /* TODO: limit total size */ + size_t bytes = s*nm; + parse((const char *)p,bytes,false); + 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; + 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) { + /* TODO: xrd:XRD/xrd:Expires */ + 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 "\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 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); + cdata = &(xrd_service->uris.add(element_priority(a),string())); + }else if(is_qelement(n,NSURI_XRD "\tLocalID") + || is_qelement(n,NSURI_OPENID10 "\tDelegate") ) { + assert(xrd); assert(xrd_service); + cdata = &(xrd_service->uris.add(element_priority(a),string())); + }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((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,href); + else if(rel=="openid.delegate") + html_openid1.local_ids.add(-1,href); + else if(rel=="openid2.provider") + html_openid2.uris.add(-1,href); + else if(rel=="openid2.local_id") + html_openid2.local_ids.add(-1,href); + } + }else if(is_element(n,"body")) { + skipping = -1; + } + } + + }; + + void idiscover(idiscovery_t& result,const string& identity) { + idigger_t idigger; + idigger.discover(result,identity); + } + +} diff --git a/lib/expat.cc b/lib/expat.cc new file mode 100644 index 0000000..fa6fdde --- a/dev/null +++ b/lib/expat.cc @@ -0,0 +1,96 @@ +#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; + } + + 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/util.cc b/lib/util.cc index 416e2cc..4600576 100644 --- a/lib/util.cc +++ b/lib/util.cc @@ -1,295 +1,305 @@ #include <errno.h> #include <cassert> #include <cctype> #include <cstring> #include <vector> #include <string> #include <stack> #include <openssl/bio.h> #include <openssl/evp.h> #include <curl/curl.h> #include "opkele/util.h" #include "opkele/exception.h" namespace opkele { using namespace std; namespace util { /* * base64 */ string encode_base64(const void *data,size_t length) { BIO *b64 = 0, *bmem = 0; try { b64 = BIO_new(BIO_f_base64()); if(!b64) throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 encoder"); BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); bmem = BIO_new(BIO_s_mem()); BIO_set_flags(b64,BIO_CLOSE); if(!bmem) throw exception_openssl(OPKELE_CP_ "failed to BIO_new() memory buffer"); BIO_push(b64,bmem); if(((size_t)BIO_write(b64,data,length))!=length) throw exception_openssl(OPKELE_CP_ "failed to BIO_write()"); if(BIO_flush(b64)!=1) throw exception_openssl(OPKELE_CP_ "failed to BIO_flush()"); char *rvd; long rvl = BIO_get_mem_data(bmem,&rvd); string rv(rvd,rvl); BIO_free_all(b64); return rv; }catch(...) { if(b64) BIO_free_all(b64); throw; } } void decode_base64(const string& data,vector<unsigned char>& rv) { BIO *b64 = 0, *bmem = 0; rv.clear(); try { bmem = BIO_new_mem_buf((void*)data.data(),data.size()); if(!bmem) throw exception_openssl(OPKELE_CP_ "failed to BIO_new_mem_buf()"); b64 = BIO_new(BIO_f_base64()); if(!b64) throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 decoder"); BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); BIO_push(b64,bmem); unsigned char tmp[512]; size_t rb = 0; while((rb=BIO_read(b64,tmp,sizeof(tmp)))>0) rv.insert(rv.end(),tmp,&tmp[rb]); BIO_free_all(b64); }catch(...) { if(b64) BIO_free_all(b64); throw; } } /* * big numerics */ BIGNUM *base64_to_bignum(const string& b64) { vector<unsigned char> bin; decode_base64(b64,bin); BIGNUM *rv = BN_bin2bn(&(bin.front()),bin.size(),0); if(!rv) throw failed_conversion(OPKELE_CP_ "failed to BN_bin2bn()"); return rv; } BIGNUM *dec_to_bignum(const string& dec) { BIGNUM *rv = 0; if(!BN_dec2bn(&rv,dec.c_str())) throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()"); return rv; } string bignum_to_base64(const BIGNUM *bn) { vector<unsigned char> bin(BN_num_bytes(bn)+1); unsigned char *binptr = &(bin.front())+1; int l = BN_bn2bin(bn,binptr); if(l && (*binptr)&0x80){ (*(--binptr)) = 0; ++l; } return encode_base64(binptr,l); } /* * w3c times */ string time_to_w3c(time_t t) { struct tm tm_t; if(!gmtime_r(&t,&tm_t)) throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()"); char rv[25]; if(!strftime(rv,sizeof(rv)-1,"%Y-%m-%dT%H:%M:%SZ",&tm_t)) throw failed_conversion(OPKELE_CP_ "failed to strftime()"); return rv; } time_t w3c_to_time(const string& w) { struct tm tm_t; memset(&tm_t,0,sizeof(tm_t)); if( sscanf( w.c_str(), "%04d-%02d-%02dT%02d:%02d:%02dZ", &tm_t.tm_year,&tm_t.tm_mon,&tm_t.tm_mday, &tm_t.tm_hour,&tm_t.tm_min,&tm_t.tm_sec ) != 6 ) throw failed_conversion(OPKELE_CP_ "failed to sscanf()"); tm_t.tm_mon--; tm_t.tm_year-=1900; time_t rv = mktime(&tm_t); if(rv==(time_t)-1) throw failed_conversion(OPKELE_CP_ "failed to mktime()"); return rv; } /* * */ string url_encode(const string& str) { char * t = curl_escape(str.c_str(),str.length()); if(!t) throw failed_conversion(OPKELE_CP_ "failed to curl_escape()"); string rv(t); curl_free(t); return rv; } string long_to_string(long l) { char rv[32]; int r=snprintf(rv,sizeof(rv),"%ld",l); if(r<0 || r>=(int)sizeof(rv)) throw failed_conversion(OPKELE_CP_ "failed to snprintf()"); return rv; } long string_to_long(const string& s) { char *endptr = 0; long rv = strtol(s.c_str(),&endptr,10); if((!endptr) || endptr==s.c_str()) throw failed_conversion(OPKELE_CP_ "failed to strtol()"); return rv; } /* * Normalize URL according to the rules, described in rfc 3986, section 6 * * - uppercase hext triplets (e.g. %ab -> %AB) * - lowercase scheme and host * - decode %-encoded characters, specified as unreserved in rfc 3986, section 2.3, * that is - [:alpha:][:digit:]._~- * - remove dot segments * - remove empty and default ports * - if there's no path component, add '/' */ string rfc_3986_normalize_uri(const string& uri) { + static const char *whitespace = " \t\r\n"; string rv; - string::size_type colon = uri.find(':'); + string::size_type ns = uri.find_first_not_of(whitespace); + if(ns==string::npos) + throw bad_input(OPKELE_CP_ "Can't normalize empty URI"); + string::size_type colon = uri.find(':',ns); if(colon==string::npos) throw bad_input(OPKELE_CP_ "No scheme specified in URI"); transform( - uri.begin(), uri.begin()+colon+1, + uri.begin()+ns, uri.begin()+colon+1, back_inserter(rv), ::tolower ); bool s; - if(rv=="http:") - s = false; - else if(rv=="https:") - s = true; - else - throw not_implemented(OPKELE_CP_ "Only http(s) URIs can be normalized here"); - string::size_type ul = uri.length(); + string::size_type ul = uri.find_last_not_of(whitespace)+1; if(ul <= (colon+3)) throw bad_input(OPKELE_CP_ "Unexpected end of URI being normalized encountered"); if(uri[colon+1]!='/' || uri[colon+2]!='/') throw bad_input(OPKELE_CP_ "Unexpected input in URI being normalized after scheme component"); + if(rv=="http:") + s = false; + else if(rv=="https:") + s = true; + else{ + /* TODO: support more schemes. + * e.g. xri. How do we normalize + * xri? + */ + rv.append(uri,colon+1,ul-colon-1); + return rv; + } rv += "//"; string::size_type interesting = uri.find_first_of(":/#?",colon+3); if(interesting==string::npos) { transform( - uri.begin()+colon+3,uri.end(), + uri.begin()+colon+3,uri.begin()+ul, back_inserter(rv), ::tolower ); rv += '/'; return rv; } transform( uri.begin()+colon+3,uri.begin()+interesting, back_inserter(rv), ::tolower ); bool qf = false; char ic = uri[interesting]; if(ic==':') { string::size_type ni = uri.find_first_of("/#?%",interesting+1); const char *nptr = uri.data()+interesting+1; char *eptr = 0; long port = strtol(nptr,&eptr,10); if( (port>0) && (port<65535) && port!=(s?443:80) ) { char tmp[8]; snprintf(tmp,sizeof(tmp),":%ld",port); rv += tmp; } if(ni==string::npos) { rv += '/'; return rv; } interesting = ni; }else if(ic!='/') { rv += '/'; rv += ic; qf = true; ++interesting; } string::size_type n = interesting; char tmp[3] = { 0,0,0 }; stack<string::size_type> psegs; psegs.push(rv.length()); string pseg; for(;n<ul;) { string::size_type unsafe = uri.find_first_of(qf?"%":"%/?#",n); if(unsafe==string::npos) { pseg.append(uri,n,ul-n-1); n = ul-1; }else{ pseg.append(uri,n,unsafe-n); n = unsafe; } char c = uri[n++]; if(c=='%') { if((n+1)>=ul) throw bad_input(OPKELE_CP_ "Unexpected end of URI encountered while parsing percent-encoded character"); tmp[0] = uri[n++]; tmp[1] = uri[n++]; if(!( isxdigit(tmp[0]) && isxdigit(tmp[1]) )) throw bad_input(OPKELE_CP_ "Invalid percent-encoded character in URI being normalized"); int cc = strtol(tmp,0,16); if( isalpha(cc) || isdigit(cc) || strchr("._~-",cc) ) pseg += cc; else{ pseg += '%'; pseg += toupper(tmp[0]); pseg += toupper(tmp[1]); } }else if(qf) { rv += pseg; rv += c; pseg.clear(); }else if(n>=ul || strchr("?/#",c)) { if(pseg.empty() || pseg==".") { }else if(pseg=="..") { if(psegs.size()>1) { rv.resize(psegs.top()); psegs.pop(); } }else{ psegs.push(rv.length()); if(c!='/') { pseg += c; qf = true; } rv += '/'; rv += pseg; } if(c=='/' && (n>=ul || strchr("?#",uri[n])) ) { rv += '/'; if(n<ul) qf = true; }else if(strchr("?#",c)) { if(psegs.size()==1 && psegs.top()==rv.length()) rv += '/'; if(pseg.empty()) rv += c; qf = true; } pseg.clear(); }else{ pseg += c; } } if(!pseg.empty()) { rv += '/'; rv += pseg; } return rv; } } } diff --git a/libopkele.pc.in b/libopkele.pc.in index bc67362..0a95e96 100644 --- a/libopkele.pc.in +++ b/libopkele.pc.in @@ -1,11 +1,11 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libopkele Description: C++ implementation of OpenID protocol Version: @VERSION@ Requires: openssl libpcre @KONFORKA_KONFORKA@ -Cflags: -I${includedir} @LIBCURL_CPPFLAGS@ @PCRE_CFLAGS@ -Libs: -L${libdir} -lopkele @LIBCURL@ @PCRE_LIBS@ +Cflags: -I${includedir} @LIBCURL_CPPFLAGS@ @PCRE_CFLAGS@ @EXPAT_CFLAGS@ +Libs: -L${libdir} -lopkele @LIBCURL@ @PCRE_LIBS@ @EXPAT_LIBS@ diff --git a/test/.gitignore b/test/.gitignore index 918b3c9..31ae686 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1,4 +1,5 @@ /.deps /.libs /test *.o +/idiscover diff --git a/test/Makefile.am b/test/Makefile.am index 4b78087..13c4cd2 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,12 +1,15 @@ -noinst_PROGRAMS = test +noinst_PROGRAMS = test idiscover DEFAULT_INCLUDES = -I${top_builddir} INCLUDES = -I${top_srcdir}/include/ ${KONFORKA_CFLAGS} test_SOURCES = test.cc test_LDADD = ${top_builddir}/lib/libopkele.la EXTRA_DIST=$(addsuffix .html,$(addprefix html/, \ empty head-in-body hkn-delegate hkn-server hkn in-body \ unclosed-head spaced-links spaced-link-attrs 2rels \ )) + +idiscover_SOURCES = idiscover.cc +idiscover_LDADD = ${top_builddir}/lib/libopkele.la diff --git a/test/idiscover.cc b/test/idiscover.cc new file mode 100644 index 0000000..2abedc9 --- a/dev/null +++ b/test/idiscover.cc @@ -0,0 +1,49 @@ +#include <iostream> +#include <stdexcept> +#include <iterator> +#include <algorithm> +using namespace std; +#include <opkele/exception.h> +#include <opkele/discovery.h> + +template<typename _PDT> + ostream& operator<<(ostream& o,const opkele::xrd::priority_map<_PDT>& pm) { + for(typename opkele::xrd::priority_map<_PDT>::const_iterator i=pm.begin(); + i!=pm.end();++i) + o << ' ' << i->second << '[' << i->first << ']'; + return o; + } + +ostream& operator<<(ostream& o,const opkele::xrd::service_t s) { + o << "{" << endl + << " Type: "; + copy(s.types.begin(),s.types.end(), + ostream_iterator<string>(o," ")); + o << endl + << " URI: " << s.uris << endl + << " LocalID: " << s.local_ids << endl; + o << "}"; +} + +int main(int argc,char **argv) { + try { + if(argc<2) + throw opkele::exception(OPKELE_CP_ "Please, give me something to resolve"); + for(int a=1;a<argc;++a) { + opkele::idiscovery_t discovery(argv[a]); + clog + << "===============================================================" << endl + << "User-supplied ID: " << argv[a] << endl + << "Normalized ID: " << discovery.normalized_id << endl + << "Canonicalized ID: " << discovery.canonicalized_id << endl + << endl + << "CanonicalID: " << discovery.xrd.canonical_ids << endl + << "LocalID: " << discovery.xrd.local_ids << endl + << "Services: " << discovery.xrd.services << endl; + } + }catch(exception& e) { + cerr << "oops: " << e.what() << endl; + _exit(1); + } + _exit(0); +} |