35 files changed, 2505 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87771db --- a/dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +configure +Makefile.in +Doxyfile +config.log +depcomp +config.guess +config.h +config.sub +ltmain.sh +INSTALL +NEWS +Makefile +config.status +stamp-h1 +config.h.in +libtool +autom4te.cache +libopkele.pc +missing +aclocal.m4 +install-sh @@ -0,0 +1,3 @@ +Klever dissected: + Michael 'hacker' Krelin <hacker@klever.net> + Leonid Ivanov <kamel@klever.net> @@ -0,0 +1,19 @@ +Copyright (c) 2005 Klever Group (http://www.klever.net/) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 --- a/dev/null +++ b/ChangeLog diff --git a/Doxyfile.in b/Doxyfile.in new file mode 100644 index 0000000..fac9a29 --- a/dev/null +++ b/Doxyfile.in @@ -0,0 +1,247 @@ +# Doxyfile 1.3.9.1 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = @PACKAGE@ +PROJECT_NUMBER = @VERSION@ +OUTPUT_DIRECTORY = @builddir@/doxydox +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = include +STRIP_FROM_INC_PATH = include +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = YES +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = NO +SHOW_DIRECTORIES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +INPUT = \ + @srcdir@/include/opkele/ +FILE_PATTERNS = *.h +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 2 +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +GENERATE_XML = YES +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = @HAVE_DOT@ +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = @DOT@ +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 0 +GENERATE_LEGEND = YES +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +SEARCHENGINE = NO diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..c577ef8 --- a/dev/null +++ b/Makefile.am @@ -0,0 +1,23 @@ + +SUBDIRS=include lib +EXTRA_DIST= NEWS NEWS.xml NEWS.xsl + +DISTCHECK_CONFIGURE_FLAGS=--with-pkgconfigdir=$${dc_install_base}/lib/pkgconfig +if HAVE_PKGCONFIG +pkgconfigdir=@PKGCONFIG_DIR@ +pkgconfig_DATA=libopkele.pc +endif + +all-local: NEWS +if HAVE_DOXYGEN +clean-local: + rm -rf doxydox +endif + +NEWS: NEWS.xsl NEWS.xml + ${XSLTPROC} -o $@ NEWS.xsl NEWS.xml + +if HAVE_DOXYGEN +dox: Doxyfile + ${DOXYGEN} +endif diff --git a/NEWS.xml b/NEWS.xml new file mode 100644 index 0000000..f8f3331 --- a/dev/null +++ b/NEWS.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="us-ascii"?> +<news> + <version version="0.0" date="July 16th, 2005"> + <ni>Initial release</ni> + </version> +</news> diff --git a/NEWS.xsl b/NEWS.xsl new file mode 100644 index 0000000..7c71307 --- a/dev/null +++ b/NEWS.xsl @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="us-ascii"?> +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + > + <xsl:output + method="text" + encoding="us-ascii" + media-type="text/plain" /> + + <xsl:template match="news"> + <xsl:apply-templates/> + </xsl:template> + <xsl:template match="version"> + <xsl:value-of select="concat(@version,' (',@date,')
')"/> + <xsl:apply-templates/> + </xsl:template> + <xsl:template match="ni"> + <xsl:text> - </xsl:text> + <xsl:apply-templates mode="text"/> + <xsl:text>
</xsl:text> + </xsl:template> + <xsl:template match="*|text()"/> + +</xsl:stylesheet> diff --git a/acinclude.d/libcurl.m4 b/acinclude.d/libcurl.m4 new file mode 100644 index 0000000..e80c206 --- a/dev/null +++ b/acinclude.d/libcurl.m4 @@ -0,0 +1,209 @@ +# LIBCURL_CHECK_CONFIG ([DEFAULT-ACTION], [MINIMUM-VERSION], +# [ACTION-IF-YES], [ACTION-IF-NO]) +# ---------------------------------------------------------- +# David Shaw <dshaw@jabberwocky.com> Jan-23-2005 +# +# Checks for libcurl. DEFAULT-ACTION is the string yes or no to +# specify whether to default to --with-libcurl or --without-libcurl. +# If not supplied, DEFAULT-ACTION is yes. MINIMUM-VERSION is the +# minimum version of libcurl to accept. Pass the version as a regular +# version number like 7.10.1. If not supplied, any version is +# accepted. ACTION-IF-YES is a list of shell commands to run if +# libcurl was successfully found and passed the various tests. +# ACTION-IF-NO is a list of shell commands that are run otherwise. +# Note that using --without-libcurl does run ACTION-IF-NO. +# +# This macro defines HAVE_LIBCURL if a working libcurl setup is found, +# and sets @LIBCURL@ and @LIBCURL_CPPFLAGS@ to the necessary values. +# Other useful defines are LIBCURL_FEATURE_xxx where xxx are the +# various features supported by libcurl, and LIBCURL_PROTOCOL_yyy +# where yyy are the various protocols supported by libcurl. Both xxx +# and yyy are capitalized. See the list of AH_TEMPLATEs at the top of +# the macro for the complete list of possible defines. Shell +# variables $libcurl_feature_xxx and $libcurl_protocol_yyy are also +# defined to 'yes' for those features and protocols that were found. +# Note that xxx and yyy keep the same capitalization as in the +# curl-config list (e.g. it's "HTTP" and not "http"). +# +# Users may override the detected values by doing something like: +# LIBCURL="-lcurl" LIBCURL_CPPFLAGS="-I/usr/myinclude" ./configure +# +# For the sake of sanity, this macro assumes that any libcurl that is +# found is after version 7.7.2, the first version that included the +# curl-config script. Note that it is very important for people +# packaging binary versions of libcurl to include this script! +# Without curl-config, we can only make educated guesses as to what +# protocols are available. Specifically, we assume that all of HTTP, +# FTP, GOPHER, FILE, TELNET, LDAP, and DICT exist, and (if SSL exists) +# HTTPS is present. All of these protocols existed when libcurl was +# first created in version 7, so this is a safe assumption. If the +# version is 7.11.0 or later, FTPS is assumed to be present as well. +# FTPS existed before then, but was not yet fully standards compliant. + +AC_DEFUN([LIBCURL_CHECK_CONFIG], +[ + AH_TEMPLATE([LIBCURL_FEATURE_SSL],[Defined if libcurl supports SSL]) + AH_TEMPLATE([LIBCURL_FEATURE_KRB4],[Defined if libcurl supports KRB4]) + AH_TEMPLATE([LIBCURL_FEATURE_IPV6],[Defined if libcurl supports IPv6]) + AH_TEMPLATE([LIBCURL_FEATURE_LIBZ],[Defined if libcurl supports libz]) + AH_TEMPLATE([LIBCURL_FEATURE_ASYNCHDNS],[Defined if libcurl supports AsynchDNS]) + + AH_TEMPLATE([LIBCURL_PROTOCOL_HTTP],[Defined if libcurl supports HTTP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_HTTPS],[Defined if libcurl supports HTTPS]) + AH_TEMPLATE([LIBCURL_PROTOCOL_FTP],[Defined if libcurl supports FTP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_FTPS],[Defined if libcurl supports FTPS]) + AH_TEMPLATE([LIBCURL_PROTOCOL_GOPHER],[Defined if libcurl supports GOPHER]) + AH_TEMPLATE([LIBCURL_PROTOCOL_FILE],[Defined if libcurl supports FILE]) + AH_TEMPLATE([LIBCURL_PROTOCOL_TELNET],[Defined if libcurl supports TELNET]) + AH_TEMPLATE([LIBCURL_PROTOCOL_LDAP],[Defined if libcurl supports LDAP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_DICT],[Defined if libcurl supports DICT]) + + AC_ARG_WITH(libcurl, + AC_HELP_STRING([--with-libcurl=DIR],[look for the curl library in DIR]), + [_libcurl_with=$withval],[_libcurl_with=ifelse([$1],,[yes],[$1])]) + + if test "$_libcurl_with" != "no" ; then + + AC_PROG_AWK + + _libcurl_version_parse="eval $AWK '{split(\$NF,A,\".\"); X=256*256*A[[1]]+256*A[[2]]+A[[3]]; print X;}'" + + _libcurl_try_link=yes + + if test -d "$_libcurl_with" ; then + CPPFLAGS="${CPPFLAGS} -I$withval/include" + LDFLAGS="${LDFLAGS} -L$withval/lib" + fi + + AC_PATH_PROG([_libcurl_config],[curl-config]) + + if test x$_libcurl_config != "x" ; then + AC_CACHE_CHECK([for the version of libcurl], + [libcurl_cv_lib_curl_version], + [libcurl_cv_lib_curl_version=`$_libcurl_config --version | $AWK '{print $[]2}'`]) + + _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse` + _libcurl_wanted=`echo ifelse([$2],,[0],[$2]) | $_libcurl_version_parse` + + if test $_libcurl_wanted -gt 0 ; then + AC_CACHE_CHECK([for libcurl >= version $2], + [libcurl_cv_lib_version_ok], + [ + if test $_libcurl_version -ge $_libcurl_wanted ; then + libcurl_cv_lib_version_ok=yes + else + libcurl_cv_lib_version_ok=no + fi + ]) + fi + + if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes ; then + if test x"$LIBCURL_CPPFLAGS" = "x" ; then + LIBCURL_CPPFLAGS=`$_libcurl_config --cflags` + fi + if test x"$LIBCURL" = "x" ; then + LIBCURL=`$_libcurl_config --libs` + fi + + # All curl-config scripts support --feature + _libcurl_features=`$_libcurl_config --feature` + + # Is it modern enough to have --protocols? (7.12.4) + if test $_libcurl_version -ge 461828 ; then + _libcurl_protocols=`$_libcurl_config --protocols` + fi + else + _libcurl_try_link=no + fi + + unset _libcurl_wanted + fi + + if test $_libcurl_try_link = yes ; then + + # we didn't find curl-config, so let's see if the user-supplied + # link line (or failing that, "-lcurl") is enough. + LIBCURL=${LIBCURL-"-lcurl"} + + AC_CACHE_CHECK([whether libcurl is usable], + [libcurl_cv_lib_curl_usable], + [ + _libcurl_save_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS" + _libcurl_save_ldflags=$LDFLAGS + LDFLAGS="$LDFLAGS $LIBCURL" + + AC_LINK_IFELSE(AC_LANG_PROGRAM([#include <curl/curl.h>],[ +/* Try and use a few common options to force a failure if we are + missing symbols or can't link. */ +int x; +curl_easy_setopt(NULL,CURLOPT_URL,NULL); +x=CURL_ERROR_SIZE; +x=CURLOPT_WRITEFUNCTION; +x=CURLOPT_FILE; +x=CURLOPT_ERRORBUFFER; +x=CURLOPT_STDERR; +x=CURLOPT_VERBOSE; +]),libcurl_cv_lib_curl_usable=yes,libcurl_cv_lib_curl_usable=no) + + CPPFLAGS=$_libcurl_save_cppflags + LDFLAGS=$_libcurl_save_ldflags + unset _libcurl_save_cppflags + unset _libcurl_save_ldflags + ]) + + if test $libcurl_cv_lib_curl_usable = yes ; then + AC_DEFINE(HAVE_LIBCURL,1, + [Define to 1 if you have a functional curl library.]) + AC_SUBST(LIBCURL_CPPFLAGS) + AC_SUBST(LIBCURL) + + for _libcurl_feature in $_libcurl_features ; do + AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_feature_$_libcurl_feature),[1]) + eval AS_TR_SH(libcurl_feature_$_libcurl_feature)=yes + done + + if test "x$_libcurl_protocols" = "x" ; then + + # We don't have --protocols, so just assume that all + # protocols are available + _libcurl_protocols="HTTP FTP GOPHER FILE TELNET LDAP DICT" + + if test x$libcurl_feature_SSL = xyes ; then + _libcurl_protocols="$_libcurl_protocols HTTPS" + + # FTPS wasn't standards-compliant until version + # 7.11.0 + if test $_libcurl_version -ge 461568; then + _libcurl_protocols="$_libcurl_protocols FTPS" + fi + fi + fi + + for _libcurl_protocol in $_libcurl_protocols ; do + AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_protocol_$_libcurl_protocol),[1]) + eval AS_TR_SH(libcurl_protocol_$_libcurl_protocol)=yes + done + fi + fi + + unset _libcurl_try_link + unset _libcurl_version_parse + unset _libcurl_config + unset _libcurl_feature + unset _libcurl_features + unset _libcurl_protocol + unset _libcurl_protocols + unset _libcurl_version + fi + + if test x$_libcurl_with = xno || test x$libcurl_cv_lib_curl_usable != xyes ; then + # This is the IF-NO path + ifelse([$4],,:,[$4]) + else + # This is the IF-YES path + ifelse([$3],,:,[$3]) + fi + + unset _libcurl_with +])dnl diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..9c9f945 --- a/dev/null +++ b/acinclude.m4 @@ -0,0 +1,213 @@ +dnl AC_WITH_PKGCONFIG([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: PKGCONFIG_PKGCONFIG PKGCONFIG_DIR +dnl AM_CONDTIONAL: HAVE_PKGCONFIG +AC_DEFUN([AC_WITH_PKGCONFIG],[ + PKGCONFIG_PKGCONFIG="" + PKGCONFIG_DIR="" + HAVE_PKGCONFIG="no" + EXPLICIT_PKGCONFIGDIR="no" + test -z "${WANT_PKGCONFIG}" && WANT_PKGCONFIG="" + AC_PATH_PROG([PKGCONFIG_PKGCONFIG],[pkg-config],[false]) + if test "${PKGCONFIG_PKGCONFIG}" != "false" ; then + AC_ARG_WITH([pkgconfigdir], + AC_HELP_STRING([--with-pkgconfigdir=dir],[Specify pkgconfig directory]), + [ + if test "${withval}" = "no" ; then + WANT_PKGCONFIG="no" + else + PKGCONFIG_DIR="${withval}" + EXPLICIT_PKGCONFIGDIR="yes" + fi + ],[ + AC_MSG_CHECKING([for pkgconfig directory]) + PKGCONFIG_DIR="`${PKGCONFIG_PKGCONFIG} --debug 2>&1 | grep '^Scanning'| head -n 1 | cut -d\' -f2-|cut -d\' -f1`" + AC_MSG_RESULT([${PKGCONFIG_DIR}]) + ] + ) + if test -d "${PKGCONFIG_DIR}" ; then + HAVE_PKGCONFIG=yes + AC_SUBST([PKGCONFIG_PKGCONFIG]) + AC_SUBST([PKGCONFIG_DIR]) + else + AC_MSG_NOTICE([unexistent pkgconfig directory: ${PKGCONFIG_DIR}]) + if test "${EXPLICIT_PKGCONFIGDIR}" = "yes" ; then + HAVE_PKGCONFIG=yes + AC_SUBST([PKGCONFIG_PKGCONFIG]) + AC_SUBST([PKGCONFIG_DIR]) + else + ifelse([$2], , :, [$2]) + fi + fi + fi + AM_CONDITIONAL([HAVE_PKGCONFIG],[test "${HAVE_PKGCONFIG}" = "yes"]) +]) + +dnl AC_WITH_DOXYGEN([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: DOXYGEN HAVE_DOXYGEN +dnl AM_CONDTIONAL: HAVE_DOXYGEN +AC_DEFUN([AC_WITH_DOXYGEN],[ + HAVE_DOXYGEN="no" + AC_PATH_PROG([DOXYGEN],[doxygen],[false]) + if test "${DOXYGEN}" = "false" ; then + ifelse([$2], , :, [$2]) + else + HAVE_DOXYGEN="yes" + AC_SUBST([DOXYGEN]) + $1 + fi + AC_SUBST([HAVE_DOXYGEN]) + AM_CONDITIONAL([HAVE_DOXYGEN],[test "${HAVE_DOXYGEN}" = "yes"]) +]) + +dnl AC_WITH_DOT([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: DOT HAVE_DOT +dnl AM_CONDITIONAL: HAVE_DOT +AC_DEFUN([AC_WITH_DOT],[ + HAVE_DOT="no" + AC_PATH_PROG([DOT],[dot],[false]) + if test "${DOT}" = "false" ; then + ifelse([$2], , :, [$2]) + else + HAVE_DOT="yes" + AC_SUBST([DOT]) + $1 + fi +AC_SUBST([HAVE_DOT]) + AM_CONDITIONAL([HAVE_DOT],[test "${HAVE_DOT}" = "yes"]) +]) + +dnl AC_WITH_PCRE([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: PCRE_CONFIG PCRE_PREFIX PCRE_EXEC_PREFIX +dnl PCRE_VERSION PCRE_CFLAGS PCRE_LIBS +dnl PCRE_LIBS_POSIX PCRE_CFLAGS_POSIX +dnl AM_CONDITIONAL: HAVE_PCRE +dnl AC_DEFINE: HAVE_PCRE PCRE_VERSION +AC_DEFUN([AC_WITH_PCRE],[ + HAVE_PCRE="no" + PCRE_CONFIG="" + PCRE_PREFIX="" + PCRE_EXEC_PREFIX="" + PCRE_VERSION="" + PCRE_CFLAGS="" + PCRE_LIBS="" + PCRE_LOCATIONS="${PATH}:/usr/local/bin:/usr/bin" + test -z "$WANT_PCRE" && WANT_PCRE="" + AC_ARG_WITH([pcre], + AC_HELP_STRING([--with-pcre=location],[Look for pcre in specified locations]), + [ + if test "${withval}" = "no" ; then + WANT_PCRE="no" + else + if test -x "${withval}" ; then + PCRE_CONFIG="${withval}" + elif test -x "${withval}/pcre-config" ; then + PCRE_CONFIG="${withval}/pcre-config" + elif test -x "${withval}/bin/pcre-config" ; then + PCRE_CONFIG="${withval}/bin/pcre-config" + fi + fi + ] + ) + if test "${WANT_PCRE}" = "no" ; then + ifelse([$2], , :, [$2]) + else + if test -z "${PCRE_CONFIG}" ; then + AC_PATH_PROG(PCRE_CONFIG,[pcre-config],false,[${PCRE_LOCATIONS}]) + if test "${PCRE_CONFIG}" = "false" ; then + ifelse([$2], , :, [$2]) + else + HAVE_PCRE="yes" + PCRE_PREFIX="`${PCRE_CONFIG} --prefix`" + PCRE_EXEC_PREFIX="`${PCRE_CONFIG} --exec-prefix`" + PCRE_VERSION="`${PCRE_CONFIG} --version`" + PCRE_CFLAGS="`${PCRE_CONFIG} --cflags`" + PCRE_LIBS="`${PCRE_CONFIG} --libs`" + PCRE_CFLAGS_POSIX="`${PCRE_CONFIG} --cflags-posix`" + PCRE_LIBS_POSIX="`${PCRE_CONFIG} --libs-posix`" + AC_SUBST([PCRE_CONFIG]) + AC_SUBST([PCRE_PREFIX]) + AC_SUBST([PCRE_EXEC_PREFIX]) + AC_SUBST([PCRE_VERSION]) + AC_SUBST([PCRE_CFLAGS]) + AC_SUBST([PCRE_LIBS]) + AC_SUBST([PCRE_CFLAGS_POSIX]) + AC_SUBST([PCRE_LIBS_POSIX]) + AC_DEFINE([HAVE_PCRE],,[pcre support]) + AC_DEFINE_UNQUOTED([PCRE_VERSION],["${PCRE_VERSION}"],[pcre version]) + $1 + fi + fi + fi + AM_CONDITIONAL([HAVE_PCRE],[test "${HAVE_PCRE}" = "yes"]) +]) + +dnl AC_WITH_PCREPP([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: PCREPP_CONFIG PCREPP_PREFIX PCREPP_EXEC_PREFIX +dnl PCREPP_VERSION PCREPP_CFLAGS PCREPP_LIBS +dnl AM_CONDITIONAL: HAVE_PCREPP +dnl AC_DEFINE: HAVE_PCREPP PCREPP_VERSION +AC_DEFUN([AC_WITH_PCREPP],[ + HAVE_PCREPP="no" + PCREPP_CONFIG="" + PCREPP_PREFIX="" + PCREPP_EXEC_PREFIX="" + PCREPP_VERSION="" + PCREPP_CFLAGS="" + PCREPP_LIBS="" + PCREPP_LOCATIONS="${PATH}:/usr/local/bin:/usr/bin" + test -z "$WANT_PCREPP" && WANT_PCREPP="" + AC_ARG_WITH([pcre++], + AC_HELP_STRING([--with-pcre++=location],[Look for pcre++ in specified locations]), + [ + if test "${withval}" = "no" ; then + WANT_PCREPP="no" + else + if test -x "${withval}" ; then + PCREPP_CONFIG="${withval}" + elif test -x "${withval}/pcre++-config" ; then + PCREPP_CONFIG="${withval}/pcre++-config" + elif test -x "${withval}/bin/pcre++-config" ; then + PCREPP_CONFIG="${withval}/bin/pcre++-config" + fi + fi + ] + ) + if test "${WANT_PCREPP}" = "no" ; then + ifelse([$2], , :, [$2]) + else + if test "${HAVE_PCRE}" != "yes" ; then + ifelse([$2], , :, [$2]) + else + if test -z "${PCREPP_CONFIG}" ; then + AC_PATH_PROG([PCREPP_CONFIG],[pcre++-config],false,[${PCREPP_LOCATIONS}]) + if test "${PCREPP_CONFIG}" = "false" ; then + ifelse([$2], , :, [$2]) + else + HAVE_PCREPP="yes" + PCREPP_PREFIX="`${PCREPP_CONFIG} --prefix`" + PCREPP_EXEC_PREFIX="`${PCREPP_CONFIG} --exec-prefix`" + PCREPP_VERSION="`${PCREPP_CONFIG} --version`" + PCREPP_CFLAGS="`${PCREPP_CONFIG} --cflags` ${PCRE_CFLAGS}" + PCREPP_LIBS="`${PCREPP_CONFIG} --libs` ${PCRE_LIBS}" + AC_SUBST([PCREPP_CONFIG]) + AC_SUBST([PCREPP_PREFIX]) + AC_SUBST([PCREPP_EXEC_PREFIX]) + AC_SUBST([PCREPP_VERSION]) + AC_SUBST([PCREPP_CFLAGS]) + AC_SUBST([PCREPP_LIBS]) + AC_DEFINE([HAVE_PCREPP],,[pcre++ support]) + AC_DEFINE_UNQUOTED([PCREPP_VERSION],["${PCREPP_VERSION}"],[pcre++ version]) + $1 + fi + fi + fi + fi + AM_CONDITIONAL([HAVE_PCREPP],[test "${HAVE_PCREPP}" = "yes"]) +]) + +m4_include([acinclude.d/libcurl.m4]) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..ba27501 --- a/dev/null +++ b/autogen.sh @@ -0,0 +1,9 @@ +#!/bin/sh +WANT_AUTOMAKE=1.8 +export WANT_AUTOMAKE +libtoolize -f \ +&& aclocal \ +&& autoheader \ +&& automake -a \ +&& autoconf \ +&& ./configure "$@" diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..a31f5b1 --- a/dev/null +++ b/configure.ac @@ -0,0 +1,87 @@ +AC_INIT([libopkele], [0.0], [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 + +AC_HEADER_STDC + +AC_PATH_PROG([XSLTPROC],[xsltproc],[true]) +AC_WITH_PKGCONFIG + +PKG_CHECK_MODULES([OPENSSL],[openssl],[ + AC_MSG_RESULT([yes]) + ],[ + 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_MSG_RESULT([yes]) + 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]) + ] + ) +fi + +AC_LANG_PUSH([C++]) + AC_CHECK_LIB([mimetic],[main],[ + MIMETIC_LIBS=-lmimetic + AC_SUBST([MIMETIC_CFLAGS]) + AC_SUBST([MIMETIC_LIBS]) + ],[ + AC_MSG_ERROR([no mimetic library found. get one from http://codesink.org/mimetic_mime_library.html]) + ] + ) +AC_LANG_POP([C++]) + +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_WITH_PCRE([ + AC_WITH_PCREPP(,[ + AC_MSG_ERROR([no pcre++ library found. get one at http://www.daemon.de/PCRE]) + ]) + ],[ + AC_MSG_ERROR([no pcre library found. get one at http://www.pcre.org/]) + ] +) + +AC_CONFIG_FILES([ + Makefile + libopkele.pc + Doxyfile + include/Makefile + lib/Makefile +]) +AC_OUTPUT diff --git a/include/.gitignore b/include/.gitignore new file mode 100644 index 0000000..3dda729 --- a/dev/null +++ b/include/.gitignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..b014752 --- a/dev/null +++ b/include/Makefile.am @@ -0,0 +1,11 @@ +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 +EXTRA_DIST = \ + opkele/data.h \ + opkele/util.h diff --git a/include/opkele/.gitignore b/include/opkele/.gitignore new file mode 100644 index 0000000..ffa24dc --- a/dev/null +++ b/include/opkele/.gitignore @@ -0,0 +1,2 @@ +acconfig.h +stamp-h2 diff --git a/include/opkele/acconfig.h.in b/include/opkele/acconfig.h.in new file mode 100644 index 0000000..d56a1cd --- a/dev/null +++ b/include/opkele/acconfig.h.in @@ -0,0 +1,3 @@ + +/* defined in presence of konforka library */ +#undef OPKELE_HAVE_KONFORKA diff --git a/include/opkele/association.h b/include/opkele/association.h new file mode 100644 index 0000000..5eb1cc3 --- a/dev/null +++ b/include/opkele/association.h @@ -0,0 +1,89 @@ +#ifndef __OPKELE_ASSOCIATION_H +#define __OPKELE_ASSOCIATION_H + +#include <time.h> +#include <opkele/types.h> + +/** + * @file + * @brief reference implementation of association_t + */ + +/** + * @brief the main opkele namespace + */ +namespace opkele { + + /** + * reference implementation of association_t class. + */ + class association : public association_t { + public: + /** + * OpenID server name + */ + string _server; + /** + * association handle + */ + string _handle; + /** + * association type + */ + string _assoc_type; + /** + * the secret + */ + secret_t _secret; + /** + * expiration time + */ + time_t _expires; + /** + * statelessness of the assoc_handle + */ + bool _stateless; + + /** + * @param __server the server name + * @param __handle association handle + * @param __assoc_type association type + * @param __secret the secret + * @param __expires expiration time + * @param __stateless statelessness of the assoc_handle + */ + association(const string& __server, const string& __handle, + const string& __assoc_type, const secret_t& __secret, + time_t __expires, bool __stateless) + : _server(__server), _handle(__handle), _assoc_type(__assoc_type), + _secret(__secret), _expires(__expires), _stateless(__stateless) { } + + /** + * @overload association_t::server() + */ + virtual string server() const { return _server; } + /** + * @overload association_t::handle() + */ + virtual string handle() const { return _handle; } + /** + * @overload association_t::assoc_type() + */ + virtual string assoc_type() const { return _assoc_type; } + /** + * @overload association_t::secret() + */ + virtual secret_t secret() const { return _secret; } + /** + * @overload association_t::expires_in() + */ + virtual int expires_in() const { return _expires-time(0); } + /** + * @overload associationn_t::stateless() + */ + virtual bool stateless() const { return _stateless; } + }; + +} + +#endif /* __OPKELE_ASSOCIATION_H */ diff --git a/include/opkele/consumer.h b/include/opkele/consumer.h new file mode 100644 index 0000000..b9c29bd --- a/dev/null +++ b/include/opkele/consumer.h @@ -0,0 +1,135 @@ +#ifndef __OPKELE_CONSUMER_H +#define __OPKELE_CONSUMER_H + +#include <opkele/types.h> + +/** + * @file + * @brief OpenID consumer-side functionality + */ + +/** + * @brief the main opkele namespace + */ +namespace opkele { + + /** + * implementation of basic consumer functionality + */ + class consumer_t { + public: + + /** + * store association. The function should be overridden in the real + * implementation to provide persistent associations store. + * @param server the OpenID server + * @param handle association handle + * @param secret the secret associated with the server and handle + * @param expires_in the number of seconds until the handle is expired + * @return the auto_ptr<> for the newly allocated association_t object + */ + virtual assoc_t store_assoc(const string& server,const string& handle,const secret_t& secret,int expires_in) = 0; + /** + * retrieve stored association. The function should be overridden + * in the real implementation to provide persistent assocations + * store. + * @param server the OpenID server + * @param handle association handle + * @return the autho_ptr<> for the newly allocated association_t object + * @throw failed_lookup in case of error + */ + virtual assoc_t retrieve_assoc(const string& server,const string& handle) = 0; + /** + * invalidate stored association. The function should be overridden + * in the real implementation of the consumer. + * @param server the OpenID server + * @param handle association handle + */ + virtual void invalidate_assoc(const string& server,const string& handle) = 0; + /** + * retrieve any unexpired association for the server. If the + * function is not overridden in the real implementation, the new + * association will be established for each request. + * @param server the OpenID server + * @return the auto_ptr<> for the newly allocated association_t object + * @throw failed_lookup in case of absence of the handle + */ + virtual assoc_t find_assoc(const string& server); + + /** + * retrieve the metainformation contained in link tags from the + * page pointed by url. the function may implement caching of the + * information. + * @param url url to harvest for link tags + * @param server reference to the string object where to put + * openid.server value + * @param delegate reference to the string object where to put the + * openid.delegate value (if any) + */ + virtual void retrieve_links(const string& url,string& server,string& delegate); + + /** + * perform the associate request to OpenID server. + * @param server the OpenID server + * @return the auto_ptr<> for the newly allocated association_t + * object, representing established association + * @throw exception in case of error + */ + assoc_t associate(const string& server); + /** + * prepare the parameters for the checkid_immediate + * request. + * @param identity the identity to verify + * @param return_to the return_to url to pass with the request + * @param trust_root the trust root to advertise with the request + * @return the location string + * @throw exception in case of error + */ + string checkid_immediate(const string& identity,const string& return_to,const string& trust_root=""); + /** + * prepare the parameters for the checkid_setup + * request. + * @param identity the identity to verify + * @param return_to the return_to url to pass with the request + * @param trust_root the trust root to advertise with the request + * @return the location string + * @throw exception in case of error + */ + string checkid_setup(const string& identity,const string& return_to,const string& trust_root=""); + /** + * the actual implementation behind checkid_immediate() and + * checkid_setup() functions. + * @param mode checkid_* mode - either mode_checkid_immediate or mode_checkid_setup + * @param identity the identity to verify + * @param return_to the return_to url to pass with the request + * @param trust_root the trust root to advertise with the request + * @return the location string + * @throw exception in case of error + */ + string checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root=""); + /** + * verify the id_res response + * @param pin the response parameters + * @param identity the identity being checked (if not specified, extracted + * from the openid.identity parameter + * @throw id_res_mismatch in case of signature + * mismatch + * @throw id_res_setup in case of + * openid.user_setup_url failure (supposedly + * checkid_immediate only) + * @throw id_res_failed in case of failure + * @throw exception in case of other failures + */ + void id_res(const params_t& pin,const string& identity=""); + /** + * perform a check_authentication request. + * @param server the OpenID server + * @param p request parameters + */ + void check_authentication(const string& server,const params_t& p); + + }; + +} + +#endif /* __OPKELE_CONSUMER_H */ diff --git a/include/opkele/data.h b/include/opkele/data.h new file mode 100644 index 0000000..7fc635b --- a/dev/null +++ b/include/opkele/data.h @@ -0,0 +1,12 @@ +#ifndef __OPKELE_DATA_H +#define __OPKELE_DATA_H + +namespace opkele { + + namespace data { + extern const char *_default_p; + extern const char *_default_g; + } +} + +#endif /* __OPKELE_DATA_H */ diff --git a/include/opkele/exception.h b/include/opkele/exception.h new file mode 100644 index 0000000..2ac0661 --- a/dev/null +++ b/include/opkele/exception.h @@ -0,0 +1,206 @@ +#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 <stdexcept> +/** + * 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 + +/* + * @brief the main opkele namespace + */ +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 */ + explicit + exception(const string& w) + : std::exception(w) { } +# 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) { } + }; + + /** + * 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() { } + }; + +} + +#endif /* __OPKELE_EXCEPTION_H */ diff --git a/include/opkele/opkele-config.h b/include/opkele/opkele-config.h new file mode 100644 index 0000000..70c2d26 --- a/dev/null +++ b/include/opkele/opkele-config.h @@ -0,0 +1,6 @@ +#ifndef __OPKELE_OPKELE_CONFIG_H +#define __OPKELE_OPKELE_CONFIG_H + +#include "opkele/acconfig.h" + +#endif /* __OPKELE_OPKELE_CONFIG_H */ diff --git a/include/opkele/server.h b/include/opkele/server.h new file mode 100644 index 0000000..fe07448 --- a/dev/null +++ b/include/opkele/server.h @@ -0,0 +1,95 @@ +#ifndef __OPKELE_SERVER_H +#define __OPKELE_SERVER_H + +/** + * @file + * @brief OpenID server-side functionality + */ + +#include <opkele/types.h> + +/** + * @brief the main opkele namespace + */ +namespace opkele { + + /** + * implementation of basic server functionality + */ + class server_t { + public: + + /** + * allocate the new association. The function should be overridden + * in the real implementation to provide persistent assocations + * store. + * @param mode the mode of request being processed to base the + * statelessness of the association upon + * @return the auto_ptr<> for the newly allocated association_t object + */ + virtual assoc_t alloc_assoc(mode_t mode) = 0; + /** + * retrieve the association. The function should be overridden in + * the reqal implementation to provide persistent assocations + * store. + * @param h association handle + * @return the auto_ptr<> for the newly allocated association_t object + * @throw failed_lookup in case of failure + */ + virtual assoc_t retrieve_assoc(const string& h) = 0; + + /** + * validate the identity. + * @param assoc association object + * @param pin incoming request parameters + * @param identity being verified + * @param trust_root presented in the request + * @throw exception if identity can not be confirmed + */ + virtual void validate(const association_t& assoc,const params_t& pin,const string& identity,const string& trust_root) = 0; + + + /** + * process the associate request. + * @param pin the incoming request parameters + * @param pout the store for the response parameters + */ + void associate(const params_t& pin,params_t& pout); + /** + * process the checkid_immediate request. + * @param pin the incoming request parameters + * @param return_to reference to the object to store return_to url to + * @param pout the response parameters + * @throw exception in case of errors or negative reply + */ + void checkid_immediate(const params_t& pin,string& return_to,params_t& pout); + /** + * process the checkid_setup request. + * @param pin the incoming request parameters + * @param return_to reference to the object to store return_to url to + * @param pout the response parameters + * @throw exception in case of errors or negative reply + */ + void checkid_setup(const params_t& pin,string& return_to,params_t& pout); + /** + * the actual functionality behind checkid_immediate() and + * checkid_setup() + * @param mode the request being processed (either + * mode_checkid_immediate or mode_checkid_setup) + * @param pin the incoming request parameters + * @param return_to reference to the object to store return_to url to + * @param pout the response parameters + * @throw exception in case of errors or negative reply + */ + void checkid_(mode_t mode,const params_t& pin,string& return_to,params_t& pout); + /** + * process the check_authentication request. + * @param pin incoming request parameters + * @param pout response parameters + */ + void check_authentication(const params_t& pin,params_t& pout); + }; + +} + +#endif /* __OPKELE_SERVER_H */ diff --git a/include/opkele/types.h b/include/opkele/types.h new file mode 100644 index 0000000..ba06776 --- a/dev/null +++ b/include/opkele/types.h @@ -0,0 +1,168 @@ +#ifndef __OPKELE_TYPES_H +#define __OPKELE_TYPES_H + +/** + * @file + * @brief various types declarations + */ + +#include <ostream> +#include <vector> +#include <string> +#include <map> +#include <memory> + +/** + * @brief the main opkele namespace + */ +namespace opkele { + using std::vector; + using std::string; + using std::map; + using std::ostream; + using std::auto_ptr; + + /** + * 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: + + /** + * 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; + }; + + /** + * 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); + +} + +#endif /* __OPKELE_TYPES_H */ diff --git a/include/opkele/util.h b/include/opkele/util.h new file mode 100644 index 0000000..fbbef93 --- a/dev/null +++ b/include/opkele/util.h @@ -0,0 +1,60 @@ +#ifndef __OPKELE_UTIL_H +#define __OPKELE_UTIL_H + +#include <time.h> +#include <string> +#include <openssl/bn.h> +#include <openssl/dh.h> + +namespace opkele { + using std::string; + + namespace util { + + class bignum_t { + public: + BIGNUM *_bn; + + bignum_t() : _bn(0) { } + bignum_t(BIGNUM *bn) : _bn(bn) { } + ~bignum_t() throw() { if(_bn) BN_free(_bn); } + + bignum_t& operator=(BIGNUM *bn) { if(_bn) BN_free(_bn); _bn = bn; return *this; } + + operator const BIGNUM*(void) const { return _bn; } + operator BIGNUM*(void) { return _bn; } + }; + class dh_t { + public: + DH *_dh; + + dh_t() : _dh(0) { } + dh_t(DH *dh) : _dh(dh) { } + ~dh_t() throw() { if(_dh) DH_free(_dh); } + + dh_t& operator=(DH *dh) { if(_dh) DH_free(_dh); _dh = dh; return *this; } + + operator const DH*(void) const { return _dh; } + operator DH*(void) { return _dh; } + + DH* operator->() { return _dh; } + const DH* operator->() const { return _dh; } + }; + + BIGNUM *base64_to_bignum(const string& b64); + BIGNUM *dec_to_bignum(const string& dec); + string bignum_to_base64(const BIGNUM *bn); + + string time_to_w3c(time_t t); + time_t w3c_to_time(const string& w); + + string canonicalize_url(const string& url); + string url_encode(const string& str); + + string long_to_string(long l); + long string_to_long(const string& s); + } + +} + +#endif /* __OPKELE_UTIL_H */ diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..2325be6 --- a/dev/null +++ b/lib/.gitignore @@ -0,0 +1,7 @@ +*.lo +*.o +Makefile.in +libopkele.la +.libs +.deps +Makefile diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..6f3f9f3 --- a/dev/null +++ b/lib/Makefile.am @@ -0,0 +1,24 @@ +lib_LTLIBRARIES = libopkele.la + +INCLUDES = \ + -I${top_srcdir}/include/ \ + ${KONFORKA_CFLAGS} \ + ${OPENSSL_CFLAGS} \ + ${MIMETIC_CFLAGS} \ + ${LIBCURL_CPPFLAGS} \ + ${PCREPP_CFLAGS} +LDADD = \ + ${LIBCURL} \ + ${PCREPP_LIBS} \ + ${MIMETIC_LIBS} \ + ${OPENSSL_LIBS} \ + ${KONFORKA_LIBS} + +libopkele_la_SOURCES = \ + params.cc \ + util.cc \ + server.cc \ + secret.cc \ + data.cc \ + consumer.cc \ + exception.cc diff --git a/lib/consumer.cc b/lib/consumer.cc new file mode 100644 index 0000000..bd76b61 --- a/dev/null +++ b/lib/consumer.cc @@ -0,0 +1,316 @@ +#include <algorithm> +#include <opkele/util.h> +#include <opkele/exception.h> +#include <opkele/data.h> +#include <opkele/consumer.h> +#include <openssl/sha.h> +#include <openssl/hmac.h> +#include <mimetic/mimetic.h> +#include <curl/curl.h> +#include <pcre++.h> + +#include <iostream> + +/* silly mimetic */ +#undef PACKAGE +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION +#undef VERSION + +#include "config.h" + +namespace opkele { + using namespace std; + + class curl_t { + public: + CURL *_c; + + curl_t() : _c(0) { } + curl_t(CURL *c) : _c(c) { } + ~curl_t() throw() { if(_c) curl_easy_cleanup(_c); } + + curl_t& operator=(CURL *c) { if(_c) curl_easy_cleanup(_c); _c=c; return *this; } + + operator const CURL*(void) const { return _c; } + operator CURL*(void) { return _c; } + }; + + static CURLcode curl_misc_sets(CURL* c) { + CURLcode r; + (r=curl_easy_setopt(c,CURLOPT_FOLLOWLOCATION,1)) + || (r=curl_easy_setopt(c,CURLOPT_MAXREDIRS,5)) + || (r=curl_easy_setopt(c,CURLOPT_DNS_CACHE_TIMEOUT,120)) + || (r=curl_easy_setopt(c,CURLOPT_DNS_USE_GLOBAL_CACHE,1)) + || (r=curl_easy_setopt(c,CURLOPT_USERAGENT,PACKAGE_NAME"/"PACKAGE_VERSION)) + || (r=curl_easy_setopt(c,CURLOPT_TIMEOUT,20)) + ; + return r; + } + + static size_t _curl_tostring(void *ptr,size_t size,size_t nmemb,void *stream) { + string *str = (string*)stream; + size_t bytes = size*nmemb; + size_t get = min(16384-str->length(),bytes); + str->append((const char*)ptr,get); + return get; + } + + assoc_t consumer_t::associate(const string& server) { + 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()"); + string request = + "openid.mode=associate" + "&openid.assoc_type=HMAC-SHA1" + "&openid.session_type=DH-SHA1" + "&openid.dh_consumer_public="; + request += util::url_encode(util::bignum_to_base64(dh->pub_key)); + curl_t curl = curl_easy_init(); + if(!curl) + throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()"); + string response; + CURLcode r; + (r=curl_misc_sets(curl)) + || (r=curl_easy_setopt(curl,CURLOPT_URL,server.c_str())) + || (r=curl_easy_setopt(curl,CURLOPT_POST,1)) + || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDS,request.data())) + || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,request.length())) + || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring)) + || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&response)) + ; + if(r) + throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r); + if(r=curl_easy_perform(curl)) + throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r); + params_t p; p.parse_keyvalues(response); + if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1") + throw bad_input(OPKELE_CP_ "unsupported assoc_type"); + string st; + if(p.has_param("session_type")) st = p.get_param("session_type"); + if((!st.empty()) && st!="DH-SHA1") + throw bad_input(OPKELE_CP_ "unsupported session_type"); + secret_t secret; + if(st.empty()) { + secret.from_base64(p.get_param("mac_key")); + }else{ + util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public")); + vector<unsigned char> ck(DH_size(dh)); + int cklen = DH_compute_key(&(ck.front()),s_pub,dh); + if(cklen<0) + throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); + ck.resize(cklen); + // OpenID algorithm requires extra zero in case of set bit here + if(ck[0]&0x80) ck.insert(ck.begin(),1,0); + unsigned char key_sha1[SHA_DIGEST_LENGTH]; + SHA1(&(ck.front()),ck.size(),key_sha1); + secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key")); + } + int expires_in = 0; + if(p.has_param("expires_in")) { + expires_in = util::string_to_long(p.get_param("expires_in")); + }else if(p.has_param("issued") && p.has_param("expiry")) { + expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued")); + }else + throw bad_input(OPKELE_CP_ "no expiration information"); + return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in); + } + + string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root) { + return checkid_(mode_checkid_immediate,identity,return_to,trust_root); + } + string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root) { + return checkid_(mode_checkid_setup,identity,return_to,trust_root); + } + string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root) { + params_t p; + if(mode==mode_checkid_immediate) + p["mode"]="checkid_immediate"; + else if(mode==mode_checkid_setup) + p["mode"]="checkid_setup"; + else + throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); + string iurl = util::canonicalize_url(identity); + string server, delegate; + retrieve_links(iurl,server,delegate); + p["identity"] = delegate.empty()?iurl:delegate; + if(!trust_root.empty()) + p["trust_root"] = trust_root; + p["return_to"] = return_to; + try { + try { + string ah = find_assoc(server)->handle(); + p["assoc_handle"] = ah; + }catch(failed_lookup& fl) { + string ah = associate(server)->handle(); + p["assoc_handle"] = ah; + } + }catch(exception& e) { } + return p.append_query(server); + } + + void consumer_t::id_res(const params_t& pin,const string& identity) { + if(pin.has_param("openid.user_setup_url")) + throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url")); + string server,delegate; + retrieve_links(identity.empty()?pin.get_param("openid.identity"):util::canonicalize_url(identity),server,delegate); + try { + assoc_t assoc = retrieve_assoc(server,pin.get_param("openid.assoc_handle")); + const string& sigenc = pin.get_param("openid.sig"); + mimetic::Base64::Decoder b; + vector<unsigned char> sig; + mimetic::decode( + sigenc.begin(),sigenc.end(), b, + back_insert_iterator<vector<unsigned char> >(sig) ); + const string& slist = pin.get_param("openid.signed"); + string kv; + 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); + kv += f; + kv += ':'; + f.insert(0,"openid."); + kv += pin.get_param(f); + kv += '\n'; + if(co==string::npos) + break; + p = co+1; + } + secret_t secret = assoc->secret(); + unsigned int md_len = 0; + unsigned char *md = HMAC( + EVP_sha1(), + &(secret.front()),secret.size(), + (const unsigned char *)kv.data(),kv.length(), + 0,&md_len); + if(sig.size()!=md_len || memcmp(&(sig.front()),md,md_len)) + throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); + }catch(failed_lookup& e) { /* XXX: more specific? */ + const string& slist = pin.get_param("openid.signed"); + string::size_type pp = 0; + params_t p; + while(true) { + string::size_type co = slist.find(',',pp); + string f = "openid."; + f += (co==string::npos)?slist.substr(pp):slist.substr(pp,co-pp); + p[f] = pin.get_param(f); + if(co==string::npos) + break; + pp = co+1; + } + p["openid.assoc_handle"] = pin.get_param("openid.assoc_handle"); + p["openid.sig"] = pin.get_param("openid.sig"); + p["openid.signed"] = pin.get_param("openid.signed"); + try { + string ih = pin.get_param("openid.invalidate_handle"); + p["openid.invalidate_handle"] = ih; + }catch(failed_lookup& fl) { } + try { + check_authentication(server,p); + }catch(failed_check_authentication& fca) { + throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); + } + } + } + + void consumer_t::check_authentication(const string& server,const params_t& p) { + string request = "openid.mode=check_authentication"; + for(params_t::const_iterator i=p.begin();i!=p.end();++i) { + if(i->first!="openid.mode") { + request += '&'; + request += i->first; + request += '='; + request += util::url_encode(i->second); + } + } + curl_t curl = curl_easy_init(); + if(!curl) + throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()"); + string response; + CURLcode r; + (r=curl_misc_sets(curl)) + || (r=curl_easy_setopt(curl,CURLOPT_URL,server.c_str())) + || (r=curl_easy_setopt(curl,CURLOPT_POST,1)) + || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDS,request.data())) + || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,request.length())) + || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring)) + || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&response)) + ; + if(r) + throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r); + if(r=curl_easy_perform(curl)) + throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r); + params_t pp; pp.parse_keyvalues(response); + if(pp.has_param("invalidate_handle")) + invalidate_assoc(server,pp.get_param("invalidate_handle")); + if(pp.has_param("is_valid")) { + if(pp.get_param("is_valid")=="true") + return; + }else if(pp.has_param("lifetime")) { + if(util::string_to_long(pp.get_param("lifetime"))) + return; + } + throw failed_check_authentication(OPKELE_CP_ "failed to verify response"); + } + + void consumer_t::retrieve_links(const string& url,string& server,string& delegate) { + server.erase(); + delegate.erase(); + curl_t curl = curl_easy_init(); + if(!curl) + throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()"); + string html; + CURLcode r; + (r=curl_misc_sets(curl)) + || (r=curl_easy_setopt(curl,CURLOPT_URL,url.c_str())) + || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring)) + || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&html)) + ; + if(r) + throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r); + r = curl_easy_perform(curl); + if(r && r!=CURLE_WRITE_ERROR) + throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r); + pcrepp::Pcre bre("<body\\b",PCRE_CASELESS); + // strip out everything past body + if(bre.search(html)) + html.erase(bre.get_match_start()); + pcrepp::Pcre hdre("<head[^>]*>",PCRE_CASELESS); + if(!hdre.search(html)) + throw bad_input(OPKELE_CP_ "failed to find head"); + html.erase(0,hdre.get_match_end()+1); + pcrepp::Pcre lre("<link\\b([^>]+)>",PCRE_CASELESS), + rre("\\brel=['\"]([^'\"]+)['\"]",PCRE_CASELESS), + hre("\\bhref=['\"]([^'\"]+)['\"]",PCRE_CASELESS); + while(lre.search(html)) { + string attrs = lre[0]; + html.erase(0,lre.get_match_end()+1); + if(!(rre.search(attrs)&&hre.search(attrs))) + continue; + if(rre[0]=="openid.server") { + server = hre[0]; + if(!delegate.empty()) + break; + }else if(rre[0]=="openid.delegate") { + delegate = hre[0]; + if(!server.empty()) + break; + } + } + if(server.empty()) + throw failed_assertion(OPKELE_CP_ "The location has no openid.server declaration"); + } + + assoc_t consumer_t::find_assoc(const string& server) { + throw failed_lookup(OPKELE_CP_ "no find_assoc() provided"); + } + +} diff --git a/lib/data.cc b/lib/data.cc new file mode 100644 index 0000000..c040430 --- a/dev/null +++ b/lib/data.cc @@ -0,0 +1,11 @@ +#include <opkele/data.h> + +namespace opkele { + + namespace data { + + const char *_default_p = "155172898181473697471232257763715539915724801966915404479707795314057629378541917580651227423698188993727816152646631438561595825688188889951272158842675419950341258706556549803580104870537681476726513255747040765857479291291572334510643245094715007229621094194349783925984760375594985848253359305585439638443"; + const char *_default_g = "2"; + + } +} diff --git a/lib/exception.cc b/lib/exception.cc new file mode 100644 index 0000000..b7c1702 --- a/dev/null +++ b/lib/exception.cc @@ -0,0 +1,22 @@ +#include <openssl/err.h> +#include <curl/curl.h> +#include <opkele/exception.h> + +namespace opkele { + + exception_openssl::exception_openssl(OPKELE_E_PARS) + : _error(ERR_peek_last_error()), + _ssl_string(ERR_error_string(_error,0)), + exception(OPKELE_E_CONS_ w+" ["+_ssl_string+']') { + } + + exception_curl::exception_curl(OPKELE_E_PARS) + : _error(CURLE_OK), + exception_network(OPKELE_E_CONS) { } + exception_curl::exception_curl(OPKELE_E_PARS,CURLcode e) + : _error(e), + _curl_string(curl_easy_strerror(e)), + exception_network(OPKELE_E_CONS_ w+" ["+_curl_string+']') { + } + +} diff --git a/lib/params.cc b/lib/params.cc new file mode 100644 index 0000000..14f1a53 --- a/dev/null +++ b/lib/params.cc @@ -0,0 +1,96 @@ +#include <opkele/types.h> +#include <opkele/exception.h> +#include <opkele/util.h> +#include <openssl/sha.h> +#include <openssl/hmac.h> +#include <mimetic/mimetic.h> + +namespace opkele { + using namespace std; + + bool params_t::has_param(const string& n) const { + return find(n)!=end(); + } + const string& params_t::get_param(const string& n) const { + const_iterator i = find(n); + if(i==end()) + throw failed_lookup(OPKELE_CP_ n+": no such parameter"); + return i->second; + } + string& params_t::get_param(const string& n) { + iterator i = find(n); + if(i==end()) + throw failed_lookup(OPKELE_CP_ n+": no such parameter"); + return i->second; + } + + void params_t::parse_keyvalues(const string& kv) { + clear(); + string::size_type p = 0; + while(true) { + string::size_type co = kv.find(':',p); + if(co==string::npos) + break; + string::size_type nl = kv.find('\n',co+1); + if(nl==string::npos) + throw bad_input(OPKELE_CP_ "malformed input"); + insert(value_type(kv.substr(p,co-p),kv.substr(co+1,nl-co-1))); + p = nl+1; + } + } + + void params_t::sign(secret_t secret,string& sig,const string& slist,const char *prefix) const { + string kv; + 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); + kv += f; + kv += ':'; + if(prefix) f.insert(0,prefix); + kv += get_param(f); + kv += '\n'; + if(co==string::npos) + break; + p = co+1; + } + unsigned int md_len = 0; + unsigned char *md = HMAC( + EVP_sha1(), + &(secret.front()),secret.size(), + (const unsigned char *)kv.data(),kv.length(), + 0,&md_len); + mimetic::Base64::Encoder b(0); + sig.erase(); + mimetic::encode( + md,md+md_len, b, + back_insert_iterator<string>(sig) ); + } + + string params_t::append_query(const string& url,const char *prefix) const { + string rv = url; + bool p = true; + if(rv.find('?')==string::npos) { + rv += '?'; + p = false; + } + for(const_iterator i=begin();i!=end();++i) { + if(p) + rv += '&'; + else + p = true; + rv += prefix; + rv += i->first; + rv += '='; + rv += util::url_encode(i->second); + } + return rv; + } + + ostream& operator << (ostream& o,const params_t& p) { + for(params_t::const_iterator i=p.begin();i!=p.end();++i) + o << i->first << ':' << i->second << '\n'; + return o; + } + +} diff --git a/lib/secret.cc b/lib/secret.cc new file mode 100644 index 0000000..ae8a3c5 --- a/dev/null +++ b/lib/secret.cc @@ -0,0 +1,61 @@ +#include <algorithm> +#include <functional> +#include <opkele/types.h> +#include <opkele/exception.h> +#include <mimetic/mimetic.h> + +namespace opkele { + using namespace std; + + template<class __a1,class __a2,class __r> + struct bitwise_xor : public binary_function<__a1,__a2,__r> { + __r operator() (const __a1& a1,const __a2& a2) const { + return a1^a2; + } + }; + + void secret_t::enxor_to_base64(const unsigned char *key_sha1,string& rv) const { + if(size()!=20) + throw bad_input(OPKELE_CP_ "wrong secret size"); + vector<unsigned char> tmp; + transform( + begin(), end(), + key_sha1, + back_insert_iterator<vector<unsigned char> >(tmp), + bitwise_xor<unsigned char,unsigned char,unsigned char>() ); + mimetic::Base64::Encoder b(0); + mimetic::encode( + tmp.begin(),tmp.end(), b, + back_insert_iterator<string>(rv) ); + } + + void secret_t::enxor_from_base64(const unsigned char *key_sha1,const string& b64) { + mimetic::Base64::Decoder b; + clear(); + mimetic::decode( + b64.begin(),b64.end(), b, + back_insert_iterator<secret_t>(*this) ); + transform( + begin(), end(), + key_sha1, + begin(), + bitwise_xor<unsigned char,unsigned char,unsigned char>() ); + } + + void secret_t::to_base64(string& rv) const { + if(size()!=20) + throw bad_input(OPKELE_CP_ "wrong secret size"); + mimetic::Base64::Encoder b(0); + mimetic::encode( + begin(),end(), b, + back_insert_iterator<string>(rv) ); + } + + void secret_t::from_base64(const string& b64) { + mimetic::Base64::Decoder b; + mimetic::decode( + b64.begin(),b64.end(), b, + back_insert_iterator<secret_t>(*this) ); + } + +} diff --git a/lib/server.cc b/lib/server.cc new file mode 100644 index 0000000..51d4554 --- a/dev/null +++ b/lib/server.cc @@ -0,0 +1,169 @@ +#include <vector> +#include <openssl/sha.h> +#include <openssl/hmac.h> +#include <mimetic/mimetic.h> +#include <opkele/util.h> +#include <opkele/exception.h> +#include <opkele/server.h> +#include <opkele/data.h> + +namespace opkele { + using namespace std; + + void server_t::associate(const params_t& pin,params_t& pout) { + util::dh_t dh; + util::bignum_t c_pub; + unsigned char key_sha1[SHA_DIGEST_LENGTH]; + enum { + sess_cleartext, + sess_dh_sha1 + } st = sess_cleartext; + if( + pin.has_param("openid.session_type") + && pin.get_param("openid.session_type")=="DH-SHA1" ) { + /* TODO: fallback to cleartext in case of exceptions here? */ + if(!(dh = DH_new())) + throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); + c_pub = util::base64_to_bignum(pin.get_param("openid.dh_consumer_public")); + if(pin.has_param("openid.dh_modulus")) + dh->p = util::base64_to_bignum(pin.get_param("openid.dh_modulus")); + else + dh->p = util::dec_to_bignum(data::_default_p); + if(pin.has_param("openid.dh_gen")) + dh->g = util::base64_to_bignum(pin.get_param("openid.dh_gen")); + else + 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)); + int cklen = DH_compute_key(&(ck.front()),c_pub,dh); + if(cklen<0) + throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); + ck.resize(cklen); + // OpenID algorithm requires extra zero in case of set bit here + if(ck[0]&0x80) ck.insert(ck.begin(),1,0); + SHA1(&(ck.front()),ck.size(),key_sha1); + st = sess_dh_sha1; + } + assoc_t assoc = alloc_assoc(mode_associate); + time_t now = time(0); + pout.clear(); + pout["assoc_type"] = assoc->assoc_type(); + pout["assoc_handle"] = assoc->handle(); + /* TODO: eventually remove deprecated stuff */ + pout["issued"] = util::time_to_w3c(now); + pout["expiry"] = util::time_to_w3c(now+assoc->expires_in()); + pout["expires_in"] = util::long_to_string(assoc->expires_in()); + secret_t secret = assoc->secret(); + switch(st) { + case sess_dh_sha1: + pout["session_type"] = "DH-SHA1"; + pout["dh_server_public"] = util::bignum_to_base64(dh->pub_key); + secret.enxor_to_base64(key_sha1,pout["enc_mac_key"]); + break; + default: + secret.to_base64(pout["mac_key"]); + break; + } + } + + void server_t::checkid_immediate(const params_t& pin,string& return_to,params_t& pout) { + checkid_(mode_checkid_immediate,pin,return_to,pout); + } + + void server_t::checkid_setup(const params_t& pin,string& return_to,params_t& pout) { + checkid_(mode_checkid_setup,pin,return_to,pout); + } + + void server_t::checkid_(mode_t mode,const params_t& pin,string& return_to,params_t& pout) { + if(mode!=mode_checkid_immediate && mode!=mode_checkid_setup) + throw bad_input(OPKELE_CP_ "invalid checkid_* mode"); + assoc_t assoc; + try { + assoc = retrieve_assoc(pin.get_param("openid.assoc_handle")); + }catch(failed_lookup& fl) { + // no handle specified or no valid handle found, going dumb + assoc = alloc_assoc(mode_checkid_setup); + } + string trust_root; + try { + trust_root = pin.get_param("openid.trust_root"); + }catch(failed_lookup& fl) { } + string identity = pin.get_param("openid.identity"); + return_to = pin.get_param("openid.return_to"); + validate(*assoc,pin,identity,trust_root); + pout.clear(); + pout["mode"] = "id_res"; + pout["assoc_handle"] = assoc->handle(); + if(pin.has_param("openid.assoc_handle") && assoc->stateless()) + pout["invalidate_handle"] = pin.get_param("openid.assoc_handle"); + pout["identity"] = identity; + pout["return_to"] = return_to; + /* TODO: eventually remove deprecated stuff */ + time_t now = time(0); + pout["issued"] = util::time_to_w3c(now); + pout["valid_to"] = util::time_to_w3c(now+120); + pout["exipres_in"] = "120"; + pout.sign(assoc->secret(),pout["sig"],pout["signed"]="mode,identity,return_to"); + } + + void server_t::check_authentication(const params_t& pin,params_t& pout) { + vector<unsigned char> sig; + mimetic::Base64::Decoder b; + const string& sigenc = pin.get_param("openid.sig"); + mimetic::decode( + sigenc.begin(),sigenc.end(), b, + back_insert_iterator<vector<unsigned char> >(sig)); + assoc_t assoc; + try { + assoc = retrieve_assoc(pin.get_param("openid.assoc_handle")); + }catch(failed_lookup& fl) { + throw failed_assertion(OPKELE_CP_ "invalid handle or handle not specified"); + } + if(!assoc->stateless()) + throw stateful_handle(OPKELE_CP_ "will not do check_authentication on a stateful handle"); + const string& slist = pin.get_param("openid.signed"); + string kv; + 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); + kv += f; + kv += ':'; + if(f=="mode") + kv += "id_res"; + else { + f.insert(0,"openid."); + kv += pin.get_param(f); + } + kv += '\n'; + if(co==string::npos) + break; + p = co+1; + } + secret_t secret = assoc->secret(); + unsigned int md_len = 0; + unsigned char *md = HMAC( + EVP_sha1(), + &(secret.front()),secret.size(), + (const unsigned char *)kv.data(),kv.length(), + 0,&md_len); + pout.clear(); + if(sig.size()==md_len && !memcmp(&(sig.front()),md,md_len)) { + pout["is_valid"]="true"; + pout["lifetime"]="60"; /* TODO: eventually remove deprecated stuff */ + }else{ + pout["is_valid"]="false"; + pout["lifetime"]="0"; /* TODO: eventually remove deprecated stuff */ + } + if(pin.has_param("openid.invalidate_handle")) { + string h = pin.get_param("openid.invalidate_handle"); + try { + assoc_t assoc = retrieve_assoc(h); + }catch(invalid_handle& ih) { + pout["invalidate_handle"] = h; + }catch(failed_lookup& fl) { } + } + } + +} diff --git a/lib/util.cc b/lib/util.cc new file mode 100644 index 0000000..1e7335c --- a/dev/null +++ b/lib/util.cc @@ -0,0 +1,138 @@ +#include <errno.h> +#include <cassert> +#include <vector> +#include <string> +#include <mimetic/mimetic.h> +#include <curl/curl.h> +#include "opkele/util.h" +#include "opkele/exception.h" + +namespace opkele { + using namespace std; + + namespace util { + + /* + * big numerics + */ + + BIGNUM *base64_to_bignum(const string& b64) { + vector<unsigned char> bin; + mimetic::Base64::Decoder b; + mimetic::decode( + b64.begin(),b64.end(), b, + back_insert_iterator<vector<unsigned char> >(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)); + int l = BN_bn2bin(bn,&(bin.front())); + string rv; + mimetic::Base64::Encoder b(0); + mimetic::encode( + bin.begin(),bin.begin()+l, b, + back_insert_iterator<string>(rv) ); + return rv; + } + + /* + * 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 canonicalize_url(const string& url) { + string rv = url; + // strip leading and trailing spaces + string::size_type i = rv.find_first_not_of(" \t\r\n"); + if(i==string::npos) + throw bad_input(OPKELE_CP_ "empty URL"); + if(i) + rv.erase(0,i); + i = rv.find_last_not_of(" \t\r\n"); + assert(i!=string::npos); + if(i<(rv.length()-1)) + rv.erase(i+1); + // add missing http:// + i = rv.find("://"); + if(i==string::npos) { // primitive. but do we need more? + rv.insert(0,"http://"); + i = sizeof("http://")-1; + }else{ + i += sizeof("://")-1; + } + if(rv.find('/',i)==string::npos) + rv += '/'; + 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>=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; + } + + } + +} diff --git a/libopkele.pc.in b/libopkele.pc.in new file mode 100644 index 0000000..60bca34 --- a/dev/null +++ b/libopkele.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libopkele +Description: C++ implementation of OpenID protocol +Version: @VERSION@ +Requires: openssl @KONFORKA_KONFORKA@ +Cflags: -I${includedir} @LIBCURL_CPPFLAGS@ @PCREPP_CFLAGS@ +Libs: -L${libdir} -lopkele @LIBCURL@ @PCREPP_LIBS@ @MIMETIC_LIBS@ |