diff options
author | CodedOre <25259-CodedOre@users.noreply.gitlab.gnome.org> | 2022-01-13 11:36:29 +0000 |
---|---|---|
committer | CodedOre <25259-CodedOre@users.noreply.gitlab.gnome.org> | 2022-01-13 11:36:29 +0000 |
commit | 7b3285507d2d01acf293223fba275011636aeb07 (patch) | |
tree | 7c9a9cb2cca39f01274d7236339962aca7b4972b | |
parent | 7e645d051ce4b1ef44482e2f3017e43fd5ebcdaa (diff) | |
parent | 85bd00adfa6e06d3426ce7c9007e68e62f51be14 (diff) | |
download | librest-7b3285507d2d01acf293223fba275011636aeb07.tar.gz |
Fix merge conflicts.
75 files changed, 3032 insertions, 2997 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e897e44..4ec1589 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ build-librest: - dnf update -y --nogpgcheck - dnf -y install --nogpgcheck redhat-rpm-config glib2-devel gobject-introspection-devel libxml2-devel meson ninja-build - libsoup-devel vala + libsoup-devel vala json-glib-devel git python3-jinja2 python3-toml python3-typogrify python3-pygments script: - meson _build - ninja -C _build @@ -64,6 +64,7 @@ reference: libxslt libsoup-devel gtk-doc + json-glib-devel MESON_VERSION: "0.55.3" MESON_EXTRA_FLAGS: "-Dintrospection=true" DOCS_FLAGS: -Dgtk_doc=true @@ -1,2 +1,3 @@ Rob Bradford <rob@linux.intel.com> Ross Burton <ross@linux.intel.com> +Günther Wagner <info@gunibert.de> diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index e69de29..0000000 --- a/ChangeLog +++ /dev/null diff --git a/INSTALL b/INSTALL deleted file mode 100644 index d3c5b40..0000000 --- a/INSTALL +++ /dev/null @@ -1,237 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2007 Free Software Foundation, Inc. - -This file is free documentation; the Free Software Foundation gives -unlimited permission to copy, distribute and modify it. - -Basic Installation -================== - -Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - -The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package. - - 4. Type `make install' to install the programs and any data files and - documentation. - - 5. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - - 6. Often, you can also type `make uninstall' to remove the installed - files again. - -Compilers and Options -===================== - -Some systems require unusual options for compilation or linking that the -`configure' script does not know about. Run `./configure --help' for -details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - -You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - -Installation Names -================== - -By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX'. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - -Optional Features -================= - -Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - -Specifying the System Type -========================== - -There may be some features `configure' cannot figure out automatically, -but needs to determine by the type of machine the package will run on. -Usually, assuming the package is built to be run on the _same_ -architectures, `configure' can figure that out, but if it prints a -message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - -If you want to set default values for `configure' scripts to share, you -can create a site shell script called `config.site' that gives default -values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - -Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: - - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - -`configure' recognizes the following options to control how it operates. - -`--help' -`-h' - Print a summary of the options to `configure', and exit. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index e9d99aa..0000000 --- a/Makefile.am +++ /dev/null @@ -1,36 +0,0 @@ -SUBDIRS = rest rest-extras examples docs tests - -%-$(API_VERSION).pc: %.pc - $(AM_V_GEN)cp $< $@ - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = rest-$(API_VERSION).pc rest-extras-$(API_VERSION).pc - -CLEANFILES = $(pkgconfig_DATA) - -DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc - -ACLOCAL_AMFLAGS = -I build ${ACLOCAL_FLAGS} - -all-local: rest-$(API_VERSION).pc rest-extras-$(API_VERSION).pc - -if GCOV_ENABLED -cov-reset: - @rm -fr coverage - @find . -name "*.gcda" -exec rm {} \; - @lcov --directory . --zerocounters - -cov-report: - @mkdir -p coverage - @lcov --compat-libtool --directory . --capture --output-file coverage/app.info - @genhtml -o coverage/ coverage/app.info - -cov: - @make cov-report - -clean-local: - @make cov-reset - -check: - @make cov -endif @@ -0,0 +1,5 @@ +Overview of changes for 0.9 +=========================== +* new oauth2 proxy to accomplish pkce workflow with api endpoints +* introduced meson as buildsystem +* introduced the possibility to build librest with soup-2.4 or soup-3.0 diff --git a/autogen.sh b/autogen.sh deleted file mode 100755 index a345dc3..0000000 --- a/autogen.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh - -set -e - -test -n "$srcdir" || srcdir=`dirname "$0"` -test -n "$srcdir" || srcdir=. - -olddir=`pwd` -cd "$srcdir" - -GTKDOCIZE=`which gtkdocize` -if test -z $GTKDOCIZE; then - echo "*** No gtk-doc support ***" - echo "EXTRA_DIST =" > gtk-doc.make - echo "CLEANFILES =" >> gtk-doc.make -else - gtkdocize --flavour no-tmpl || exit 1 -fi - -ACLOCAL="${ACLOCAL-aclocal} $ACLOCAL_FLAGS" autoreconf -v -i - -cd "$olddir" - -test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" diff --git a/build/Makefile.am b/build/Makefile.am deleted file mode 100644 index a250674..0000000 --- a/build/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -EXTRA_DIST = introspection.m4 diff --git a/build/introspection.m4 b/build/introspection.m4 deleted file mode 100644 index 589721c..0000000 --- a/build/introspection.m4 +++ /dev/null @@ -1,94 +0,0 @@ -dnl -*- mode: autoconf -*- -dnl Copyright 2009 Johan Dahlin -dnl -dnl This file is free software; the author(s) gives unlimited -dnl permission to copy and/or distribute it, with or without -dnl modifications, as long as this notice is preserved. -dnl - -# serial 1 - -m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL], -[ - AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first - AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first - AC_BEFORE([LT_INIT],[$0])dnl setup libtool first - - dnl enable/disable introspection - m4_if([$2], [require], - [dnl - enable_introspection=yes - ],[dnl - AC_ARG_ENABLE(introspection, - AS_HELP_STRING([--enable-introspection[=@<:@no/auto/yes@:>@]], - [Enable introspection for this build]),, - [enable_introspection=auto]) - ])dnl - - AC_MSG_CHECKING([for gobject-introspection]) - - dnl presence/version checking - AS_CASE([$enable_introspection], - [no], [dnl - found_introspection="no (disabled, use --enable-introspection to enable)" - ],dnl - [yes],[dnl - PKG_CHECK_EXISTS([gobject-introspection-1.0],, - AC_MSG_ERROR([gobject-introspection-1.0 is not installed])) - PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], - found_introspection=yes, - AC_MSG_ERROR([You need to have gobject-introspection >= $1 installed to build AC_PACKAGE_NAME])) - ],dnl - [auto],[dnl - PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, found_introspection=no) - ],dnl - [dnl - AC_MSG_ERROR([invalid argument passed to --enable-introspection, should be one of @<:@no/auto/yes@:>@]) - ])dnl - - AC_MSG_RESULT([$found_introspection]) - - INTROSPECTION_SCANNER= - INTROSPECTION_COMPILER= - INTROSPECTION_GENERATE= - INTROSPECTION_GIRDIR= - INTROSPECTION_TYPELIBDIR= - if test "x$found_introspection" = "xyes"; then - INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` - INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0` - INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0` - INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0` - INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)" - INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0` - INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0` - INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection - fi - AC_SUBST(INTROSPECTION_SCANNER) - AC_SUBST(INTROSPECTION_COMPILER) - AC_SUBST(INTROSPECTION_GENERATE) - AC_SUBST(INTROSPECTION_GIRDIR) - AC_SUBST(INTROSPECTION_TYPELIBDIR) - AC_SUBST(INTROSPECTION_CFLAGS) - AC_SUBST(INTROSPECTION_LIBS) - AC_SUBST(INTROSPECTION_MAKEFILE) - - AM_CONDITIONAL(HAVE_INTROSPECTION, test "x$found_introspection" = "xyes") -]) - - -dnl Usage: -dnl GOBJECT_INTROSPECTION_CHECK([minimum-g-i-version]) - -AC_DEFUN([GOBJECT_INTROSPECTION_CHECK], -[ - _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1]) -]) - -dnl Usage: -dnl GOBJECT_INTROSPECTION_REQUIRE([minimum-g-i-version]) - - -AC_DEFUN([GOBJECT_INTROSPECTION_REQUIRE], -[ - _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1], [require]) -]) diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 75c02fe..0000000 --- a/configure.ac +++ /dev/null @@ -1,161 +0,0 @@ -m4_define([rest_major], [0]) -m4_define([rest_minor], [9]) -m4_define([rest_micro], [0]) - -m4_define([rest_version], [rest_major.rest_minor.rest_micro]) - - -AC_PREREQ([2.63]) -AC_INIT([rest], [rest_version], - [], - [rest], - [https://gitlab.gnome.org/GNOME/librest/]) - -AC_CONFIG_SRCDIR([rest/rest-proxy.h]) -AC_CONFIG_HEADERS([config.h]) -AC_CONFIG_AUX_DIR([build]) -AC_CONFIG_MACRO_DIR([build]) - -AM_INIT_AUTOMAKE([1.11 foreign -Wno-portability no-define dist-xz]) - -AM_SILENT_RULES([yes]) - -AC_CANONICAL_HOST - -AC_PROG_CC -AC_PROG_CC_STDC -AC_PROG_INSTALL -AC_ISC_POSIX -AC_HEADER_STDC - -AM_PROG_CC_C_O - -# require libtool >= 2.2 -LT_PREREQ([2.2.6]) -LT_INIT([disable-static]) - -PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.67.4) -PKG_CHECK_MODULES(XML, libxml-2.0) -PKG_CHECK_MODULES(GTHREAD, gthread-2.0) - -# gtkdocize greps for ^GTK_DOC_CHECK and parses it, so you need to have -# it on it's own line. -m4_ifdef([GTK_DOC_CHECK], [ -GTK_DOC_CHECK([1.13], [--flavour no-tmpl]) -]) - -GOBJECT_INTROSPECTION_CHECK([0.6.7]) - -AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums]) - -localedir=${datadir}/locale -AC_SUBST(localedir) - -AC_MSG_CHECKING([for libsoup version to use]) -AC_ARG_WITH(soup, - [AC_HELP_STRING([--soup=2|3], - [version of libsoup library to use (default: 2)])], - [case "$withval" in - 2|3) ;; - *) AC_MSG_ERROR([invalid argument "$withval" for --with-soup]) ;; - esac], - [with_soup=2]) -AC_MSG_RESULT([$with_soup]) - -API_MAJOR=1 - -if test "$with_soup" = "2"; then - PKG_CHECK_MODULES(SOUP, libsoup-2.4 >= 2.42) - SOUP_API_VERSION=2.4 - API_MINOR=0 - AC_DEFINE([WITH_SOUP_2],[1],[Define if libsoup version used is 2]) -else - PKG_CHECK_MODULES(SOUP, libsoup-3.0 >= 2.99.8) - SOUP_API_VERSION=3.0 - API_MINOR=1 -fi - -AM_CONDITIONAL([WITH_SOUP_2],[test "$with_soup" = "2"]) -AC_SUBST(SOUP_API_VERSION) - -AC_SUBST([API_VERSION],[$API_MAJOR.$API_MINOR]) -AC_SUBST([API_VERSION_AM],[$API_MAJOR\_$API_MINOR]) -AC_DEFINE_UNQUOTED(API_VERSION, [$API_VERSION], [API version]) - -dnl === Coverage report ======================================================= -AC_PATH_PROG([GCOV], [lcov], [enable_gcov=no]) - -AC_MSG_CHECKING([whether to build with gcov testing]) - -AC_ARG_ENABLE([gcov], - [AS_HELP_STRING([--enable-gcov], - [Whether to enable coverage testing (requires gcc -and lcov)])], - [], - [enable_gcov=no]) - -AS_IF([test "x$enable_gcov" = "xyes" && test "x$GCC" = "xyes"], - [ - AS_IF([test "x$enable_gtk_doc" = "xyes"], - [AC_MSG_WARN([gtk-doc is enabled, this may go horribly wrong])], - [AC_MSG_RESULT([yes])] - ) - - GCOV_CFLAGS="-g -O0 -fprofile-arcs -ftest-coverage" - GCOV_LDFLAGS="-lgcov" - ], - [AC_MSG_RESULT([no])] -) - -AM_CONDITIONAL([GCOV_ENABLED], [test "x$enable_gcov" = "xyes"]) -AC_SUBST([GCOV_CFLAGS]) -AC_SUBST([GCOV_LDFLAGS]) - -dnl Stolen from glib-networking - those guys rock -AC_MSG_CHECKING([location of system Certificate Authority list]) -AC_ARG_WITH(ca-certificates, - [AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@], - [path to system Certificate Authority list])]) -if test "$with_ca_certificates" = "no"; then - AC_MSG_RESULT([disabled]) -else - if test -z "$with_ca_certificates"; then - for f in /etc/pki/tls/certs/ca-bundle.crt \ - /etc/ssl/certs/ca-certificates.crt \ - /etc/ssl/ca-bundle.pem; do - if test -f "$f"; then - with_ca_certificates="$f" - fi - done - if test -z "$with_ca_certificates"; then - AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable]) - fi - fi - - AC_MSG_RESULT($with_ca_certificates) - AC_DEFINE_UNQUOTED(REST_SYSTEM_CA_FILE, ["$with_ca_certificates"], [The system TLS CA list]) -fi - -AC_OUTPUT([ - Makefile - rest/Makefile - rest-extras/Makefile - examples/Makefile - docs/Makefile - docs/reference/Makefile - docs/reference/rest/Makefile - tests/Makefile - rest.pc - rest-extras.pc -]) - -echo "" -echo " LibRest $VERSION" -echo " ================" -echo "" -echo " prefix: ${prefix}" -echo " libsoup version: ${with_soup}" -echo "" -echo " Documentation: ${enable_gtk_doc}" -echo " Introspection data: ${enable_introspection}" -echo "" diff --git a/docs/Makefile.am b/docs/Makefile.am deleted file mode 100644 index f3ddc22..0000000 --- a/docs/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -SUBDIRS = reference diff --git a/docs/librest.toml.in b/docs/librest.toml.in index 4f4c59d..1f95b0b 100644 --- a/docs/librest.toml.in +++ b/docs/librest.toml.in @@ -6,7 +6,7 @@ repository_url = "https://gitlab.gnome.org/GNOME/librest.git" authors = "Günther Wagner" license = "LGPL-2.1-or-later" description = "REST client library" -dependencies = [ "GObject-2.0", "Gio-2.0", "Soup-2.4" ] +dependencies = [ "GObject-2.0", "Gio-2.0", "Soup-@SOUP_VERSION@" ] devhelp = true search_index = true @@ -20,10 +20,10 @@ search_index = true description = "GObject interfaces and objects" docs_url = "https://developer.gnome.org/gio/stable" - [dependencies."Soup-2.4"] + [dependencies."Soup-@SOUP_VERSION@"] name = "Soup" description = "HTTP library" - docs_url = "https://developer.gnome.org/libsoup/stable" + docs_url = "https://libsoup.org/libsoup-@SOUP_VERSION@/index.html" [theme] name = "basic" diff --git a/docs/meson.build b/docs/meson.build index 2eeb084..f2aeb76 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -1,5 +1,3 @@ -subdir('reference/rest') - if get_option('gtk_doc') and get_option('introspection') dependency('gi-docgen', version: '>= 2021.6', fallback: ['gi-docgen', 'dummy_dep'], @@ -11,6 +9,7 @@ if get_option('gtk_doc') and get_option('introspection') toml_conf = configuration_data() toml_conf.set('REST_VERSION', meson.project_version()) + toml_conf.set('SOUP_VERSION', libsoup_api_version) rest_toml = configure_file( input: 'librest.toml.in', @@ -39,5 +38,19 @@ if get_option('gtk_doc') and get_option('introspection') install_dir: rest_docdir, ) + # if get_option('tests') + # test('doc-check', + # gidocgen, + # args: [ + # 'check', + # '--config', rest_toml, + # '--add-include-path=@0@'.format(meson.current_build_dir() / '../rest'), + # librest_gir[0], + # ], + # depends: librest_gir[0], + # suite: ['docs'], + # ) + # endif + endif diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am deleted file mode 100644 index b7e99c2..0000000 --- a/docs/reference/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -SUBDIRS = rest diff --git a/docs/reference/rest/Makefile.am b/docs/reference/rest/Makefile.am deleted file mode 100644 index a4c690e..0000000 --- a/docs/reference/rest/Makefile.am +++ /dev/null @@ -1,92 +0,0 @@ -## Process this file with automake to produce Makefile.in - -# We require automake 1.6 at least. -AUTOMAKE_OPTIONS = 1.6 - -# This is a blank Makefile.am for using gtk-doc. -# Copy this to your project's API docs directory and modify the variables to -# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples -# of using the various options. - -# The name of the module, e.g. 'glib'. -DOC_MODULE=rest -DOC_MODULE_VERSION=@API_VERSION@ - -# The top-level SGML file. You can change this if you want to. -DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml - -# The directory containing the source code. Relative to $(srcdir). -# gtk-doc will search all .c & .h files beneath here for inline comments -# documenting the functions and macros. -# e.g. DOC_SOURCE_DIR=../../../gtk -DOC_SOURCE_DIR=../../../rest ../../../rest-extras - -# Extra options to pass to gtkdoc-scangobj. Not normally needed. -SCANGOBJ_OPTIONS= - -# Extra options to supply to gtkdoc-scan. -# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" -SCAN_OPTIONS= - -# Extra options to supply to gtkdoc-mkdb. -# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml -MKDB_OPTIONS=--sgml-mode --output-format=xml - -# Extra options to supply to gtkdoc-mktmpl -# e.g. MKTMPL_OPTIONS=--only-section-tmpl -MKTMPL_OPTIONS= - -# Extra options to supply to gtkdoc-mkhtml -MKHTML_OPTIONS= - -# Extra options to supply to gtkdoc-fixref. Not normally needed. -# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html -FIXXREF_OPTIONS= - -# Used for dependencies. The docs will be rebuilt if any of these change. -# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h -# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c -HFILE_GLOB=$(top_srcdir)/rest/*.h $(top_srcdir)/rest-extras/*.h -CFILE_GLOB=$(top_srcdir)/rest/*.c $(top_srcdir)/rest-extras/*.c - -# Header files to ignore when scanning. -# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h -IGNORE_HFILES=sha1.h rest-private.h rest-proxy-call-private.h oauth-proxy-private.h oauth2-proxy-private.h flickr-proxy-private.h lastfm-proxy-private.h - -# Images to copy into HTML directory. -# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png -HTML_IMAGES= - -# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). -# e.g. content_files=running.sgml building.sgml changes-2.0.sgml -content_files= - -# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded -# These files must be listed here *and* in content_files -# e.g. expand_content_files=running.sgml -expand_content_files= - -# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. -# Only needed if you are using gtkdoc-scangobj to dynamically query widget -# signals and properties. -# e.g. INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) -# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) -AM_CPPFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GLIB_CFLAGS) $(SOUP_CFLAGS) $(XML_CFLAGS) -GTKDOC_LIBS=$(top_builddir)/rest/librest-@API_VERSION@.la $(top_builddir)/rest-extras/librest-extras-@API_VERSION@.la \ - $(GLIB_LIBS) $(SOUP_LIBS) $(XML_LIBS) - - -# This includes the standard gtk-doc make rules, copied by gtkdocize. -include $(top_srcdir)/gtk-doc.make - -# Other files to distribute -# e.g. EXTRA_DIST += version.xml.in -EXTRA_DIST += - -# Files not to distribute -# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types -# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt -#DISTCLEANFILES += - -# Comment this out if you want your docs-status tested during 'make check' -#TESTS = $(GTKDOC_CHECK) diff --git a/docs/reference/rest/meson.build b/docs/reference/rest/meson.build deleted file mode 100644 index d02adf3..0000000 --- a/docs/reference/rest/meson.build +++ /dev/null @@ -1,26 +0,0 @@ -gnome.gtkdoc('rest', - src_dir: [ - include_directories('../../../rest'), - include_directories('../../../rest-extras'), - ], - main_xml: 'rest-docs.xml', - module_version: librest_api_version, - dependencies: [ - librest_deps, - librest_dep, - librest_extras_dep, - librest_extras_deps, - ], - scan_args: [ - '--rebuild-types', - ], - ignore_headers: [ - 'flickr-proxy-private.h', - 'lastfm-proxy-private.h', - 'oauth-proxy-private.h', - 'oauth2-proxy-private.h', - 'rest-private.h', - 'rest-proxy-call-private.h', - 'sha1.h', - ], -) diff --git a/docs/reference/rest/rest-docs.xml b/docs/reference/rest/rest-docs.xml deleted file mode 100644 index 16f8d87..0000000 --- a/docs/reference/rest/rest-docs.xml +++ /dev/null @@ -1,51 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" - "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"> -<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude"> - <bookinfo> - <title>librest Reference Manual</title> - </bookinfo> - - <chapter> - <title>Generic Proxies</title> - <xi:include href="xml/rest-param.xml"/> - <xi:include href="xml/rest-params.xml"/> - <xi:include href="xml/rest-proxy.xml"/> - <xi:include href="xml/rest-proxy-call.xml"/> - </chapter> - - <chapter> - <title>OAuth Proxies</title> - <xi:include href="xml/oauth-proxy.xml"/> - <xi:include href="xml/oauth-proxy-call.xml"/> - <xi:include href="xml/oauth2-proxy.xml"/> - <xi:include href="xml/oauth2-proxy-call.xml"/> - </chapter> - - <chapter> - <title>Service-specific Proxies</title> - <xi:include href="xml/flickr-proxy.xml"/> - <xi:include href="xml/flickr-proxy-call.xml"/> - <xi:include href="xml/lastfm-proxy.xml"/> - <xi:include href="xml/lastfm-proxy-call.xml"/> - </chapter> - <chapter> - <title>XML Parsing</title> - <xi:include href="xml/rest-xml-parser.xml"/> - </chapter> - - <chapter> - <title>Glossaries</title> - <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> - </chapter> - - <index> - <title>Index of all symbols</title> - <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include> - </index> - - <index role="deprecated"> - <title>Index of deprecated symbols</title> - <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include> - </index> -</book> diff --git a/docs/reference/rest/rest-overrides.txt b/docs/reference/rest/rest-overrides.txt deleted file mode 100644 index e69de29..0000000 --- a/docs/reference/rest/rest-overrides.txt +++ /dev/null diff --git a/docs/reference/rest/rest-sections.txt b/docs/reference/rest/rest-sections.txt deleted file mode 100644 index ef4b3e3..0000000 --- a/docs/reference/rest/rest-sections.txt +++ /dev/null @@ -1,303 +0,0 @@ -<SECTION> -<FILE>rest-xml-parser</FILE> -<TITLE>RestXmlParser</TITLE> -RestXmlParser -RestXmlNode -rest_xml_parser_new -rest_xml_parser_parse_from_data -rest_xml_node_ref -rest_xml_node_unref -rest_xml_node_get_attr -rest_xml_node_find -<SUBSECTION Standard> -REST_XML_PARSER -REST_IS_XML_PARSER -REST_TYPE_XML_PARSER -rest_xml_parser_get_type -REST_XML_PARSER_CLASS -REST_IS_XML_PARSER_CLASS -REST_XML_PARSER_GET_CLASS -REST_TYPE_XML_NODE -rest_xml_node_get_type -</SECTION> - -<SECTION> -<FILE>rest-proxy-call</FILE> -<TITLE>RestProxyCall</TITLE> -RestProxyCall -REST_PROXY_CALL_ERROR -RestProxyCallError -rest_proxy_call_set_method -rest_proxy_call_get_method -rest_proxy_call_set_function -rest_proxy_call_get_function -rest_proxy_call_add_header -rest_proxy_call_add_headers -rest_proxy_call_add_headers_from_valist -rest_proxy_call_lookup_header -rest_proxy_call_remove_header -rest_proxy_call_add_param -rest_proxy_call_add_param_full -rest_proxy_call_add_params -rest_proxy_call_add_params_from_valist -rest_proxy_call_lookup_param -rest_proxy_call_remove_param -rest_proxy_call_get_params -rest_proxy_call_run -RestProxyCallAsyncCallback -RestProxyCallUploadCallback -rest_proxy_call_async -rest_proxy_call_cancel -rest_proxy_call_sync -rest_proxy_call_upload -rest_proxy_call_invoke_async -rest_proxy_call_lookup_response_header -rest_proxy_call_get_response_headers -rest_proxy_call_get_payload_length -rest_proxy_call_get_payload -rest_proxy_call_get_status_code -rest_proxy_call_get_status_message -<SUBSECTION Standard> -RestProxyCallPrivate -REST_PROXY_CALL -REST_IS_PROXY_CALL -REST_TYPE_PROXY_CALL -rest_proxy_call_get_type -REST_PROXY_CALL_CLASS -REST_IS_PROXY_CALL_CLASS -REST_PROXY_CALL_GET_CLASS -rest_proxy_call_error_quark -</SECTION> - -<SECTION> -<FILE>oauth2-proxy</FILE> -<TITLE>OAuth2Proxy</TITLE> -OAuth2Proxy -oauth2_proxy_new -oauth2_proxy_new_with_token -oauth2_proxy_build_login_url_full -oauth2_proxy_build_login_url -oauth2_proxy_get_access_token -oauth2_proxy_set_access_token -oauth2_proxy_extract_access_token -<SUBSECTION Standard> -OAuth2ProxyClass -OAuth2ProxyPrivate -OAUTH2_PROXY -OAUTH2_IS_PROXY -OAUTH2_TYPE_PROXY -oauth2_proxy_get_type -OAUTH2_PROXY_CLASS -OAUTH2_IS_PROXY_CLASS -OAUTH2_PROXY_GET_CLASS -</SECTION> - -<SECTION> -<FILE>oauth2-proxy-call</FILE> -<TITLE>OAuth2ProxyCall</TITLE> -OAuth2ProxyCall -<SUBSECTION Standard> -OAuth2ProxyCallClass -OAUTH2_PROXY_CALL -OAUTH2_IS_PROXY_CALL -OAUTH2_TYPE_PROXY_CALL -oauth2_proxy_call_get_type -OAUTH2_PROXY_CALL_CLASS -OAUTH2_IS_PROXY_CALL_CLASS -OAUTH2_PROXY_CALL_GET_CLASS -</SECTION> - -<SECTION> -<FILE>rest-proxy</FILE> -<TITLE>RestProxy</TITLE> -RestProxy -RestProxyClass -REST_PROXY_ERROR -RestProxyError -rest_proxy_new -rest_proxy_bind -rest_proxy_bind_valist -rest_proxy_set_user_agent -rest_proxy_get_user_agent -rest_proxy_add_soup_feature -rest_proxy_new_call -rest_proxy_simple_run -rest_proxy_simple_run_valist -<SUBSECTION Standard> -REST_PROXY -REST_IS_PROXY -REST_TYPE_PROXY -rest_proxy_get_type -REST_PROXY_CLASS -REST_IS_PROXY_CLASS -REST_PROXY_GET_CLASS -rest_proxy_error_quark -</SECTION> - -<SECTION> -<FILE>oauth-proxy-call</FILE> -<TITLE>OAuthProxyCall</TITLE> -OAuthProxyCall -oauth_proxy_call_parse_token_response -<SUBSECTION Standard> -OAuthProxyCallClass -OAUTH_PROXY_CALL -OAUTH_IS_PROXY_CALL -OAUTH_TYPE_PROXY_CALL -oauth_proxy_call_get_type -OAUTH_PROXY_CALL_CLASS -OAUTH_IS_PROXY_CALL_CLASS -OAUTH_PROXY_CALL_GET_CLASS -</SECTION> - -<SECTION> -<FILE>oauth-proxy</FILE> -<TITLE>OAuthProxy</TITLE> -OAuthProxy -OAuthSignatureMethod -oauth_proxy_new -oauth_proxy_new_echo_proxy -oauth_proxy_new_with_token -OAuthProxyAuthCallback -oauth_proxy_auth_step -oauth_proxy_auth_step_async -oauth_proxy_request_token -oauth_proxy_request_token_async -oauth_proxy_is_oauth10a -oauth_proxy_access_token -oauth_proxy_access_token_async -oauth_proxy_get_token -oauth_proxy_set_token -oauth_proxy_get_token_secret -oauth_proxy_set_token_secret -oauth_proxy_get_signature_host -oauth_proxy_set_signature_host -<SUBSECTION Standard> -OAuthProxyPrivate -OAuthProxyClass -OAUTH_PROXY -OAUTH_IS_PROXY -OAUTH_TYPE_PROXY -oauth_proxy_get_type -OAUTH_PROXY_CLASS -OAUTH_IS_PROXY_CLASS -OAUTH_PROXY_GET_CLASS -</SECTION> - -<SECTION> -<FILE>rest-params</FILE> -<TITLE>RestParams</TITLE> -RestParams -RestParamsIter -rest_params_new -rest_params_free -rest_params_add -rest_params_get -rest_params_remove -rest_params_are_strings -rest_params_as_string_hash_table -rest_params_iter_init -rest_params_iter_next -</SECTION> - -<SECTION> -<FILE>rest-param</FILE> -<TITLE>RestParam</TITLE> -RestMemoryUse -RestParam -rest_param_new_string -rest_param_new_full -rest_param_new_with_owner -rest_param_is_string -rest_param_get_name -rest_param_get_content_type -rest_param_get_file_name -rest_param_get_content -rest_param_get_content_length -rest_param_ref -rest_param_unref -</SECTION> - -<SECTION> -<FILE>flickr-proxy-call</FILE> -<TITLE>FlickrProxyCall</TITLE> -FlickrProxyCall -<SUBSECTION Standard> -FlickrProxyCallClass -FLICKR_PROXY_CALL -FLICKR_IS_PROXY_CALL -FLICKR_TYPE_PROXY_CALL -flickr_proxy_call_get_type -FLICKR_PROXY_CALL_CLASS -FLICKR_IS_PROXY_CALL_CLASS -FLICKR_PROXY_CALL_GET_CLASS -</SECTION> - -<SECTION> -<FILE>flickr-proxy</FILE> -<TITLE>FlickrProxy</TITLE> -FlickrProxy -FLICKR_PROXY_ERROR -flickr_proxy_new -flickr_proxy_new_with_token -flickr_proxy_get_api_key -flickr_proxy_get_shared_secret -flickr_proxy_get_token -flickr_proxy_set_token -flickr_proxy_sign -flickr_proxy_build_login_url -flickr_proxy_is_successful -flickr_proxy_new_upload -flickr_proxy_new_upload_for_file -<SUBSECTION Standard> -FlickrProxyClass -FlickrProxyPrivate -FLICKR_PROXY -FLICKR_IS_PROXY -FLICKR_TYPE_PROXY -flickr_proxy_get_type -FLICKR_PROXY_CLASS -FLICKR_IS_PROXY_CLASS -FLICKR_PROXY_GET_CLASS -</SECTION> - -<SECTION> -<FILE>lastfm-proxy-call</FILE> -<TITLE>LastfmProxyCall</TITLE> -LastfmProxyCall -<SUBSECTION Standard> -LastfmProxyCallClass -LASTFM_PROXY_CALL -LASTFM_IS_PROXY_CALL -LASTFM_TYPE_PROXY_CALL -lastfm_proxy_call_get_type -LASTFM_PROXY_CALL_CLASS -LASTFM_IS_PROXY_CALL_CLASS -LASTFM_PROXY_CALL_GET_CLASS -</SECTION> - -<SECTION> -<FILE>lastfm-proxy</FILE> -<TITLE>LastfmProxy</TITLE> -LastfmProxy -LASTFM_PROXY_ERROR -lastfm_proxy_new -lastfm_proxy_new_with_session -lastfm_proxy_get_api_key -lastfm_proxy_get_secret -lastfm_proxy_get_session_key -lastfm_proxy_set_session_key -lastfm_proxy_sign -lastfm_proxy_build_login_url -lastfm_proxy_is_successful -<SUBSECTION Standard> -LastfmProxyClass -LastfmProxyPrivate -LASTFM_PROXY -LASTFM_IS_PROXY -LASTFM_TYPE_PROXY -lastfm_proxy_get_type -LASTFM_PROXY_CLASS -LASTFM_IS_PROXY_CLASS -LASTFM_PROXY_GET_CLASS -</SECTION> diff --git a/docs/reference/rest/rest.types b/docs/reference/rest/rest.types deleted file mode 100644 index 97a392c..0000000 --- a/docs/reference/rest/rest.types +++ /dev/null @@ -1,12 +0,0 @@ -flickr_proxy_call_get_type -flickr_proxy_get_type -lastfm_proxy_call_get_type -lastfm_proxy_get_type -oauth2_proxy_call_get_type -oauth2_proxy_get_type -oauth_proxy_call_get_type -oauth_proxy_get_type -rest_proxy_call_get_type -rest_proxy_get_type -rest_xml_node_get_type -rest_xml_parser_get_type diff --git a/examples/Makefile.am b/examples/Makefile.am deleted file mode 100644 index 4f68e7c..0000000 --- a/examples/Makefile.am +++ /dev/null @@ -1,13 +0,0 @@ -noinst_PROGRAMS = test-raw test-xml dump-xml post-twitter post-twitter-media get-flickr-favorites lastfm-shout continuous-twitter - -AM_CFLAGS = $(GLIB_CFLAGS) $(GTHREAD_CFLAGS) $(SOUP_CFLAGS) -I$(top_srcdir) -AM_LDFLAGS = $(GLIB_LIBS) $(GTHREAD_LIBS) $(SOUP_LIBS) ../rest/librest-@API_VERSION@.la ../rest-extras/librest-extras-@API_VERSION@.la - -test_raw_SOURCES = test-raw.c -test_xml_SOURCES = test-xml.c -dump_xml_SOURCES = dump-xml.c -post_twitter_SOURCES = post-twitter.c -post_twitter_media_SOURCES = post-twitter-media.c -get_flickr_favorites_SOURCES = get-flickr-favorites.c -lastfm_shout_SOURCES = lastfm-shout.c -continuous_twitter_SOURCES = continuous-twitter.c diff --git a/examples/gitlab-oauth2-example.c b/examples/gitlab-oauth2-example.c new file mode 100644 index 0000000..f18cbab --- /dev/null +++ b/examples/gitlab-oauth2-example.c @@ -0,0 +1,148 @@ +/* gitlab-oauth2-example.c + * + * Copyright 2021 Günther Wagner <info@gunibert.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * This example shows the recommended PKCE authorization flow for Gitlab. + */ + +#include <glib.h> +#include <rest/rest.h> +#include <stdio.h> +#include <libsoup/soup.h> + +GMainLoop *loop; + +static void +load_projects (RestProxy *proxy) +{ + g_autoptr(GError) error = NULL; + RestProxyCall *call; + + call = rest_proxy_new_call (proxy); + rest_proxy_call_set_method (call, "GET"); + rest_proxy_call_set_function (call, "projects/426/issues"); + rest_proxy_call_sync (call, &error); + + g_print ("%s\n", rest_proxy_call_get_payload (call)); + g_main_loop_quit (loop); +} + +static void +gitlab_oauth2_example_refreshed_access_token (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + RestOAuth2Proxy *oauth2_proxy = (RestOAuth2Proxy *)object; + g_autoptr(GError) error = NULL; + + g_assert (G_IS_OBJECT (object)); + g_assert (G_IS_ASYNC_RESULT (result)); + + rest_oauth2_proxy_refresh_access_token_finish (oauth2_proxy, result, &error); + if (error) + g_error ("%s", error->message); + + g_print ("Access Token: %s\n", rest_oauth2_proxy_get_access_token (oauth2_proxy)); + g_print ("Refresh Token: %s\n", rest_oauth2_proxy_get_refresh_token (oauth2_proxy)); + GDateTime *expiration_date = rest_oauth2_proxy_get_expiration_date (oauth2_proxy); + if (expiration_date) + g_print ("Expires in: %s\n", g_date_time_format (expiration_date, "%X %x")); + + load_projects (REST_PROXY (oauth2_proxy)); +} + +static void +gitlab_oauth2_example_fetched_access_token (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + RestOAuth2Proxy *oauth2_proxy = (RestOAuth2Proxy *)object; + g_autoptr(GError) error = NULL; + + g_assert (G_IS_OBJECT (object)); + g_assert (G_IS_ASYNC_RESULT (result)); + + rest_oauth2_proxy_fetch_access_token_finish (oauth2_proxy, result, &error); + if (error) + g_error ("%s", error->message); + + g_print ("Access Token: %s\n", rest_oauth2_proxy_get_access_token (oauth2_proxy)); + g_print ("Refresh Token: %s\n", rest_oauth2_proxy_get_refresh_token (oauth2_proxy)); + GDateTime *expiration_date = rest_oauth2_proxy_get_expiration_date (oauth2_proxy); + if (expiration_date) + g_print ("Expires in: %s\n", g_date_time_format (expiration_date, "%X %x")); + + /* refresh token */ + rest_oauth2_proxy_refresh_access_token_async (oauth2_proxy, NULL, gitlab_oauth2_example_refreshed_access_token, user_data); +} + +gint +main (gint argc, + gchar *argv[]) +{ + g_autofree gchar *line = NULL; + size_t len = 0; + + gchar **environment = g_get_environ (); + gchar *authurl = "https://gitlab.gnome.org/oauth/authorize"; + gchar *tokenurl = "https://gitlab.gnome.org/oauth/token"; + gchar *redirecturl = "http://example.com"; + gchar *baseurl = "https://gitlab.gnome.org/api/v4/"; + const gchar *clientid = g_environ_getenv (environment, "REST_OAUTH2_CLIENT_ID"); + if (!clientid) + { + g_print ("You have to define your Gitlab Client ID as REST_OAUTH2_CLIENT_ID environment variable\n"); + return EXIT_SUCCESS; + } + + const gchar *clientsecret = g_environ_getenv (environment, "REST_OAUTH2_CLIENT_SECRET"); + if (!clientsecret) + { + g_print ("You have to define your Gitlab Client Secret as REST_OAUTH2_CLIENT_SECRET environment variable\n"); + return EXIT_SUCCESS; + } + RestPkceCodeChallenge *pkce = rest_pkce_code_challenge_new_random (); + gchar *state = NULL; + +#ifdef WITH_SOUP_2 + SoupLogger *logger = soup_logger_new (SOUP_LOGGER_LOG_HEADERS, -1); +#else + SoupLogger *logger = soup_logger_new (SOUP_LOGGER_LOG_HEADERS); +#endif + + RestOAuth2Proxy *oauth2_proxy = rest_oauth2_proxy_new (authurl, tokenurl, redirecturl, clientid, clientsecret, baseurl); + rest_proxy_add_soup_feature (REST_PROXY (oauth2_proxy), SOUP_SESSION_FEATURE (logger)); + const gchar *authorize_url = rest_oauth2_proxy_build_authorization_url (oauth2_proxy, rest_pkce_code_challenge_get_challenge (pkce), NULL, &state); + + g_print ("URL to authorize: %s\n", authorize_url); + + ssize_t chars = getline (&line, &len, stdin); + if (line[chars - 1] == '\n') { + line[chars - 1] = '\0'; + } + + g_print ("Got Authorization Grant: %s\n", line); + + /* fetch access token */ + rest_oauth2_proxy_fetch_access_token_async (oauth2_proxy, line, rest_pkce_code_challenge_get_verifier (pkce), NULL, gitlab_oauth2_example_fetched_access_token, NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + return 0; +} diff --git a/examples/meson.build b/examples/meson.build index 7d16dc7..375a72a 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -7,6 +7,7 @@ example_names = [ 'get-flickr-favorites', 'lastfm-shout', 'continuous-twitter', + 'gitlab-oauth2-example', ] example_deps = [ diff --git a/meson.build b/meson.build index 81c5ed1..4fd3195 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('rest', 'c', version: '0.9.0', license: 'LGPL2.1+', - meson_version: '>= 0.49', + meson_version: '>= 0.53', ) # Versioning @@ -57,6 +57,7 @@ endif glib_dep = dependency('glib-2.0', version: '>= 2.44') gobject_dep = dependency('gobject-2.0', version: '>= 2.44') libsoup_dep = dependency(libsoup_name, version: libsoup_req_version) +libjson_glib_dep = dependency('json-glib-1.0') libxml_dep = dependency('libxml-2.0') # config.h @@ -73,7 +74,9 @@ config_h_inc = include_directories('.') # Subdirectories subdir('rest') subdir('rest-extras') -subdir('tests') +if get_option('tests') + subdir('tests') +endif if get_option('examples') subdir('examples') endif @@ -87,7 +90,7 @@ pkgconfig.generate(librest_lib, filebase: librest_pkg_string, description: 'RESTful web api query library', subdirs: librest_pkg_string, - requires: [ glib_dep, libsoup_dep, libxml_dep, ], + requires: [ glib_dep, libsoup_dep, libxml_dep, libjson_glib_dep ], variables: [ 'apiversion=@0@'.format(librest_api_version), ], @@ -98,8 +101,25 @@ pkgconfig.generate(librest_extras_lib, filebase: librest_extras_pkg_string, description: 'RESTful web api query library', subdirs: librest_pkg_string, - requires: [ glib_dep, libsoup_dep, libxml_dep, ], + requires: [ glib_dep, libsoup_dep, libxml_dep], variables: [ 'apiversion=@0@'.format(librest_api_version), ], ) + +summary({ + 'prefix': get_option('prefix'), + 'libdir': get_option('prefix') / get_option('libdir'), + }, + section: 'Directories', +) + +summary({ + 'Introspection': get_option('introspection'), + 'Documentation': get_option('gtk_doc'), + 'Tests': get_option('tests'), + 'Soup 2': get_option('soup2'), + }, + section: 'Build', + bool_yn: true, +) diff --git a/meson_options.txt b/meson_options.txt index 53e3b8a..f81f738 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -25,7 +25,7 @@ option('examples', ) option('gtk_doc', type: 'boolean', - value: false, + value: true, description: 'Build the gtk-doc reference docs', ) option('soup2', @@ -33,3 +33,8 @@ option('soup2', value: true, description: 'Whether to build with libsoup2', ) +option('tests', + type: 'boolean', + value: true, + description: 'Whether to build the tests', +) diff --git a/rest-extras/Makefile.am b/rest-extras/Makefile.am deleted file mode 100644 index c7dd606..0000000 --- a/rest-extras/Makefile.am +++ /dev/null @@ -1,79 +0,0 @@ -CLEANFILES = - -lib_sources = \ - flickr-proxy.c \ - flickr-proxy-call.c \ - flickr-proxy-private.h \ - lastfm-proxy.c \ - lastfm-proxy-call.c \ - lastfm-proxy-private.h \ - youtube-proxy.c \ - youtube-proxy-private.h -lib_headers = \ - flickr-proxy.h \ - flickr-proxy-call.h \ - lastfm-proxy.h \ - lastfm-proxy-call.h \ - youtube-proxy.h - - -lib_LTLIBRARIES = librest-extras-@API_VERSION@.la -librest_extras_@API_VERSION@_la_CFLAGS = $(GLIB_CFLAGS) $(GTHREAD_CFLAGS) \ - $(SOUP_CFLAGS) $(SOUP_GNOME_CFLAGS) \ - $(XML_CFLAGS) $(GCOV_CFLAGS) \ - -I$(top_srcdir) -Wall -DG_LOG_DOMAIN=\"Rest\" -librest_extras_@API_VERSION@_la_LIBADD = $(GLIB_LIBS) $(GTHREAD_LIBS) \ - $(SOUP_LIBS) $(SOUP_GNOME_LIBS) $(XML_LIBS) \ - $(GCOV_LDFLAGS) \ - $(top_builddir)/rest/librest-@API_VERSION@.la -librest_extras_@API_VERSION@_la_LDFLAGS = -no-undefined -librest_extras_@API_VERSION@_la_SOURCES = $(lib_sources) $(lib_headers) -librest_extras_@API_VERSION@_la_HEADERS = $(lib_headers) -librest_extras_@API_VERSION@_ladir = $(includedir)/rest-@API_VERSION@/rest-extras - - -# Test suite -TESTS = test-runner -check_PROGRAMS = test-runner - -test_runner_SOURCES = test-runner.c $(lib_sources) $(lib_headers) -test_runner_CFLAGS = -DBUILD_TESTS $(librest_extras_@API_VERSION@_la_CFLAGS) $(GCOV_CFLAGS) -test_runner_LDFLAGS = $(librest_extras_@API_VERSION@_la_LIBADD) $(GCOV_LIBS) - -# TODO: use gtester - -# intospection --include $(INTROSPECTION_MAKEFILE) - -if HAVE_INTROSPECTION -INTROSPECTION_GIRS = RestExtras-@API_VERSION@.gir - -# need to include ../rest when scanning/compiling -INTROSPECTION_SCANNER_ARGS = --add-include-path=$(top_builddir)/rest --identifier-prefix= -INTROSPECTION_COMPILER_ARGS = --includedir=$(top_builddir)/rest - -REST_EXTRAS_CINCLUDES=$(patsubst %,--c-include='rest/%',$(shell echo $(lib_headers))) -INTROSPECTION_SCANNER_ARGS += $(REST_EXTRAS_CINCLUDES) - -RestExtras-@API_VERSION@.gir: librest-extras-@API_VERSION@.la Makefile - -RestExtras_@API_VERSION_AM@_gir_NAMESPACE = RestExtras -RestExtras_@API_VERSION_AM@_gir_VERSION = @API_VERSION@ -RestExtras_@API_VERSION_AM@_gir_LIBS = librest-extras-@API_VERSION@.la -RestExtras_@API_VERSION_AM@_gir_FILES = \ - $(lib_headers) \ - $(filter-out %private.h, $(lib_sources)) -RestExtras_@API_VERSION_AM@_gir_CFLAGS = -I$(top_srcdir) -RestExtras_@API_VERSION_AM@_gir_INCLUDES = GObject-2.0 libxml2-2.0 -RestExtras_@API_VERSION_AM@_gir_PACKAGES = gobject-2.0 libsoup-2.4 libxml-2.0 -RestExtras_@API_VERSION_AM@_gir_SCANNERFLAGS = --include-uninstalled=$(top_builddir)/rest/Rest-@API_VERSION@.gir --accept-unprefixed --warn-all -RestExtras_@API_VERSION_AM@_gir_EXPORT_PACKAGES = rest-extras-@API_VERSION@ - -girdir = $(datadir)/gir-1.0 -dist_gir_DATA = $(INTROSPECTION_GIRS) - -typelibsdir = $(libdir)/girepository-1.0/ -typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) - -CLEANFILES += $(dist_gir_DATA) $(typelibs_DATA) -endif # HAVE_INTROSPECTION diff --git a/rest-extras/flickr-proxy-call.c b/rest-extras/flickr-proxy-call.c index e7dfa07..fc38ca0 100644 --- a/rest-extras/flickr-proxy-call.c +++ b/rest-extras/flickr-proxy-call.c @@ -4,6 +4,7 @@ * * Authors: Rob Bradford <rob@linux.intel.com> * Ross Burton <ross@linux.intel.com> + * Günther Wagner <info@gunibert.de> * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, @@ -24,50 +25,57 @@ #include <libsoup/soup.h> #include <rest/rest-proxy-call.h> #include "flickr-proxy-call.h" -#include "flickr-proxy-private.h" +#include "flickr-proxy.h" #include "rest/sha1.h" -G_DEFINE_TYPE (FlickrProxyCall, flickr_proxy_call, REST_TYPE_PROXY_CALL) - -#define GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), FLICKR_TYPE_PROXY_CALL, FlickrProxyCallPrivate)) - typedef struct { gboolean upload; } FlickrProxyCallPrivate; +G_DEFINE_TYPE_WITH_PRIVATE (FlickrProxyCall, flickr_proxy_call, REST_TYPE_PROXY_CALL) + enum { PROP_0, - PROP_UPLOAD + PROP_UPLOAD, + N_PROPS }; +static GParamSpec *properties [N_PROPS]; + static void flickr_proxy_call_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { - switch (property_id) { - case PROP_UPLOAD: - GET_PRIVATE (object)->upload = g_value_get_boolean (value); - break; + FlickrProxyCall *self = FLICKR_PROXY_CALL (object); + FlickrProxyCallPrivate *priv = flickr_proxy_call_get_instance_private (self); + + switch (property_id) + { + case PROP_UPLOAD: + priv->upload = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } + } } static gboolean -_prepare (RestProxyCall *call, GError **error) +_prepare (RestProxyCall *call, + GError **error) { + FlickrProxyCall *self = (FlickrProxyCall *)call; + FlickrProxyCallPrivate *priv = flickr_proxy_call_get_instance_private (self); + FlickrProxy *proxy = NULL; - FlickrProxyPrivate *priv; + const gchar *token = NULL; GHashTable *params; char *s; - g_object_get (call, "proxy", &proxy, NULL); - priv = FLICKR_PROXY_GET_PRIVATE (proxy); + g_object_get (self, "proxy", &proxy, NULL); - if (GET_PRIVATE (call)->upload) { + if (priv->upload) { rest_proxy_bind (REST_PROXY(proxy), "up", "upload"); rest_proxy_call_set_function (call, NULL); } else { @@ -79,10 +87,11 @@ _prepare (RestProxyCall *call, GError **error) rest_proxy_call_set_function (call, NULL); } - rest_proxy_call_add_param (call, "api_key", priv->api_key); + rest_proxy_call_add_param (call, "api_key", flickr_proxy_get_api_key (proxy)); + token = flickr_proxy_get_token (proxy); - if (priv->token) - rest_proxy_call_add_param (call, "auth_token", priv->token); + if (token) + rest_proxy_call_add_param (call, "auth_token", token); /* Get the string params as a hash for signing */ params = rest_params_as_string_hash_table (rest_proxy_call_get_params (call)); @@ -100,14 +109,11 @@ _prepare (RestProxyCall *call, GError **error) static void flickr_proxy_call_class_init (FlickrProxyCallClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); RestProxyCallClass *call_class = REST_PROXY_CALL_CLASS (klass); - GObjectClass *obj_class = G_OBJECT_CLASS (klass); - GParamSpec *pspec; - - g_type_class_add_private (klass, sizeof (FlickrProxyCallPrivate)); call_class->prepare = _prepare; - obj_class->set_property = flickr_proxy_call_set_property; + object_class->set_property = flickr_proxy_call_set_property; /** * FlickrProxyCall:upload: @@ -115,16 +121,19 @@ flickr_proxy_call_class_init (FlickrProxyCallClass *klass) * Set if the call should be sent to the photo upload endpoint and not the * general-purpose endpoint. */ - pspec = g_param_spec_boolean ("upload", "upload", "upload", - FALSE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (obj_class, PROP_UPLOAD, pspec); + properties [PROP_UPLOAD] = + g_param_spec_boolean ("upload", + "upload", + "upload", + FALSE, + (G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void flickr_proxy_call_init (FlickrProxyCall *self) { } - -#if BUILD_TESTS -#warning TODO flickr signature test cases -#endif diff --git a/rest-extras/flickr-proxy-call.h b/rest-extras/flickr-proxy-call.h index 195886e..3903631 100644 --- a/rest-extras/flickr-proxy-call.h +++ b/rest-extras/flickr-proxy-call.h @@ -20,49 +20,22 @@ * */ -#ifndef _FLICKR_PROXY_CALL -#define _FLICKR_PROXY_CALL +#pragma once #include <rest/rest-proxy-call.h> G_BEGIN_DECLS -#define FLICKR_TYPE_PROXY_CALL flickr_proxy_call_get_type() +#define FLICKR_TYPE_PROXY_CALL (flickr_proxy_call_get_type()) -#define FLICKR_PROXY_CALL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLICKR_TYPE_PROXY_CALL, FlickrProxyCall)) +G_DECLARE_DERIVABLE_TYPE (FlickrProxyCall, flickr_proxy_call, FLICKR, PROXY_CALL, RestProxyCall) -#define FLICKR_PROXY_CALL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), FLICKR_TYPE_PROXY_CALL, FlickrProxyCallClass)) - -#define FLICKR_IS_PROXY_CALL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLICKR_TYPE_PROXY_CALL)) - -#define FLICKR_IS_PROXY_CALL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), FLICKR_TYPE_PROXY_CALL)) - -#define FLICKR_PROXY_CALL_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), FLICKR_TYPE_PROXY_CALL, FlickrProxyCallClass)) - -/** - * FlickrProxyCall: - * - * #FlickrProxyCall has no publicly available members. - */ -typedef struct { - RestProxyCall parent; -} FlickrProxyCall; - -typedef struct { +struct _FlickrProxyCallClass { RestProxyCallClass parent_class; + /*< private >*/ /* padding for future expansion */ gpointer _padding_dummy[8]; -} FlickrProxyCallClass; - -GType flickr_proxy_call_get_type (void); +}; G_END_DECLS - -#endif /* _FLICKR_PROXY_CALL */ - diff --git a/rest-extras/flickr-proxy.c b/rest-extras/flickr-proxy.c index 7726359..7e60fa1 100644 --- a/rest-extras/flickr-proxy.c +++ b/rest-extras/flickr-proxy.c @@ -33,18 +33,26 @@ #include <rest/rest-proxy.h> #include <libsoup/soup.h> #include "flickr-proxy.h" -#include "flickr-proxy-private.h" #include "flickr-proxy-call.h" -G_DEFINE_TYPE (FlickrProxy, flickr_proxy, REST_TYPE_PROXY) +typedef struct { + char *api_key; + char *shared_secret; + char *token; +} FlickrProxyPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (FlickrProxy, flickr_proxy, REST_TYPE_PROXY) enum { PROP_0, PROP_API_KEY, PROP_SHARED_SECRET, PROP_TOKEN, + N_PROPS }; +static GParamSpec *properties [N_PROPS]; + GQuark flickr_proxy_error_quark (void) { @@ -52,22 +60,25 @@ flickr_proxy_error_quark (void) } static RestProxyCall * -_new_call (RestProxy *proxy) +_new_call (RestProxy *self) { RestProxyCall *call; call = g_object_new (FLICKR_TYPE_PROXY_CALL, - "proxy", proxy, + "proxy", self, NULL); return call; } static void -flickr_proxy_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec) +flickr_proxy_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - FlickrProxyPrivate *priv = FLICKR_PROXY_GET_PRIVATE (object); + FlickrProxy *self = FLICKR_PROXY (object); + FlickrProxyPrivate *priv = flickr_proxy_get_instance_private (self); switch (property_id) { case PROP_API_KEY: @@ -85,10 +96,13 @@ flickr_proxy_get_property (GObject *object, guint property_id, } static void -flickr_proxy_set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec) +flickr_proxy_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) { - FlickrProxyPrivate *priv = FLICKR_PROXY_GET_PRIVATE (object); + FlickrProxy *self = FLICKR_PROXY (object); + FlickrProxyPrivate *priv = flickr_proxy_get_instance_private (self); switch (property_id) { case PROP_API_KEY: @@ -114,27 +128,21 @@ flickr_proxy_set_property (GObject *object, guint property_id, static void flickr_proxy_finalize (GObject *object) { - FlickrProxyPrivate *priv = FLICKR_PROXY_GET_PRIVATE (object); + FlickrProxy *self = FLICKR_PROXY (object); + FlickrProxyPrivate *priv = flickr_proxy_get_instance_private (self); - g_free (priv->api_key); - g_free (priv->shared_secret); - g_free (priv->token); + g_clear_pointer (&priv->api_key, g_free); + g_clear_pointer (&priv->shared_secret, g_free); + g_clear_pointer (&priv->token, g_free); G_OBJECT_CLASS (flickr_proxy_parent_class)->finalize (object); } -#ifndef G_PARAM_STATIC_STRINGS -#define G_PARAM_STATIC_STRINGS (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB) -#endif - static void flickr_proxy_class_init (FlickrProxyClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); RestProxyClass *proxy_class = REST_PROXY_CLASS (klass); - GParamSpec *pspec; - - g_type_class_add_private (klass, sizeof (FlickrProxyPrivate)); object_class->get_property = flickr_proxy_get_property; object_class->set_property = flickr_proxy_set_property; @@ -142,37 +150,43 @@ flickr_proxy_class_init (FlickrProxyClass *klass) proxy_class->new_call = _new_call; - pspec = g_param_spec_string ("api-key", "api-key", - "The API key", NULL, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, - PROP_API_KEY, - pspec); - - pspec = g_param_spec_string ("shared-secret", "shared-secret", - "The shared secret", NULL, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, - PROP_SHARED_SECRET, - pspec); - - pspec = g_param_spec_string ("token", "token", - "The request or access token", NULL, - G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, - PROP_TOKEN, - pspec); + properties [PROP_API_KEY] = + g_param_spec_string ("api-key", + "api-key", + "The API key", + NULL, + (G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_SHARED_SECRET] = + g_param_spec_string ("shared-secret", + "shared-secret", + "The shared secret", + NULL, + (G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_TOKEN] = + g_param_spec_string ("token", + "token", + "The request or access token", + NULL, + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void flickr_proxy_init (FlickrProxy *self) { - self->priv = FLICKR_PROXY_GET_PRIVATE (self); } RestProxy * flickr_proxy_new (const char *api_key, - const char *shared_secret) + const char *shared_secret) { return flickr_proxy_new_with_token (api_key, shared_secret, @@ -203,9 +217,12 @@ flickr_proxy_new_with_token (const char *api_key, * freed. */ const char * -flickr_proxy_get_api_key (FlickrProxy *proxy) +flickr_proxy_get_api_key (FlickrProxy *self) { - FlickrProxyPrivate *priv = FLICKR_PROXY_GET_PRIVATE (proxy); + FlickrProxyPrivate *priv = flickr_proxy_get_instance_private (self); + + g_return_val_if_fail (FLICKR_IS_PROXY (self), NULL); + return priv->api_key; } @@ -219,9 +236,12 @@ flickr_proxy_get_api_key (FlickrProxy *proxy) * freed. */ const char * -flickr_proxy_get_shared_secret (FlickrProxy *proxy) +flickr_proxy_get_shared_secret (FlickrProxy *self) { - FlickrProxyPrivate *priv = FLICKR_PROXY_GET_PRIVATE (proxy); + FlickrProxyPrivate *priv = flickr_proxy_get_instance_private (self); + + g_return_val_if_fail (FLICKR_IS_PROXY (self), NULL); + return priv->shared_secret; } @@ -235,9 +255,12 @@ flickr_proxy_get_shared_secret (FlickrProxy *proxy) * by #FlickrProxy and should not be freed. */ const char * -flickr_proxy_get_token (FlickrProxy *proxy) +flickr_proxy_get_token (FlickrProxy *self) { - FlickrProxyPrivate *priv = FLICKR_PROXY_GET_PRIVATE (proxy); + FlickrProxyPrivate *priv = flickr_proxy_get_instance_private (self); + + g_return_val_if_fail (FLICKR_IS_PROXY (self), NULL); + return priv->token; } @@ -249,31 +272,36 @@ flickr_proxy_get_token (FlickrProxy *proxy) * Set the token. */ void -flickr_proxy_set_token (FlickrProxy *proxy, const char *token) +flickr_proxy_set_token (FlickrProxy *self, + const char *token) { FlickrProxyPrivate *priv; - g_return_if_fail (FLICKR_IS_PROXY (proxy)); - priv = FLICKR_PROXY_GET_PRIVATE (proxy); + g_return_if_fail (FLICKR_IS_PROXY (self)); - if (priv->token) - g_free (priv->token); + priv = flickr_proxy_get_instance_private (self); - priv->token = g_strdup (token); + if (g_strcmp0 (priv->token, token) != 0) + { + g_clear_pointer (&priv->token, g_free); + priv->token = g_strdup (token); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TOKEN]); + } } char * -flickr_proxy_sign (FlickrProxy *proxy, GHashTable *params) +flickr_proxy_sign (FlickrProxy *self, + GHashTable *params) { FlickrProxyPrivate *priv; GList *keys; char *md5; GChecksum *checksum; - g_return_val_if_fail (FLICKR_IS_PROXY (proxy), NULL); + g_return_val_if_fail (FLICKR_IS_PROXY (self), NULL); g_return_val_if_fail (params, NULL); - priv = FLICKR_PROXY_GET_PRIVATE (proxy); + priv = flickr_proxy_get_instance_private (self); checksum = g_checksum_new (G_CHECKSUM_MD5); g_checksum_update (checksum, (guchar *)priv->shared_secret, -1); @@ -300,26 +328,29 @@ flickr_proxy_sign (FlickrProxy *proxy, GHashTable *params) } char * -flickr_proxy_build_login_url (FlickrProxy *proxy, +flickr_proxy_build_login_url (FlickrProxy *self, const char *frob, const char *perms) { + FlickrProxyPrivate *priv; GUri *uri; GHashTable *params; char *sig, *s; char *query; - g_return_val_if_fail (FLICKR_IS_PROXY (proxy), NULL); + g_return_val_if_fail (FLICKR_IS_PROXY (self), NULL); + + priv = flickr_proxy_get_instance_private (self); params = g_hash_table_new (g_str_hash, g_str_equal); - g_hash_table_insert (params, "api_key", proxy->priv->api_key); + g_hash_table_insert (params, "api_key", priv->api_key); g_hash_table_insert (params, "perms", (gpointer)perms); if (frob) g_hash_table_insert (params, "frob", (gpointer)frob); - sig = flickr_proxy_sign (proxy, params); + sig = flickr_proxy_sign (self, params); g_hash_table_insert (params, "api_sig", sig); query = soup_form_encode_hash (params); @@ -328,7 +359,7 @@ flickr_proxy_build_login_url (FlickrProxy *proxy, NULL, "flickr.com", -1, - "services/auth/", + "/services/auth/", query, NULL); @@ -353,7 +384,8 @@ flickr_proxy_build_login_url (FlickrProxy *proxy, * Returns: %TRUE if this response is successful, %FALSE otherwise. */ gboolean -flickr_proxy_is_successful (RestXmlNode *root, GError **error) +flickr_proxy_is_successful (RestXmlNode *root, + GError **error) { RestXmlNode *node; @@ -389,12 +421,12 @@ flickr_proxy_is_successful (RestXmlNode *root, GError **error) * Returns: (type FlickrProxyCall) (transfer full): a new #FlickrProxyCall */ RestProxyCall * -flickr_proxy_new_upload (FlickrProxy *proxy) +flickr_proxy_new_upload (FlickrProxy *self) { - g_return_val_if_fail (FLICKR_IS_PROXY (proxy), NULL); + g_return_val_if_fail (FLICKR_IS_PROXY (self), NULL); return g_object_new (FLICKR_TYPE_PROXY_CALL, - "proxy", proxy, + "proxy", self, "upload", TRUE, NULL); } @@ -417,7 +449,9 @@ flickr_proxy_new_upload (FlickrProxy *proxy) * Returns: (type FlickrProxyCall) (transfer full): a new #FlickrProxyCall */ RestProxyCall * -flickr_proxy_new_upload_for_file (FlickrProxy *proxy, const char *filename, GError **error) +flickr_proxy_new_upload_for_file (FlickrProxy *self, + const char *filename, + GError **error) { GMappedFile *map; GError *err = NULL; @@ -425,7 +459,7 @@ flickr_proxy_new_upload_for_file (FlickrProxy *proxy, const char *filename, GErr RestParam *param; RestProxyCall *call = NULL; - g_return_val_if_fail (FLICKR_IS_PROXY (proxy), NULL); + g_return_val_if_fail (FLICKR_IS_PROXY (self), NULL); g_return_val_if_fail (filename, NULL); /* Open the file */ @@ -443,7 +477,7 @@ flickr_proxy_new_upload_for_file (FlickrProxy *proxy, const char *filename, GErr NULL); /* Make the call */ - call = flickr_proxy_new_upload (proxy); + call = flickr_proxy_new_upload (self); param = rest_param_new_with_owner ("photo", g_mapped_file_get_contents (map), g_mapped_file_get_length (map), diff --git a/rest-extras/flickr-proxy.h b/rest-extras/flickr-proxy.h index 5cff622..bc13149 100644 --- a/rest-extras/flickr-proxy.h +++ b/rest-extras/flickr-proxy.h @@ -4,6 +4,7 @@ * * Authors: Rob Bradford <rob@linux.intel.com> * Ross Burton <ross@linux.intel.com> + * Günther Wagner <info@gunibert.de> * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, @@ -20,81 +21,47 @@ * */ -#ifndef _FLICKR_PROXY -#define _FLICKR_PROXY +#pragma once #include <rest/rest-proxy.h> #include <rest/rest-xml-parser.h> G_BEGIN_DECLS +#define FLICKR_PROXY_ERROR flickr_proxy_error_quark() #define FLICKR_TYPE_PROXY flickr_proxy_get_type() -#define FLICKR_PROXY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLICKR_TYPE_PROXY, FlickrProxy)) - -#define FLICKR_PROXY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), FLICKR_TYPE_PROXY, FlickrProxyClass)) - -#define FLICKR_IS_PROXY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLICKR_TYPE_PROXY)) - -#define FLICKR_IS_PROXY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), FLICKR_TYPE_PROXY)) +G_DECLARE_DERIVABLE_TYPE (FlickrProxy, flickr_proxy, FLICKR, PROXY, RestProxy) -#define FLICKR_PROXY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), FLICKR_TYPE_PROXY, FlickrProxyClass)) - -typedef struct _FlickrProxyPrivate FlickrProxyPrivate; - -/** - * FlickrProxy: - * - * #FlickrProxy has no publicly available members. - */ -typedef struct { - RestProxy parent; - FlickrProxyPrivate *priv; -} FlickrProxy; - -typedef struct { +struct _FlickrProxyClass { RestProxyClass parent_class; + /*< private >*/ /* padding for future expansion */ gpointer _padding_dummy[8]; -} FlickrProxyClass; - -#define FLICKR_PROXY_ERROR flickr_proxy_error_quark() - -GType flickr_proxy_get_type (void); - -RestProxy* flickr_proxy_new (const char *api_key, - const char *shared_secret); - -RestProxy* flickr_proxy_new_with_token (const char *api_key, - const char *shared_secret, - const char *token); - -const char * flickr_proxy_get_api_key (FlickrProxy *proxy); - -const char * flickr_proxy_get_shared_secret (FlickrProxy *proxy); - -const char * flickr_proxy_get_token (FlickrProxy *proxy); - -void flickr_proxy_set_token (FlickrProxy *proxy, const char *token); - -char * flickr_proxy_sign (FlickrProxy *proxy, GHashTable *params); - -char * flickr_proxy_build_login_url (FlickrProxy *proxy, - const char *frob, - const char *perms); - -gboolean flickr_proxy_is_successful (RestXmlNode *root, GError **error); - -RestProxyCall * flickr_proxy_new_upload (FlickrProxy *proxy); - -RestProxyCall * flickr_proxy_new_upload_for_file (FlickrProxy *proxy, const char *filename, GError **error); +}; + + +RestProxy *flickr_proxy_new (const char *api_key, + const char *shared_secret); +RestProxy *flickr_proxy_new_with_token (const char *api_key, + const char *shared_secret, + const char *token); +const char *flickr_proxy_get_api_key (FlickrProxy *proxy); +const char *flickr_proxy_get_shared_secret (FlickrProxy *proxy); +const char *flickr_proxy_get_token (FlickrProxy *proxy); +void flickr_proxy_set_token (FlickrProxy *proxy, + const char *token); +char *flickr_proxy_sign (FlickrProxy *proxy, + GHashTable *params); +char *flickr_proxy_build_login_url (FlickrProxy *proxy, + const char *frob, + const char *perms); +gboolean flickr_proxy_is_successful (RestXmlNode *root, + GError **error); +RestProxyCall *flickr_proxy_new_upload (FlickrProxy *proxy); +RestProxyCall *flickr_proxy_new_upload_for_file (FlickrProxy *proxy, + const char *filename, + GError **error); G_END_DECLS - -#endif /* _FLICKR_PROXY */ diff --git a/rest-extras/lastfm-proxy-call.c b/rest-extras/lastfm-proxy-call.c index 417fa18..dd56715 100644 --- a/rest-extras/lastfm-proxy-call.c +++ b/rest-extras/lastfm-proxy-call.c @@ -25,7 +25,7 @@ #include <rest/rest-private.h> #include <rest/rest-proxy-call.h> #include "lastfm-proxy-call.h" -#include "lastfm-proxy-private.h" +#include "lastfm-proxy.h" #include "rest/sha1.h" G_DEFINE_TYPE (LastfmProxyCall, lastfm_proxy_call, REST_TYPE_PROXY_CALL) @@ -34,23 +34,23 @@ static gboolean _prepare (RestProxyCall *call, GError **error) { LastfmProxy *proxy = NULL; - LastfmProxyPrivate *priv; GHashTable *params; + const gchar *session_key; char *s; g_object_get (call, "proxy", &proxy, NULL); - priv = LASTFM_PROXY_GET_PRIVATE (proxy); rest_proxy_call_add_params (call, "method", rest_proxy_call_get_function (call), - "api_key", priv->api_key, + "api_key", lastfm_proxy_get_api_key (proxy), NULL); /* Reset function because Lastfm puts the function in the parameters */ rest_proxy_call_set_function (call, NULL); - if (priv->session_key) - rest_proxy_call_add_param (call, "sk", priv->session_key); + session_key = lastfm_proxy_get_session_key (proxy); + if (session_key) + rest_proxy_call_add_param (call, "sk", session_key); params = rest_params_as_string_hash_table (rest_proxy_call_get_params (call)); s = lastfm_proxy_sign (proxy, params); @@ -75,7 +75,3 @@ static void lastfm_proxy_call_init (LastfmProxyCall *self) { } - -#if BUILD_TESTS -#warning TODO lastfm signature test cases -#endif diff --git a/rest-extras/lastfm-proxy-call.h b/rest-extras/lastfm-proxy-call.h index da4c7ad..20e9b8e 100644 --- a/rest-extras/lastfm-proxy-call.h +++ b/rest-extras/lastfm-proxy-call.h @@ -20,8 +20,7 @@ * */ -#ifndef _LASTFM_PROXY_CALL -#define _LASTFM_PROXY_CALL +#pragma once #include <rest/rest-proxy-call.h> @@ -44,6 +43,3 @@ struct _LastfmProxyCallClass { }; G_END_DECLS - -#endif /* _LASTFM_PROXY_CALL */ - diff --git a/rest-extras/lastfm-proxy.c b/rest-extras/lastfm-proxy.c index b9fc713..7b0dd28 100644 --- a/rest-extras/lastfm-proxy.c +++ b/rest-extras/lastfm-proxy.c @@ -26,18 +26,26 @@ #include <rest/rest-proxy.h> #include <libsoup/soup.h> #include "lastfm-proxy.h" -#include "lastfm-proxy-private.h" #include "lastfm-proxy-call.h" -G_DEFINE_TYPE (LastfmProxy, lastfm_proxy, REST_TYPE_PROXY) +typedef struct { + char *api_key; + char *secret; + char *session_key; +} LastfmProxyPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (LastfmProxy, lastfm_proxy, REST_TYPE_PROXY) enum { PROP_0, PROP_API_KEY, PROP_SECRET, PROP_SESSION_KEY, + N_PROPS, }; +static GParamSpec *properties [N_PROPS]; + GQuark lastfm_proxy_error_quark (void) { @@ -57,10 +65,13 @@ _new_call (RestProxy *proxy) } static void -lastfm_proxy_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec) +lastfm_proxy_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - LastfmProxyPrivate *priv = LASTFM_PROXY_GET_PRIVATE (object); + LastfmProxy *self = LASTFM_PROXY (object); + LastfmProxyPrivate *priv = lastfm_proxy_get_instance_private (self); switch (property_id) { case PROP_API_KEY: @@ -81,7 +92,8 @@ static void lastfm_proxy_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { - LastfmProxyPrivate *priv = LASTFM_PROXY_GET_PRIVATE (object); + LastfmProxy *self = LASTFM_PROXY (object); + LastfmProxyPrivate *priv = lastfm_proxy_get_instance_private (self); switch (property_id) { case PROP_API_KEY: @@ -107,7 +119,8 @@ lastfm_proxy_set_property (GObject *object, guint property_id, static void lastfm_proxy_finalize (GObject *object) { - LastfmProxyPrivate *priv = LASTFM_PROXY_GET_PRIVATE (object); + LastfmProxy *self = LASTFM_PROXY (object); + LastfmProxyPrivate *priv = lastfm_proxy_get_instance_private (self); g_free (priv->api_key); g_free (priv->secret); @@ -116,18 +129,11 @@ lastfm_proxy_finalize (GObject *object) G_OBJECT_CLASS (lastfm_proxy_parent_class)->finalize (object); } -#ifndef G_PARAM_STATIC_STRINGS -#define G_PARAM_STATIC_STRINGS (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB) -#endif - static void lastfm_proxy_class_init (LastfmProxyClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); RestProxyClass *proxy_class = REST_PROXY_CLASS (klass); - GParamSpec *pspec; - - g_type_class_add_private (klass, sizeof (LastfmProxyPrivate)); object_class->get_property = lastfm_proxy_get_property; object_class->set_property = lastfm_proxy_set_property; @@ -135,32 +141,38 @@ lastfm_proxy_class_init (LastfmProxyClass *klass) proxy_class->new_call = _new_call; - pspec = g_param_spec_string ("api-key", "api-key", - "The API key", NULL, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, - PROP_API_KEY, - pspec); - - pspec = g_param_spec_string ("secret", "secret", - "The API key secret", NULL, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, - PROP_SECRET, - pspec); - - pspec = g_param_spec_string ("session-key", "session-key", - "The session key", NULL, - G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, - PROP_SESSION_KEY, - pspec); + properties [PROP_API_KEY] = + g_param_spec_string ("api-key", + "api-key", + "The API key", + NULL, + (G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_SECRET] = + g_param_spec_string ("secret", + "secret", + "The API key secret", + NULL, + (G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_SESSION_KEY] = + g_param_spec_string ("session-key", + "session-key", + "The session key", + NULL, + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void lastfm_proxy_init (LastfmProxy *self) { - self->priv = LASTFM_PROXY_GET_PRIVATE (self); } RestProxy * @@ -196,9 +208,12 @@ lastfm_proxy_new_with_session (const char *api_key, * freed. */ const char * -lastfm_proxy_get_api_key (LastfmProxy *proxy) +lastfm_proxy_get_api_key (LastfmProxy *self) { - LastfmProxyPrivate *priv = LASTFM_PROXY_GET_PRIVATE (proxy); + LastfmProxyPrivate *priv = lastfm_proxy_get_instance_private (self); + + g_return_val_if_fail (LASTFM_IS_PROXY (self), NULL); + return priv->api_key; } @@ -212,9 +227,12 @@ lastfm_proxy_get_api_key (LastfmProxy *proxy) * freed. */ const char * -lastfm_proxy_get_secret (LastfmProxy *proxy) +lastfm_proxy_get_secret (LastfmProxy *self) { - LastfmProxyPrivate *priv = LASTFM_PROXY_GET_PRIVATE (proxy); + LastfmProxyPrivate *priv = lastfm_proxy_get_instance_private (self); + + g_return_val_if_fail (LASTFM_IS_PROXY (self), NULL); + return priv->secret; } @@ -228,9 +246,12 @@ lastfm_proxy_get_secret (LastfmProxy *proxy) * by #LastfmProxy and should not be freed. */ const char * -lastfm_proxy_get_session_key (LastfmProxy *proxy) +lastfm_proxy_get_session_key (LastfmProxy *self) { - LastfmProxyPrivate *priv = LASTFM_PROXY_GET_PRIVATE (proxy); + LastfmProxyPrivate *priv = lastfm_proxy_get_instance_private (self); + + g_return_val_if_fail (LASTFM_IS_PROXY (self), NULL); + return priv->session_key; } @@ -242,12 +263,14 @@ lastfm_proxy_get_session_key (LastfmProxy *proxy) * Set the session key. */ void -lastfm_proxy_set_session_key (LastfmProxy *proxy, const char *session_key) +lastfm_proxy_set_session_key (LastfmProxy *self, + const char *session_key) { LastfmProxyPrivate *priv; - g_return_if_fail (LASTFM_IS_PROXY (proxy)); - priv = LASTFM_PROXY_GET_PRIVATE (proxy); + g_return_if_fail (LASTFM_IS_PROXY (self)); + + priv = lastfm_proxy_get_instance_private (self); if (priv->session_key) g_free (priv->session_key); @@ -256,17 +279,18 @@ lastfm_proxy_set_session_key (LastfmProxy *proxy, const char *session_key) } char * -lastfm_proxy_sign (LastfmProxy *proxy, GHashTable *params) +lastfm_proxy_sign (LastfmProxy *self, + GHashTable *params) { LastfmProxyPrivate *priv; GString *s; GList *keys; char *md5; - g_return_val_if_fail (LASTFM_IS_PROXY (proxy), NULL); + g_return_val_if_fail (LASTFM_IS_PROXY (self), NULL); g_return_val_if_fail (params, NULL); - priv = LASTFM_PROXY_GET_PRIVATE (proxy); + priv = lastfm_proxy_get_instance_private (self); s = g_string_new (NULL); @@ -295,13 +319,18 @@ lastfm_proxy_sign (LastfmProxy *proxy, GHashTable *params) } char * -lastfm_proxy_build_login_url (LastfmProxy *proxy, const char *token) +lastfm_proxy_build_login_url (LastfmProxy *self, + const char *token) { - g_return_val_if_fail (LASTFM_IS_PROXY (proxy), NULL); + LastfmProxyPrivate *priv; + + g_return_val_if_fail (LASTFM_IS_PROXY (self), NULL); g_return_val_if_fail (token, NULL); + priv = lastfm_proxy_get_instance_private (self); + return g_strdup_printf ("http://www.last.fm/api/auth/?api_key=%s&token=%s", - proxy->priv->api_key, + priv->api_key, token); } @@ -316,7 +345,8 @@ lastfm_proxy_build_login_url (LastfmProxy *proxy, const char *token) * Returns: %TRUE if this response is successful, %FALSE otherwise. */ gboolean -lastfm_proxy_is_successful (RestXmlNode *root, GError **error) +lastfm_proxy_is_successful (RestXmlNode *root, + GError **error) { RestXmlNode *node; diff --git a/rest-extras/lastfm-proxy.h b/rest-extras/lastfm-proxy.h index 85e612a..b378882 100644 --- a/rest-extras/lastfm-proxy.h +++ b/rest-extras/lastfm-proxy.h @@ -20,8 +20,7 @@ * */ -#ifndef _LASTFM_PROXY -#define _LASTFM_PROXY +#pragma once #include <rest/rest-proxy.h> #include <rest/rest-xml-parser.h> @@ -30,65 +29,33 @@ G_BEGIN_DECLS #define LASTFM_TYPE_PROXY lastfm_proxy_get_type() -#define LASTFM_PROXY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), LASTFM_TYPE_PROXY, LastfmProxy)) +G_DECLARE_DERIVABLE_TYPE (LastfmProxy, lastfm_proxy, LASTFM, PROXY, RestProxy) -#define LASTFM_PROXY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), LASTFM_TYPE_PROXY, LastfmProxyClass)) - -#define LASTFM_IS_PROXY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LASTFM_TYPE_PROXY)) - -#define LASTFM_IS_PROXY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), LASTFM_TYPE_PROXY)) - -#define LASTFM_PROXY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), LASTFM_TYPE_PROXY, LastfmProxyClass)) - -typedef struct _LastfmProxyPrivate LastfmProxyPrivate; - -/** - * LastfmProxy: - * - * #LastfmProxy has no publicly available members. - */ -typedef struct { - RestProxy parent; - LastfmProxyPrivate *priv; -} LastfmProxy; - -typedef struct { +struct _LastfmProxyClass { RestProxyClass parent_class; + /*< private >*/ /* padding for future expansion */ gpointer _padding_dummy[8]; -} LastfmProxyClass; +}; #define LASTFM_PROXY_ERROR lastfm_proxy_error_quark() -GType lastfm_proxy_get_type (void); - -RestProxy* lastfm_proxy_new (const char *api_key, - const char *secret); - -RestProxy* lastfm_proxy_new_with_session (const char *api_key, - const char *secret, - const char *session_key); - -const char * lastfm_proxy_get_api_key (LastfmProxy *proxy); - -const char * lastfm_proxy_get_secret (LastfmProxy *proxy); - -const char * lastfm_proxy_get_session_key (LastfmProxy *proxy); - -void lastfm_proxy_set_session_key (LastfmProxy *proxy, const char *session_key); - -char * lastfm_proxy_sign (LastfmProxy *proxy, GHashTable *params); - -char * lastfm_proxy_build_login_url (LastfmProxy *proxy, const char *token); - -gboolean lastfm_proxy_is_successful (RestXmlNode *root, GError **error); +RestProxy *lastfm_proxy_new (const char *api_key, + const char *secret); +RestProxy *lastfm_proxy_new_with_session (const char *api_key, + const char *secret, + const char *session_key); +const char *lastfm_proxy_get_api_key (LastfmProxy *proxy); +const char *lastfm_proxy_get_secret (LastfmProxy *proxy); +const char *lastfm_proxy_get_session_key (LastfmProxy *proxy); +void lastfm_proxy_set_session_key (LastfmProxy *proxy, + const char *session_key); +char *lastfm_proxy_sign (LastfmProxy *proxy, + GHashTable *params); +char *lastfm_proxy_build_login_url (LastfmProxy *proxy, + const char *token); +gboolean lastfm_proxy_is_successful (RestXmlNode *root, + GError **error); G_END_DECLS - -#endif /* _LASTFM_PROXY */ diff --git a/rest-extras/meson.build b/rest-extras/meson.build index 241bc73..159676b 100644 --- a/rest-extras/meson.build +++ b/rest-extras/meson.build @@ -49,9 +49,12 @@ if get_option('introspection') librest_extras_gir = gnome.generate_gir(librest_extras_lib, sources: librest_extras_sources + librest_extras_headers, namespace: 'RestExtras', + symbol_prefix: 'rest_extras', + identifier_prefix: 'RestExtras', nsversion: librest_api_version, includes: [ 'GObject-2.0', 'Gio-2.0', 'Soup-@0@'.format(libsoup_api_version), librest_gir[0] ], extra_args: [ '--accept-unprefixed' ], + dependencies: librest_extras_deps, install: true, ) diff --git a/rest-extras/youtube-proxy.c b/rest-extras/youtube-proxy.c index cd598f4..08faca4 100644 --- a/rest-extras/youtube-proxy.c +++ b/rest-extras/youtube-proxy.c @@ -29,9 +29,13 @@ #include "rest/rest-private.h" #include "youtube-proxy.h" -#include "youtube-proxy-private.h" -G_DEFINE_TYPE (YoutubeProxy, youtube_proxy, REST_TYPE_PROXY) +typedef struct { + char *developer_key; + char *user_auth; +} YoutubeProxyPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (YoutubeProxy, youtube_proxy, REST_TYPE_PROXY) #define UPLOAD_URL \ "http://uploads.gdata.youtube.com/feeds/api/users/default/uploads" @@ -40,8 +44,11 @@ enum { PROP_0, PROP_DEVELOPER_KEY, PROP_USER_AUTH, + N_PROPS, }; +static GParamSpec *properties [N_PROPS]; + GQuark youtube_proxy_error_quark (void) { @@ -49,10 +56,13 @@ youtube_proxy_error_quark (void) } static void -youtube_proxy_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec) +youtube_proxy_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - YoutubeProxyPrivate *priv = YOUTUBE_PROXY_GET_PRIVATE (object); + YoutubeProxy *self = YOUTUBE_PROXY (object); + YoutubeProxyPrivate *priv = youtube_proxy_get_instance_private (self); switch (property_id) { case PROP_DEVELOPER_KEY: @@ -67,10 +77,13 @@ youtube_proxy_get_property (GObject *object, guint property_id, } static void -youtube_proxy_set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec) +youtube_proxy_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) { - YoutubeProxyPrivate *priv = YOUTUBE_PROXY_GET_PRIVATE (object); + YoutubeProxy *self = YOUTUBE_PROXY (object); + YoutubeProxyPrivate *priv = youtube_proxy_get_instance_private (self); switch (property_id) { case PROP_DEVELOPER_KEY: @@ -89,7 +102,8 @@ youtube_proxy_set_property (GObject *object, guint property_id, static void youtube_proxy_finalize (GObject *object) { - YoutubeProxyPrivate *priv = YOUTUBE_PROXY_GET_PRIVATE (object); + YoutubeProxy *self = YOUTUBE_PROXY (object); + YoutubeProxyPrivate *priv = youtube_proxy_get_instance_private (self); g_free (priv->developer_key); g_free (priv->user_auth); @@ -101,36 +115,34 @@ static void youtube_proxy_class_init (YoutubeProxyClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - GParamSpec *pspec; - - g_type_class_add_private (klass, sizeof (YoutubeProxyPrivate)); object_class->get_property = youtube_proxy_get_property; object_class->set_property = youtube_proxy_set_property; object_class->finalize = youtube_proxy_finalize; - pspec = g_param_spec_string ("developer-key", "developer-key", - "The developer API key", NULL, - G_PARAM_READWRITE| - G_PARAM_CONSTRUCT_ONLY| - G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, - PROP_DEVELOPER_KEY, - pspec); - - pspec = g_param_spec_string ("user-auth", "user-auth", - "The ClientLogin token", NULL, - G_PARAM_READWRITE| - G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, - PROP_USER_AUTH, - pspec); + properties [PROP_DEVELOPER_KEY] = + g_param_spec_string ("developer-key", + "developer-key", + "The developer API key", + NULL, + (G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_USER_AUTH] = + g_param_spec_string ("user-auth", + "user-auth", + "The ClientLogin token", + NULL, + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void youtube_proxy_init (YoutubeProxy *self) { - self->priv = YOUTUBE_PROXY_GET_PRIVATE (self); } RestProxy * @@ -151,16 +163,17 @@ youtube_proxy_new_with_auth (const char *developer_key, } void -youtube_proxy_set_user_auth (YoutubeProxy *proxy, +youtube_proxy_set_user_auth (YoutubeProxy *self, const gchar *user_auth) { - YoutubeProxyPrivate *priv = proxy->priv; + YoutubeProxyPrivate *priv = youtube_proxy_get_instance_private (self); priv->user_auth = g_strdup (user_auth); } static gchar * -_construct_upload_atom_xml (GHashTable *fields, gboolean incomplete) +_construct_upload_atom_xml (GHashTable *fields, + gboolean incomplete) { GHashTableIter iter; gpointer key, value; @@ -215,7 +228,7 @@ _set_upload_headers (YoutubeProxy *self, SoupMessageHeaders *headers, const gchar *filename) { - YoutubeProxyPrivate *priv = self->priv; + YoutubeProxyPrivate *priv = youtube_proxy_get_instance_private (self); gchar *user_auth_header; gchar *devkey_header; gchar *basename; diff --git a/rest-extras/youtube-proxy.h b/rest-extras/youtube-proxy.h index 1fe6c20..1215cf4 100644 --- a/rest-extras/youtube-proxy.h +++ b/rest-extras/youtube-proxy.h @@ -20,8 +20,7 @@ * */ -#ifndef _YOUTUBE_PROXY -#define _YOUTUBE_PROXY +#pragma once #include <rest/rest-proxy.h> @@ -29,52 +28,18 @@ G_BEGIN_DECLS #define YOUTUBE_TYPE_PROXY youtube_proxy_get_type() -#define YOUTUBE_PROXY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), YOUTUBE_TYPE_PROXY, YoutubeProxy)) +G_DECLARE_DERIVABLE_TYPE (YoutubeProxy, youtube_proxy, YOUTUBE, PROXY, RestProxy) -#define YOUTUBE_PROXY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), YOUTUBE_TYPE_PROXY, YoutubeProxyClass)) - -#define YOUTUBE_IS_PROXY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YOUTUBE_TYPE_PROXY)) - -#define YOUTUBE_IS_PROXY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), YOUTUBE_TYPE_PROXY)) - -#define YOUTUBE_PROXY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), YOUTUBE_TYPE_PROXY, YoutubeProxyClass)) - -typedef struct _YoutubeProxyPrivate YoutubeProxyPrivate; - -/** - * YoutubeProxy: - * - * #YoutubeProxy has no publicly available members. - */ -typedef struct { - RestProxy parent; - YoutubeProxyPrivate *priv; -} YoutubeProxy; - -typedef struct { +struct _YoutubeProxyClass { RestProxyClass parent_class; + /*< private >*/ /* padding for future expansion */ gpointer _padding_dummy[8]; -} YoutubeProxyClass; +}; #define YOUTUBE_PROXY_ERROR youtube_proxy_error_quark() -GType youtube_proxy_get_type (void); - -RestProxy* youtube_proxy_new (const gchar *developer_key); - -RestProxy* youtube_proxy_new_with_auth (const gchar *developer_key, - const gchar *user_auth); - -void youtube_proxy_set_user_auth (YoutubeProxy *proxy, - const gchar *user_auth); - typedef void (*YoutubeProxyUploadCallback)(YoutubeProxy *proxy, const gchar *payload, gsize total, @@ -83,15 +48,18 @@ typedef void (*YoutubeProxyUploadCallback)(YoutubeProxy *proxy, GObject *weak_object, gpointer user_data); -gboolean youtube_proxy_upload_async (YoutubeProxy *self, - const gchar *filename, - GHashTable *fields, - gboolean incomplete, - YoutubeProxyUploadCallback callback, - GObject *weak_object, - gpointer user_data, - GError **error); +RestProxy *youtube_proxy_new (const gchar *developer_key); +RestProxy *youtube_proxy_new_with_auth (const gchar *developer_key, + const gchar *user_auth); +void youtube_proxy_set_user_auth (YoutubeProxy *proxy, + const gchar *user_auth); +gboolean youtube_proxy_upload_async (YoutubeProxy *self, + const gchar *filename, + GHashTable *fields, + gboolean incomplete, + YoutubeProxyUploadCallback callback, + GObject *weak_object, + gpointer user_data, + GError **error); G_END_DECLS - -#endif /* _YOUTUBE_PROXY */ diff --git a/rest/Makefile.am b/rest/Makefile.am deleted file mode 100644 index 64205d4..0000000 --- a/rest/Makefile.am +++ /dev/null @@ -1,139 +0,0 @@ -CLEANFILES = - -# For some reason I can't use $(librest_@API_VERSION@_la_SOURCES) in -# test_runner_SOURCES, so we have to do this -lib_sources = \ - rest-param.c \ - rest-params.c \ - rest-proxy.c \ - rest-proxy-auth.c \ - rest-proxy-auth-private.h \ - rest-proxy-call.c \ - rest-proxy-call-private.h \ - rest-xml-node.c \ - rest-xml-parser.c \ - rest-main.c \ - rest-private.h \ - rest-enum-types.c \ - oauth-proxy.c \ - oauth-proxy-call.c \ - oauth-proxy-private.h \ - oauth2-proxy.c \ - oauth2-proxy-call.c \ - oauth2-proxy-private.h \ - sha1.c \ - sha1.h -nodist_lib_sources = \ - rest-marshal.h \ - rest-marshal.c -lib_headers = \ - rest-param.h \ - rest-params.h \ - rest-proxy.h \ - rest-proxy-auth.h \ - rest-proxy-call.h \ - rest-enum-types.h \ - oauth-proxy.h \ - oauth-proxy-call.h \ - oauth2-proxy.h \ - oauth2-proxy-call.h \ - rest-xml-node.h \ - rest-xml-parser.h - -EXTRA_DIST = \ - rest-marshal.txt - -lib_LTLIBRARIES = librest-@API_VERSION@.la -librest_@API_VERSION@_la_CFLAGS = $(GLIB_CFLAGS) $(GTHREAD_CFLAGS) \ - $(SOUP_CFLAGS) $(SOUP_GNOME_CFLAGS) \ - $(XML_CFLAGS) $(GCOV_CFLAGS) \ - -I$(top_srcdir) -Wall -DG_LOG_DOMAIN=\"Rest\" -librest_@API_VERSION@_la_LDFLAGS = -no-undefined -librest_@API_VERSION@_la_LIBADD = $(GLIB_LIBS) $(GTHREAD_LIBS) \ - $(SOUP_LIBS) $(SOUP_GNOME_LIBS) $(XML_LIBS) \ - $(GCOV_LDFLAGS) -librest_@API_VERSION@_la_SOURCES = $(lib_sources) $(lib_headers) -nodist_librest_@API_VERSION@_la_SOURCES = $(nodist_lib_sources) -librest_@API_VERSION@_la_HEADERS = $(lib_headers) -librest_@API_VERSION@_ladir = $(includedir)/rest-@API_VERSION@/rest - -rest-enum-types.h: stamp-rest-enum-types.h - @true - -stamp-rest-enum-types.h: rest-proxy.h rest-proxy-call.h Makefile - $(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) \ - --fhead "#ifndef __REST_ENUM_TYPES_H__\n#define __REST_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \ - --fprod "/* enumerations from \"@filename@\" */\n" \ - --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define REST_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ - --ftail "G_END_DECLS\n\n#endif /* __REST_ENUM_TYPES_H__ */" rest-proxy.h rest-proxy-call.h) >> xgen-gtbh \ - && (cmp -s xgen-gtbh rest-enum-types.h || cp xgen-gtbh rest-enum-types.h ) \ - && rm -f xgen-gtbh \ - && echo timestamp > $(@F) - -rest-enum-types.c: rest-proxy.h rest-proxy-call.h Makefile rest-enum-types.h - $(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) \ - --fhead "#include \"rest-proxy.h\"\n#include \"rest-proxy-call.h\"\n#include \"rest-enum-types.h\"" \ - --fprod "\n/* enumerations from \"@filename@\" */" \ - --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \ - --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ - --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \ - rest-proxy.h rest-proxy-call.h) > xgen-gtbc \ - && cp xgen-gtbc rest-enum-types.c \ - && rm -f xgen-gtbc - -BUILT_SOURCES = rest-marshal.c rest-marshal.h - -rest-marshal.c: rest-marshal.txt - $(AM_V_GEN)echo "#include \"rest-marshal.h\"" > $@ && \ - glib-genmarshal --body $< >> $@ || (rm -f $@ && exit 1) - -rest-marshal.h: rest-marshal.txt - $(AM_V_GEN)glib-genmarshal --header $< > $@ || (rm -f $@ && exit 1) - - -# Test suite -TESTS = test-runner -check_PROGRAMS = test-runner - -test_runner_SOURCES = test-runner.c $(lib_sources) $(nodist_lib_sources) $(lib_headers) -test_runner_CFLAGS = -DBUILD_TESTS $(librest_@API_VERSION@_la_CFLAGS) $(GCOV_CFLAGS) -test_runner_LDFLAGS = $(librest_@API_VERSION@_la_LIBADD) $(GCOV_LDFLAGS) - -# TODO: use gtester - -# introspection --include $(INTROSPECTION_MAKEFILE) - -if HAVE_INTROSPECTION -INTROSPECTION_GIRS = Rest-@API_VERSION@.gir - -Rest-@API_VERSION@.gir: librest-@API_VERSION@.la Makefile - -Rest_@API_VERSION_AM@_gir_NAMESPACE = Rest -Rest_@API_VERSION_AM@_gir_VERSION = @API_VERSION@ -Rest_@API_VERSION_AM@_gir_LIBS = librest-@API_VERSION@.la -Rest_@API_VERSION_AM@_gir_FILES = \ - $(lib_headers) \ - $(filter-out %private.h, $(lib_sources)) -Rest_@API_VERSION_AM@_gir_CFLAGS = -I$(top_srcdir) -Rest_@API_VERSION_AM@_gir_INCLUDES = GObject-2.0 Gio-2.0 Soup-2.4 -Rest_@API_VERSION_AM@_gir_PACKAGES = gobject-2.0 libsoup-2.4 libxml-2.0 gio-2.0 -Rest_@API_VERSION_AM@_gir_SCANNERFLAGS = --accept-unprefixed --warn-all -Rest_@API_VERSION_AM@_gir_EXPORT_PACKAGES = rest-@API_VERSION@ -REST_CINCLUDES=$(patsubst %,--c-include='rest/%',$(shell echo $(lib_headers))) -INTROSPECTION_SCANNER_ARGS = $(REST_CINCLUDES) - -girdir = $(datadir)/gir-1.0 -dist_gir_DATA = $(INTROSPECTION_GIRS) - -typelibsdir = $(libdir)/girepository-1.0/ -typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) - -CLEANFILES += $(dist_gir_DATA) \ - $(typelibs_DATA) \ - rest-enum-types.h \ - rest-enum-types.c \ - stamp-rest-enum-types.h \ - $(NULL) - -endif # HAVE_INTROSPECTION diff --git a/rest/meson.build b/rest/meson.build index 136416f..868c239 100644 --- a/rest/meson.build +++ b/rest/meson.build @@ -19,10 +19,13 @@ librest_sources = [ 'rest-main.c', 'oauth-proxy.c', 'oauth-proxy-call.c', - 'oauth2-proxy.c', - 'oauth2-proxy-call.c', 'sha1.c', + 'rest-oauth2-proxy.c', + 'rest-oauth2-proxy-call.c', + 'rest-pkce-code-challenge.c', + 'rest-utils.c', + librest_enums, librest_marshal, ] @@ -30,8 +33,6 @@ librest_sources = [ librest_headers = [ 'oauth-proxy-call.h', 'oauth-proxy.h', - 'oauth2-proxy-call.h', - 'oauth2-proxy.h', 'rest-param.h', 'rest-params.h', 'rest-proxy-auth.h', @@ -39,11 +40,18 @@ librest_headers = [ 'rest-proxy.h', 'rest-xml-node.h', 'rest-xml-parser.h', + + 'rest-oauth2-proxy.h', + 'rest-oauth2-proxy-call.h', + 'rest-pkce-code-challenge.h', + 'rest-utils.h', + 'rest.h', ] librest_deps = [ glib_dep, libsoup_dep, + libjson_glib_dep, libxml_dep, ] @@ -65,6 +73,8 @@ install_headers(librest_headers, subdir: librest_pkg_string / 'rest', ) +rest_dep_sources = [] + # GObject Introspection if get_option('introspection') librest_gir_extra_args = [ @@ -77,13 +87,16 @@ if get_option('introspection') endforeach librest_gir = gnome.generate_gir(librest_lib, - sources: [ librest_headers, librest_enums[1] ], - namespace: 'Rest', + sources: librest_sources + librest_headers + librest_enums, nsversion: librest_api_version, + namespace: 'Rest', + symbol_prefix: 'rest', + identifier_prefix: 'Rest', includes: [ 'GObject-2.0', 'Gio-2.0', 'Soup-@0@'.format(libsoup_api_version) ], extra_args: librest_gir_extra_args, install: true, ) + rest_dep_sources += librest_gir if get_option('vapi') librest_vapi = gnome.generate_vapi(librest_pkg_string, @@ -95,7 +108,10 @@ if get_option('introspection') endif librest_dep = declare_dependency( + sources: rest_dep_sources, link_with: librest_lib, + include_directories: include_directories('..'), + dependencies: librest_deps, ) # Test suite diff --git a/rest/oauth-proxy-call.c b/rest/oauth-proxy-call.c index e238c3c..560e928 100644 --- a/rest/oauth-proxy-call.c +++ b/rest/oauth-proxy-call.c @@ -24,7 +24,7 @@ #include <libsoup/soup.h> #include <rest/rest-proxy-call.h> #include "oauth-proxy-call.h" -#include "oauth-proxy-private.h" +#include "oauth-proxy.h" #include "rest-proxy-call-private.h" #include "sha1.h" @@ -33,14 +33,14 @@ G_DEFINE_TYPE (OAuthProxyCall, oauth_proxy_call, REST_TYPE_PROXY_CALL) #define OAUTH_ENCODE_STRING(x_) (x_ ? g_uri_escape_string( (x_), NULL, TRUE) : g_strdup ("")) static char * -sign_plaintext (OAuthProxyPrivate *priv) +sign_plaintext (OAuthProxy *proxy) { char *cs; char *ts; char *rv; - cs = OAUTH_ENCODE_STRING (priv->consumer_secret); - ts = OAUTH_ENCODE_STRING (priv->token_secret); + cs = OAUTH_ENCODE_STRING (oauth_proxy_get_consumer_secret (proxy)); + ts = OAUTH_ENCODE_STRING (oauth_proxy_get_token_secret (proxy)); rv = g_strconcat (cs, "&", ts, NULL); g_free (cs); @@ -117,7 +117,6 @@ merge_params (GHashTable *hash, RestParams *params) static char * sign_hmac (OAuthProxy *proxy, RestProxyCall *call, GHashTable *oauth_params) { - OAuthProxyPrivate *priv; const char *url_str; char *key, *signature, *ep, *eep; const char *content_type; @@ -127,15 +126,14 @@ sign_hmac (OAuthProxy *proxy, RestProxyCall *call, GHashTable *oauth_params) RestParam *param; gboolean encode_query_params = TRUE; - priv = PROXY_GET_PRIVATE (proxy); url_str = rest_proxy_call_get_url (call); text = g_string_new (NULL); g_string_append (text, rest_proxy_call_get_method (call)); g_string_append_c (text, '&'); - if (priv->oauth_echo) { - g_string_append_uri_escaped (text, priv->service_url, NULL, FALSE); - } else if (priv->signature_host != NULL) { + if (oauth_proxy_is_echo (proxy)) + g_string_append_uri_escaped (text, oauth_proxy_get_service_url (proxy), NULL, FALSE); + else if (oauth_proxy_get_signature_host (proxy) != NULL) { GUri *url = g_uri_parse (url_str, G_URI_FLAGS_ENCODED, NULL); GUri *new_url; gchar *signing_url; @@ -143,7 +141,7 @@ sign_hmac (OAuthProxy *proxy, RestProxyCall *call, GHashTable *oauth_params) new_url = g_uri_build (g_uri_get_flags (url), g_uri_get_scheme (url), g_uri_get_userinfo (url), - priv->signature_host, + oauth_proxy_get_signature_host (proxy), g_uri_get_port (url), g_uri_get_path (url), g_uri_get_query (url), @@ -178,7 +176,7 @@ sign_hmac (OAuthProxy *proxy, RestProxyCall *call, GHashTable *oauth_params) /* Merge the OAuth parameters with the query parameters */ all_params = g_hash_table_new (g_str_hash, g_str_equal); merge_hashes (all_params, oauth_params); - if (encode_query_params && !priv->oauth_echo) { + if (encode_query_params && !oauth_proxy_is_echo (proxy)) { merge_params (all_params, rest_proxy_call_get_params (call)); } @@ -191,7 +189,7 @@ sign_hmac (OAuthProxy *proxy, RestProxyCall *call, GHashTable *oauth_params) g_hash_table_destroy (all_params); /* PLAINTEXT signature value is the HMAC-SHA1 key value */ - key = sign_plaintext (priv); + key = sign_plaintext (proxy); signature = hmac_sha1 (key, text->str); @@ -263,12 +261,10 @@ static gboolean _prepare (RestProxyCall *call, GError **error) { OAuthProxy *proxy = NULL; - OAuthProxyPrivate *priv; char *s; GHashTable *oauth_params; g_object_get (call, "proxy", &proxy, NULL); - priv = PROXY_GET_PRIVATE (proxy); /* We have to make this hash free the strings and thus duplicate when we put * them in since when we call call steal_oauth_params that has to duplicate @@ -288,15 +284,15 @@ _prepare (RestProxyCall *call, GError **error) g_hash_table_insert (oauth_params, g_strdup ("oauth_nonce"), s); g_hash_table_insert (oauth_params, g_strdup ("oauth_consumer_key"), - g_strdup (priv->consumer_key)); + g_strdup (oauth_proxy_get_consumer_key (proxy))); - if (priv->token) - g_hash_table_insert (oauth_params, g_strdup ("oauth_token"), g_strdup (priv->token)); + if (oauth_proxy_get_token (proxy)) + g_hash_table_insert (oauth_params, g_strdup ("oauth_token"), g_strdup (oauth_proxy_get_token (proxy))); - switch (priv->method) { + switch (oauth_proxy_get_sign_method (proxy)) { case PLAINTEXT: g_hash_table_insert (oauth_params, g_strdup ("oauth_signature_method"), g_strdup ("PLAINTEXT")); - s = sign_plaintext (priv); + s = sign_plaintext (proxy); break; case HMAC_SHA1: g_hash_table_insert (oauth_params, g_strdup ("oauth_signature_method"), g_strdup ("HMAC-SHA1")); @@ -306,9 +302,9 @@ _prepare (RestProxyCall *call, GError **error) g_hash_table_insert (oauth_params, g_strdup ("oauth_signature"), s); s = make_authorized_header (oauth_params); - if (priv->oauth_echo) { + if (oauth_proxy_is_echo (proxy)) { rest_proxy_call_add_header (call, "X-Verify-Credentials-Authorization", s); - rest_proxy_call_add_param (call, "X-Auth-Service-Provider", priv->service_url); + rest_proxy_call_add_param (call, "X-Auth-Service-Provider", oauth_proxy_get_service_url (proxy)); } else { rest_proxy_call_add_header (call, "Authorization", s); } @@ -336,27 +332,24 @@ oauth_proxy_call_init (OAuthProxyCall *self) void oauth_proxy_call_parse_token_response (OAuthProxyCall *call) { - OAuthProxyPrivate *priv; GHashTable *form; OAuthProxy *proxy; + g_autofree gchar *formstr = NULL; /* TODO: sanity checks, error handling, probably return gboolean */ g_return_if_fail (OAUTH_IS_PROXY_CALL (call)); g_object_get (call, "proxy", &proxy, NULL); - priv = PROXY_GET_PRIVATE (proxy); g_object_unref (proxy); - g_assert (priv); - form = soup_form_decode (rest_proxy_call_get_payload (REST_PROXY_CALL (call))); + formstr = g_strndup (rest_proxy_call_get_payload (REST_PROXY_CALL (call)), rest_proxy_call_get_payload_length (REST_PROXY_CALL (call))); + form = soup_form_decode (formstr); - g_free (priv->token); - g_free (priv->token_secret); - priv->token = g_strdup (g_hash_table_lookup (form, "oauth_token")); - priv->token_secret = g_strdup (g_hash_table_lookup (form, "oauth_token_secret")); + oauth_proxy_set_token (proxy, g_hash_table_lookup (form, "oauth_token")); + oauth_proxy_set_token_secret (proxy, g_hash_table_lookup (form, "oauth_token_secret")); /* This header should only exist for request_token replies, but its easier just to always check it */ - priv->oauth_10a = g_hash_table_lookup (form, "oauth_callback_confirmed") != NULL; + oauth_proxy_set_oauth10a (proxy, g_hash_table_lookup (form, "oauth_callback_confirmed") != NULL); g_hash_table_destroy (form); } diff --git a/rest/oauth-proxy-private.h b/rest/oauth-proxy-private.h deleted file mode 100644 index 2d178a5..0000000 --- a/rest/oauth-proxy-private.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * librest - RESTful web services access - * Copyright (c) 2008, 2009, Intel Corporation. - * - * Authors: Rob Bradford <rob@linux.intel.com> - * Ross Burton <ross@linux.intel.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU Lesser General Public License, - * version 2.1, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - * more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include "oauth-proxy.h" - -#define PROXY_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), OAUTH_TYPE_PROXY, OAuthProxyPrivate)) - -typedef struct { - /* Application "consumer" keys */ - char *consumer_key; - char *consumer_secret; - /* Authorisation "user" tokens */ - char *token; - char *token_secret; - /* How we're signing */ - OAuthSignatureMethod method; - /* OAuth 1.0a */ - gboolean oauth_10a; - char *verifier; - /* OAuth Echo */ - gboolean oauth_echo; - char *service_url; - /* URL to use for signatures */ - char *signature_host; -} OAuthProxyPrivate; diff --git a/rest/oauth-proxy.c b/rest/oauth-proxy.c index f8c0053..d81c0d7 100644 --- a/rest/oauth-proxy.c +++ b/rest/oauth-proxy.c @@ -23,10 +23,32 @@ #include <rest/rest-proxy.h> #include <libsoup/soup.h> #include "oauth-proxy.h" -#include "oauth-proxy-private.h" #include "oauth-proxy-call.h" -G_DEFINE_TYPE (OAuthProxy, oauth_proxy, REST_TYPE_PROXY) +typedef struct { + /* Application "consumer" keys */ + char *consumer_key; + char *consumer_secret; + /* Authorisation "user" tokens */ + char *token; + char *token_secret; + /* How we're signing */ + OAuthSignatureMethod method; + /* OAuth 1.0a */ + gboolean oauth_10a; + char *verifier; + /* OAuth Echo */ + gboolean oauth_echo; + char *service_url; + /* URL to use for signatures */ + char *signature_host; +} OAuthProxyPrivate; + +struct _OAuthProxy { + RestProxy parent_instance; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (OAuthProxy, oauth_proxy, REST_TYPE_PROXY) enum { PROP_0, @@ -54,7 +76,8 @@ static void oauth_proxy_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { - OAuthProxyPrivate *priv = PROXY_GET_PRIVATE (object); + OAuthProxy *self = OAUTH_PROXY (object); + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (self); switch (property_id) { case PROP_CONSUMER_KEY: @@ -84,7 +107,8 @@ static void oauth_proxy_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { - OAuthProxyPrivate *priv = PROXY_GET_PRIVATE (object); + OAuthProxy *self = OAUTH_PROXY (object); + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (self); switch (property_id) { case PROP_CONSUMER_KEY: @@ -123,7 +147,8 @@ oauth_proxy_set_property (GObject *object, guint property_id, static void oauth_proxy_finalize (GObject *object) { - OAuthProxyPrivate *priv = PROXY_GET_PRIVATE (object); + OAuthProxy *self = OAUTH_PROXY (object); + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (self); g_free (priv->consumer_key); g_free (priv->consumer_secret); @@ -146,8 +171,6 @@ oauth_proxy_class_init (OAuthProxyClass *klass) RestProxyClass *proxy_class = REST_PROXY_CLASS (klass); GParamSpec *pspec; - g_type_class_add_private (klass, sizeof (OAuthProxyPrivate)); - object_class->get_property = oauth_proxy_get_property; object_class->set_property = oauth_proxy_set_property; object_class->finalize = oauth_proxy_finalize; @@ -201,7 +224,8 @@ oauth_proxy_class_init (OAuthProxyClass *klass) static void oauth_proxy_init (OAuthProxy *self) { - PROXY_GET_PRIVATE (self)->method = HMAC_SHA1; + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (self); + priv->method = HMAC_SHA1; } /** @@ -534,6 +558,22 @@ oauth_proxy_access_token_finish (OAuthProxy *proxy, return g_task_propagate_boolean (G_TASK (result), error); } +const char * +oauth_proxy_get_consumer_key (OAuthProxy *self) +{ + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (self); + + return priv->consumer_key; +} + +const char * +oauth_proxy_get_consumer_secret (OAuthProxy *self) +{ + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (self); + + return priv->consumer_secret; +} + /** * oauth_proxy_get_token: * @proxy: an #OAuthProxy @@ -546,7 +586,8 @@ oauth_proxy_access_token_finish (OAuthProxy *proxy, const char * oauth_proxy_get_token (OAuthProxy *proxy) { - OAuthProxyPrivate *priv = PROXY_GET_PRIVATE (proxy); + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (proxy); + return priv->token; } @@ -560,10 +601,9 @@ oauth_proxy_get_token (OAuthProxy *proxy) void oauth_proxy_set_token (OAuthProxy *proxy, const char *token) { - OAuthProxyPrivate *priv; + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (proxy); g_return_if_fail (OAUTH_IS_PROXY (proxy)); - priv = PROXY_GET_PRIVATE (proxy); g_free (priv->token); priv->token = g_strdup (token); @@ -581,7 +621,8 @@ oauth_proxy_set_token (OAuthProxy *proxy, const char *token) const char * oauth_proxy_get_token_secret (OAuthProxy *proxy) { - OAuthProxyPrivate *priv = PROXY_GET_PRIVATE (proxy); + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (proxy); + return priv->token_secret; } @@ -595,10 +636,9 @@ oauth_proxy_get_token_secret (OAuthProxy *proxy) void oauth_proxy_set_token_secret (OAuthProxy *proxy, const char *token_secret) { - OAuthProxyPrivate *priv; + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (proxy); g_return_if_fail (OAUTH_IS_PROXY (proxy)); - priv = PROXY_GET_PRIVATE (proxy); if (priv->token_secret) g_free (priv->token_secret); @@ -619,9 +659,22 @@ oauth_proxy_set_token_secret (OAuthProxy *proxy, const char *token_secret) gboolean oauth_proxy_is_oauth10a (OAuthProxy *proxy) { + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (proxy); + g_return_val_if_fail (OAUTH_IS_PROXY (proxy), FALSE); - return PROXY_GET_PRIVATE (proxy)->oauth_10a; + return priv->oauth_10a; +} + +void +oauth_proxy_set_oauth10a (OAuthProxy *self, + gboolean oauth10a) +{ + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (self); + + g_return_if_fail (OAUTH_IS_PROXY (self)); + + priv->oauth_10a = oauth10a; } /** @@ -636,10 +689,9 @@ oauth_proxy_is_oauth10a (OAuthProxy *proxy) const char * oauth_proxy_get_signature_host (OAuthProxy *proxy) { - OAuthProxyPrivate *priv; + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (proxy); g_return_val_if_fail (OAUTH_IS_PROXY (proxy), NULL); - priv = PROXY_GET_PRIVATE (proxy); return priv->signature_host; } @@ -655,10 +707,9 @@ void oauth_proxy_set_signature_host (OAuthProxy *proxy, const char *signature_host) { - OAuthProxyPrivate *priv; + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (proxy); g_return_if_fail (OAUTH_IS_PROXY (proxy)); - priv = PROXY_GET_PRIVATE (proxy); g_free (priv->signature_host); @@ -686,14 +737,13 @@ oauth_proxy_new_echo_proxy (OAuthProxy *proxy, gboolean binding_required) { OAuthProxy *echo_proxy; - OAuthProxyPrivate *priv, *echo_priv; + OAuthProxyPrivate *echo_priv; + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (proxy); g_return_val_if_fail (OAUTH_IS_PROXY (proxy), NULL); g_return_val_if_fail (service_url, NULL); g_return_val_if_fail (url_format, NULL); - priv = PROXY_GET_PRIVATE (proxy); - echo_proxy = g_object_new (OAUTH_TYPE_PROXY, "url-format", url_format, "binding-required", binding_required, @@ -703,7 +753,7 @@ oauth_proxy_new_echo_proxy (OAuthProxy *proxy, "token", priv->token, "token-secret", priv->token_secret, NULL); - echo_priv = PROXY_GET_PRIVATE (echo_proxy); + echo_priv = oauth_proxy_get_instance_private (echo_proxy); echo_priv->oauth_echo = TRUE; echo_priv->service_url = g_strdup (service_url); @@ -728,3 +778,26 @@ oauth_signature_method_get_type (void) return enum_type_id; } +gboolean +oauth_proxy_is_echo (OAuthProxy *self) +{ + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (self); + + return priv->oauth_echo; +} + +const char * +oauth_proxy_get_service_url (OAuthProxy *self) +{ + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (self); + + return priv->service_url; +} + +OAuthSignatureMethod +oauth_proxy_get_sign_method (OAuthProxy *self) +{ + OAuthProxyPrivate *priv = oauth_proxy_get_instance_private (self); + + return priv->method; +} diff --git a/rest/oauth-proxy.h b/rest/oauth-proxy.h index fa1a89e..2616a34 100644 --- a/rest/oauth-proxy.h +++ b/rest/oauth-proxy.h @@ -28,37 +28,7 @@ G_BEGIN_DECLS #define OAUTH_TYPE_PROXY oauth_proxy_get_type() - -#define OAUTH_PROXY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), OAUTH_TYPE_PROXY, OAuthProxy)) - -#define OAUTH_PROXY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), OAUTH_TYPE_PROXY, OAuthProxyClass)) - -#define OAUTH_IS_PROXY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OAUTH_TYPE_PROXY)) - -#define OAUTH_IS_PROXY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), OAUTH_TYPE_PROXY)) - -#define OAUTH_PROXY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), OAUTH_TYPE_PROXY, OAuthProxyClass)) - -/** - * OAuthProxy: - * - * #OAuthProxy has no publicly available members. - */ -typedef struct { - RestProxy parent; -} OAuthProxy; - -typedef struct { - RestProxyClass parent_class; - /*< private >*/ - /* padding for future expansion */ - gpointer _padding_dummy[8]; -} OAuthProxyClass; +G_DECLARE_FINAL_TYPE (OAuthProxy, oauth_proxy, OAUTH, PROXY, RestProxy) GType oauth_signature_method_get_type (void) G_GNUC_CONST; #define OAUTH_TYPE_SIGNATURE_METHOD (oauth_signature_method_get_type()) @@ -77,68 +47,63 @@ typedef enum { HMAC_SHA1 } OAuthSignatureMethod; -GType oauth_proxy_get_type (void); - -RestProxy* oauth_proxy_new (const char *consumer_key, - const char *consumer_secret, - const gchar *url_format, - gboolean binding_required); - -RestProxy* oauth_proxy_new_with_token (const char *consumer_key, - const char *consumer_secret, - const char *token, - const char *token_secret, - const gchar *url_format, - gboolean binding_required); - -gboolean oauth_proxy_request_token (OAuthProxy *proxy, - const char *function, - const char *callback_uri, - GError **error); - -void oauth_proxy_request_token_async (OAuthProxy *proxy, - const char *function, - const char *callback_uri, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean oauth_proxy_request_token_finish (OAuthProxy *proxy, - GAsyncResult *result, - GError **error); - -gboolean oauth_proxy_is_oauth10a (OAuthProxy *proxy); - -gboolean oauth_proxy_access_token (OAuthProxy *proxy, - const char *function, - const char *verifier, - GError **error); - -void oauth_proxy_access_token_async (OAuthProxy *proxy, - const char *function, - const char *verifier, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean oauth_proxy_access_token_finish (OAuthProxy *proxy, - GAsyncResult *result, - GError **error); - -const char * oauth_proxy_get_token (OAuthProxy *proxy); - -void oauth_proxy_set_token (OAuthProxy *proxy, const char *token); - -const char * oauth_proxy_get_token_secret (OAuthProxy *proxy); - -void oauth_proxy_set_token_secret (OAuthProxy *proxy, const char *token_secret); -const char * oauth_proxy_get_signature_host (OAuthProxy *proxy); - -void oauth_proxy_set_signature_host (OAuthProxy *proxy, - const char *signature_host); - -RestProxy *oauth_proxy_new_echo_proxy (OAuthProxy *proxy, - const char *service_url, - const gchar *url_format, - gboolean binding_required); +RestProxy *oauth_proxy_new (const char *consumer_key, + const char *consumer_secret, + const gchar *url_format, + gboolean binding_required); +RestProxy *oauth_proxy_new_with_token (const char *consumer_key, + const char *consumer_secret, + const char *token, + const char *token_secret, + const gchar *url_format, + gboolean binding_required); +gboolean oauth_proxy_request_token (OAuthProxy *proxy, + const char *function, + const char *callback_uri, + GError **error); +void oauth_proxy_request_token_async (OAuthProxy *proxy, + const char *function, + const char *callback_uri, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean oauth_proxy_request_token_finish (OAuthProxy *proxy, + GAsyncResult *result, + GError **error); +gboolean oauth_proxy_is_oauth10a (OAuthProxy *proxy); +void oauth_proxy_set_oauth10a (OAuthProxy *proxy, + gboolean oauth10a); +gboolean oauth_proxy_access_token (OAuthProxy *proxy, + const char *function, + const char *verifier, + GError **error); +void oauth_proxy_access_token_async (OAuthProxy *proxy, + const char *function, + const char *verifier, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean oauth_proxy_access_token_finish (OAuthProxy *proxy, + GAsyncResult *result, + GError **error); +const char *oauth_proxy_get_consumer_key (OAuthProxy *proxy); +const char *oauth_proxy_get_consumer_secret (OAuthProxy *proxy); +const char *oauth_proxy_get_token (OAuthProxy *proxy); +void oauth_proxy_set_token (OAuthProxy *proxy, + const char *token); +const char *oauth_proxy_get_token_secret (OAuthProxy *proxy); +void oauth_proxy_set_token_secret (OAuthProxy *proxy, + const char *token_secret); +const char *oauth_proxy_get_signature_host (OAuthProxy *proxy); +void oauth_proxy_set_signature_host (OAuthProxy *proxy, + const char *signature_host); +RestProxy *oauth_proxy_new_echo_proxy (OAuthProxy *proxy, + const char *service_url, + const gchar *url_format, + gboolean binding_required); +gboolean oauth_proxy_is_echo (OAuthProxy *proxy); +const char *oauth_proxy_get_service_url (OAuthProxy *proxy); +OAuthSignatureMethod oauth_proxy_get_sign_method (OAuthProxy *proxy); G_END_DECLS diff --git a/rest/oauth2-proxy-call.c b/rest/oauth2-proxy-call.c deleted file mode 100644 index 2d71b45..0000000 --- a/rest/oauth2-proxy-call.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * librest - RESTful web services access - * Copyright (c) 2008, 2009, Intel Corporation. - * - * Authors: Rob Bradford <rob@linux.intel.com> - * Ross Burton <ross@linux.intel.com> - * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU Lesser General Public License, - * version 2.1, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - * more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <string.h> -#include <libsoup/soup.h> -#include <rest/rest-proxy-call.h> -#include "oauth2-proxy-call.h" -#include "oauth2-proxy-private.h" -#include "sha1.h" - -G_DEFINE_TYPE (OAuth2ProxyCall, oauth2_proxy_call, REST_TYPE_PROXY_CALL) - -static gboolean -_prepare (RestProxyCall *call, GError **error) -{ - OAuth2Proxy *proxy = NULL; - gboolean result = TRUE; - - g_object_get (call, "proxy", &proxy, NULL); - - if (!proxy->priv->access_token) { - g_set_error (error, - REST_PROXY_CALL_ERROR, - REST_PROXY_CALL_FAILED, - "Missing access token, web service not properly authenticated"); - result = FALSE; - } else { - rest_proxy_call_add_param (call, "access_token", proxy->priv->access_token); - } - - g_object_unref (proxy); - - return result; -} - -static void -oauth2_proxy_call_class_init (OAuth2ProxyCallClass *klass) -{ - RestProxyCallClass *call_class = REST_PROXY_CALL_CLASS (klass); - - call_class->prepare = _prepare; -} - -static void -oauth2_proxy_call_init (OAuth2ProxyCall *self) -{ -} - diff --git a/rest/oauth2-proxy-call.h b/rest/oauth2-proxy-call.h deleted file mode 100644 index 805a501..0000000 --- a/rest/oauth2-proxy-call.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * librest - RESTful web services access - * Copyright (c) 2008, 2009, Intel Corporation. - * - * Authors: Rob Bradford <rob@linux.intel.com> - * Ross Burton <ross@linux.intel.com> - * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU Lesser General Public License, - * version 2.1, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - * more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#ifndef _OAUTH2_PROXY_CALL -#define _OAUTH2_PROXY_CALL - -#include <rest/rest-proxy-call.h> - -G_BEGIN_DECLS - -#define OAUTH2_TYPE_PROXY_CALL oauth2_proxy_call_get_type() -G_DECLARE_DERIVABLE_TYPE (OAuth2ProxyCall, oauth2_proxy_call, OAUTH2, PROXY_CALL, RestProxyCall) - -/** - * OAuth2ProxyCall: - * - * #OAuth2ProxyCall has no publicly available members. - */ - -struct _OAuth2ProxyCallClass { - RestProxyCallClass parent_class; - /*< private >*/ - /* padding for future expansion */ - gpointer _padding_dummy[8]; -}; - -G_END_DECLS - -#endif /* _OAUTH2_PROXY_CALL */ diff --git a/rest/oauth2-proxy.c b/rest/oauth2-proxy.c deleted file mode 100644 index 3382f8b..0000000 --- a/rest/oauth2-proxy.c +++ /dev/null @@ -1,397 +0,0 @@ -/* - * librest - RESTful web services access - * Copyright (c) 2008, 2009, 2010 Intel Corporation. - * - * Authors: Rob Bradford <rob@linux.intel.com> - * Ross Burton <ross@linux.intel.com> - * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU Lesser General Public License, - * version 2.1, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - * more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <rest/rest-proxy.h> -#include <libsoup/soup.h> -#include "oauth2-proxy.h" -#include "oauth2-proxy-private.h" -#include "oauth2-proxy-call.h" - -#define GET_PRIVATE(o) oauth2_proxy_get_instance_private(OAUTH2_PROXY(o)) - -G_DEFINE_TYPE_WITH_PRIVATE (OAuth2Proxy, oauth2_proxy, REST_TYPE_PROXY) - -GQuark -oauth2_proxy_error_quark (void) -{ - return g_quark_from_static_string ("rest-oauth2-proxy"); -} - -enum { - PROP_0, - PROP_CLIENT_ID, - PROP_AUTH_ENDPOINT, - PROP_ACCESS_TOKEN -}; - -static RestProxyCall * -_new_call (RestProxy *proxy) -{ - RestProxyCall *call; - - call = g_object_new (OAUTH2_TYPE_PROXY_CALL, - "proxy", proxy, - NULL); - - return call; -} - -static void -oauth2_proxy_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec) -{ - OAuth2ProxyPrivate *priv = ((OAuth2Proxy*)object)->priv; - - switch (property_id) { - case PROP_CLIENT_ID: - g_value_set_string (value, priv->client_id); - break; - case PROP_AUTH_ENDPOINT: - g_value_set_string (value, priv->auth_endpoint); - break; - case PROP_ACCESS_TOKEN: - g_value_set_string (value, priv->access_token); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -} - -static void -oauth2_proxy_set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec) -{ - OAuth2ProxyPrivate *priv = ((OAuth2Proxy*)object)->priv; - - switch (property_id) { - case PROP_CLIENT_ID: - if (priv->client_id) - g_free (priv->client_id); - priv->client_id = g_value_dup_string (value); - break; - case PROP_AUTH_ENDPOINT: - if (priv->auth_endpoint) - g_free (priv->auth_endpoint); - priv->auth_endpoint = g_value_dup_string (value); - break; - case PROP_ACCESS_TOKEN: - if (priv->access_token) - g_free (priv->access_token); - priv->access_token = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -} - -static void -oauth2_proxy_finalize (GObject *object) -{ - OAuth2ProxyPrivate *priv = ((OAuth2Proxy*)object)->priv; - - g_free (priv->client_id); - g_free (priv->auth_endpoint); - g_free (priv->access_token); - - G_OBJECT_CLASS (oauth2_proxy_parent_class)->finalize (object); -} - -static void -oauth2_proxy_class_init (OAuth2ProxyClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - RestProxyClass *proxy_class = REST_PROXY_CLASS (klass); - GParamSpec *pspec; - - object_class->get_property = oauth2_proxy_get_property; - object_class->set_property = oauth2_proxy_set_property; - object_class->finalize = oauth2_proxy_finalize; - - proxy_class->new_call = _new_call; - - pspec = g_param_spec_string ("client-id", "client-id", - "The client (application) id", NULL, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, - PROP_CLIENT_ID, - pspec); - - pspec = g_param_spec_string ("auth-endpoint", "auth-endpoint", - "The authentication endpoint url", NULL, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, - PROP_AUTH_ENDPOINT, - pspec); - - pspec = g_param_spec_string ("access-token", "access-token", - "The request or access token", NULL, - G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, - PROP_ACCESS_TOKEN, - pspec); -} - -static void -oauth2_proxy_init (OAuth2Proxy *proxy) -{ - proxy->priv = GET_PRIVATE (proxy); -} - -/** - * oauth2_proxy_new: - * @client_id: the client (application) id - * @auth_endpoint: the authentication endpoint URL - * @url_format: the endpoint URL - * @binding_required: whether the URL needs to be bound before calling - * - * Create a new #OAuth2Proxy for the specified endpoint @url_format, using the - * specified API key and secret. - * - * This proxy won't have the Token set so will be unauthorised. If the token is - * unknown then the following steps should be taken to acquire an access token: - * - Get the authentication url with oauth2_proxy_build_login_url() - * - Display this url in an embedded browser widget - * - wait for the browser widget to be redirected to the specified redirect_uri - * - extract the token from the fragment of the redirected uri (using - * convenience function oauth2_proxy_extract_access_token()) - * - set the token with oauth2_proxy_set_access_token() - * - * Set @binding_required to %TRUE if the URL contains string formatting - * operations (for example "http://foo.com/%<!-- -->s". These must be expanded - * using rest_proxy_bind() before invoking the proxy. - * - * Returns: A new #OAuth2Proxy. - */ -RestProxy * -oauth2_proxy_new (const char *client_id, - const char *auth_endpoint, - const gchar *url_format, - gboolean binding_required) -{ - return g_object_new (OAUTH2_TYPE_PROXY, - "client-id", client_id, - "auth-endpoint", auth_endpoint, - "url-format", url_format, - "binding-required", binding_required, - NULL); -} - -/** - * oauth2_proxy_new_with_token: - * @client_id: the client (application) id - * @access_token: the Access Token - * @auth_endpoint: the authentication endpoint URL - * @url_format: the endpoint URL - * @binding_required: whether the URL needs to be bound before calling - * - * Create a new #OAuth2Proxy for the specified endpoint @url_format, using the - * specified client id - * - * @access_token is used for the Access Token, so if they are still valid then - * this proxy is authorised. - * - * Set @binding_required to %TRUE if the URL contains string formatting - * operations (for example "http://foo.com/%<!-- -->s". These must be expanded - * using rest_proxy_bind() before invoking the proxy. - * - * Returns: A new #OAuth2Proxy. - */ -RestProxy * -oauth2_proxy_new_with_token (const char *client_id, - const char *access_token, - const char *auth_endpoint, - const gchar *url_format, - gboolean binding_required) -{ - return g_object_new (OAUTH2_TYPE_PROXY, - "client-id", client_id, - "access-token", access_token, - "auth-endpoint", auth_endpoint, - "url-format", url_format, - "binding-required", binding_required, - NULL); -} - -/* allocates a new string of the form "key=value" */ -static void -append_query_param (gpointer key, gpointer value, gpointer user_data) -{ - GString *params = (GString*) user_data; - char *encoded_val, *encoded_key; - char *param; - - encoded_val = g_uri_escape_string (value, NULL, TRUE); - encoded_key = g_uri_escape_string (key, NULL, TRUE); - - param = g_strdup_printf ("%s=%s", encoded_key, encoded_val); - g_free (encoded_key); - g_free (encoded_val); - - // if there's already a parameter in the string, we need to add a '&' - // separator before adding the new param - if (params->len) - g_string_append_c (params, '&'); - g_string_append (params, param); -} - -/** - * oauth2_proxy_build_login_url_full: - * @proxy: a OAuth2Proxy object - * @redirect_uri: the uri to redirect to after the user authenticates - * @extra_params: any extra parameters to add to the login url (e.g. facebook - * uses 'scope=foo,bar' to request extended permissions). - * - * Builds a url at which the user can log in to the specified OAuth2-based web - * service. In general, this url should be displayed in an embedded browser - * widget, and you should then intercept the browser's redirect to @redirect_uri - * and extract the access token from the url fragment. After the access token - * has been retrieved, call oauth2_proxy_set_access_token(). This must be done - * before making any API calls to the service. - * - * See the oauth2 spec for more details about the "user-agent" authentication - * flow. - * - * The @extra_params and @redirect_uri should not be uri-encoded, that will be - * done automatically - * - * Returns: a newly allocated uri string - */ -char * -oauth2_proxy_build_login_url_full (OAuth2Proxy *proxy, - const char* redirect_uri, - GHashTable* extra_params) -{ - char *url; - GString *params = 0; - char *encoded_uri, *encoded_id; - - g_return_val_if_fail (proxy, NULL); - g_return_val_if_fail (redirect_uri, NULL); - - if (extra_params && g_hash_table_size (extra_params) > 0) { - params = g_string_new (NULL); - g_hash_table_foreach (extra_params, append_query_param, params); - } - - encoded_uri = g_uri_escape_string (redirect_uri, NULL, TRUE); - encoded_id = g_uri_escape_string (proxy->priv->client_id, NULL, TRUE); - - url = g_strdup_printf ("%s?client_id=%s&redirect_uri=%s&type=user_agent", - proxy->priv->auth_endpoint, encoded_id, - encoded_uri); - - g_free (encoded_uri); - g_free (encoded_id); - - if (params) { - char * full_url = g_strdup_printf ("%s&%s", url, params->str); - g_free (url); - url = full_url; - g_string_free (params, TRUE); - } - - return url; -} - -/** - * oauth2_proxy_build_login_url: - * @proxy: an OAuth2Proxy object - * @redirect_uri: the uri to redirect to after the user authenticates - * - * Builds a url at which the user can log in to the specified OAuth2-based web - * service. See the documentation for oauth2_proxy_build_login_url_full() for - * detailed information. - * - * Returns: a newly allocated uri string - */ -char * -oauth2_proxy_build_login_url (OAuth2Proxy *proxy, - const char* redirect_uri) -{ - return oauth2_proxy_build_login_url_full (proxy, redirect_uri, NULL); -} - -/** - * oauth2_proxy_get_access_token: - * @proxy: an #OAuth2Proxy - * - * Get the current request or access token. - * - * Returns: the token, or %NULL if there is no token yet. This string is owned - * by #OAuth2Proxy and should not be freed. - */ -const char * -oauth2_proxy_get_access_token (OAuth2Proxy *proxy) -{ - return proxy->priv->access_token; -} - -/** - * oauth2_proxy_set_access_token: - * @proxy: an #OAuth2Proxy - * @access_token: the access token - * - * Set the access token. - */ -void -oauth2_proxy_set_access_token (OAuth2Proxy *proxy, const char *access_token) -{ - g_return_if_fail (OAUTH2_IS_PROXY (proxy)); - - if (proxy->priv->access_token) - g_free (proxy->priv->access_token); - - proxy->priv->access_token = g_strdup (access_token); -} - -/** - * oauth2_proxy_extract_access_token: - * @url: the url which contains an access token in its fragment - * - * A utility function to extract the access token from the url that results from - * the redirection after the user authenticates - */ -char * -oauth2_proxy_extract_access_token (const char *url) -{ - GHashTable *params; - char *token = NULL; - const char *fragment; - GUri *uri = g_uri_parse (url, G_URI_FLAGS_ENCODED, NULL); - - fragment = g_uri_get_fragment (uri); - if (fragment != NULL) { - params = soup_form_decode (fragment); - - if (params) { - char *encoded = g_hash_table_lookup (params, "access_token"); - if (encoded) - token = g_uri_unescape_string (encoded, NULL); - - g_hash_table_destroy (params); - } - } - g_uri_unref (uri); - - return token; -} diff --git a/rest/oauth2-proxy.h b/rest/oauth2-proxy.h deleted file mode 100644 index 0473c62..0000000 --- a/rest/oauth2-proxy.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * librest - RESTful web services access - * Copyright (c) 2008, 2009, 2010 Intel Corporation. - * - * Authors: Rob Bradford <rob@linux.intel.com> - * Ross Burton <ross@linux.intel.com> - * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU Lesser General Public License, - * version 2.1, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - * more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#ifndef _OAUTH2_PROXY -#define _OAUTH2_PROXY - -#include <rest/rest-proxy.h> - -G_BEGIN_DECLS - -#define OAUTH2_TYPE_PROXY oauth2_proxy_get_type() - -#define OAUTH2_PROXY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), OAUTH2_TYPE_PROXY, OAuth2Proxy)) - -#define OAUTH2_PROXY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), OAUTH2_TYPE_PROXY, OAuth2ProxyClass)) - -#define OAUTH2_IS_PROXY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OAUTH2_TYPE_PROXY)) - -#define OAUTH2_IS_PROXY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), OAUTH2_TYPE_PROXY)) - -#define OAUTH2_PROXY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), OAUTH2_TYPE_PROXY, OAuth2ProxyClass)) - -typedef struct _OAuth2ProxyPrivate OAuth2ProxyPrivate; - -/** - * OAuth2Proxy: - * - * #OAuth2Proxy has no publicly available members. - */ -typedef struct { - RestProxy parent; - OAuth2ProxyPrivate *priv; -} OAuth2Proxy; - -typedef struct { - RestProxyClass parent_class; - /*< private >*/ - /* padding for future expansion */ - gpointer _padding_dummy[8]; -} OAuth2ProxyClass; - -GType oauth2_proxy_get_type (void); - -RestProxy* oauth2_proxy_new (const char *client_id, - const char *auth_endpoint, - const gchar *url_format, - gboolean binding_required); - -RestProxy* oauth2_proxy_new_with_token (const char *client_id, - const char *access_token, - const char *auth_endpoint, - const gchar *url_format, - gboolean binding_required); - -char * oauth2_proxy_build_login_url_full (OAuth2Proxy *proxy, - const char* redirect_uri, - GHashTable* extra_params); - -char * oauth2_proxy_build_login_url (OAuth2Proxy *proxy, - const char* redirect_uri); - -const char * oauth2_proxy_get_access_token (OAuth2Proxy *proxy); - -void oauth2_proxy_set_access_token (OAuth2Proxy *proxy, const char *access_token); - -char * oauth2_proxy_extract_access_token (const char *url); - -G_END_DECLS - -#endif /* _OAUTH2_PROXY */ diff --git a/rest/rest-oauth2-proxy-call.c b/rest/rest-oauth2-proxy-call.c new file mode 100644 index 0000000..88840b3 --- /dev/null +++ b/rest/rest-oauth2-proxy-call.c @@ -0,0 +1,62 @@ +/* rest-oauth2-proxy-call.c + * + * Copyright 2021-2022 Günther Wagner <info@gunibert.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "rest-oauth2-proxy-call.h" + +G_DEFINE_TYPE (RestOAuth2ProxyCall, rest_oauth2_proxy_call, REST_TYPE_PROXY_CALL) + +static gboolean +rest_oauth2_proxy_call_prepare (RestProxyCall *call, + GError **error) +{ + RestOAuth2Proxy *proxy = NULL; + g_autoptr(GDateTime) now = NULL; + GDateTime *expiration_date = NULL; + + g_return_val_if_fail (REST_IS_OAUTH2_PROXY_CALL (call), FALSE); + + g_object_get (call, "proxy", &proxy, NULL); + + now = g_date_time_new_now_local (); + expiration_date = rest_oauth2_proxy_get_expiration_date (proxy); + + // access token expired -> refresh + if (g_date_time_compare (now, expiration_date) > 0) + { + g_set_error (error, + REST_OAUTH2_ERROR, + REST_OAUTH2_ERROR_ACCESS_TOKEN_EXPIRED, + "Access token is expired"); + return FALSE; + } + + return TRUE; +} + +static void +rest_oauth2_proxy_call_class_init (RestOAuth2ProxyCallClass *klass) +{ + RestProxyCallClass *call_class = REST_PROXY_CALL_CLASS (klass); + + call_class->prepare = rest_oauth2_proxy_call_prepare; +} + +static void +rest_oauth2_proxy_call_init (RestOAuth2ProxyCall *self) +{ +} diff --git a/rest-extras/lastfm-proxy-private.h b/rest/rest-oauth2-proxy-call.h index 128f7a9..fafc61f 100644 --- a/rest-extras/lastfm-proxy-private.h +++ b/rest/rest-oauth2-proxy-call.h @@ -1,9 +1,6 @@ -/* - * librest - RESTful web services access - * Copyright (c) 2010 Intel Corporation. +/* rest-oauth2-proxy-call.h * - * Authors: Rob Bradford <rob@linux.intel.com> - * Ross Burton <ross@linux.intel.com> + * Copyright 2021 Günther Wagner <info@gunibert.de> * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, @@ -17,17 +14,20 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ -#include "lastfm-proxy.h" +#pragma once + +#include <rest.h> + +G_BEGIN_DECLS + +#define REST_TYPE_OAUTH2_PROXY_CALL (rest_oauth2_proxy_call_get_type()) -#define LASTFM_PROXY_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), LASTFM_TYPE_PROXY, LastfmProxyPrivate)) +G_DECLARE_DERIVABLE_TYPE (RestOAuth2ProxyCall, rest_oauth2_proxy_call, REST, OAUTH2_PROXY_CALL, RestProxyCall) -struct _LastfmProxyPrivate { - char *api_key; - char *secret; - char *session_key; +struct _RestOAuth2ProxyCallClass { + RestProxyCallClass parent_class; }; +G_END_DECLS diff --git a/rest/rest-oauth2-proxy.c b/rest/rest-oauth2-proxy.c new file mode 100644 index 0000000..f15b589 --- /dev/null +++ b/rest/rest-oauth2-proxy.c @@ -0,0 +1,802 @@ +/* rest-oauth2-proxy.c + * + * Copyright 2021 Günther Wagner <info@gunibert.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "rest-oauth2-proxy.h" +#include "rest-oauth2-proxy-call.h" +#include "rest-utils.h" +#include "rest-private.h" +#include <json-glib/json-glib.h> + +typedef struct +{ + gchar *authurl; + gchar *tokenurl; + gchar *redirect_uri; + gchar *client_id; + gchar *client_secret; + + gchar *access_token; + gchar *refresh_token; + + GDateTime *expiration_date; +} RestOAuth2ProxyPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (RestOAuth2Proxy, rest_oauth2_proxy, REST_TYPE_PROXY) + +G_DEFINE_QUARK (rest-oauth2-error-quark, rest_oauth2_error) + +enum { + PROP_0, + PROP_AUTH_URL, + PROP_TOKEN_URL, + PROP_REDIRECT_URI, + PROP_CLIENT_ID, + PROP_CLIENT_SECRET, + PROP_ACCESS_TOKEN, + PROP_REFRESH_TOKEN, + PROP_EXPIRATION_DATE, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +static void +rest_oauth2_proxy_parse_access_token (RestOAuth2Proxy *self, + GBytes *payload, + GTask *task) +{ + g_autoptr(JsonParser) parser = NULL; + g_autoptr(GError) error = NULL; + JsonNode *root; + JsonObject *root_object; + const gchar *data; + gsize size; + gint expires_in; + gint created_at; + + g_return_if_fail (REST_IS_OAUTH2_PROXY (self)); + + data = g_bytes_get_data (payload, &size); + + parser = json_parser_new (); + json_parser_load_from_data (parser, data, size, &error); + if (error != NULL) + { + g_task_return_error (task, error); + return; + } + + root = json_parser_get_root (parser); + root_object = json_node_get_object (root); + + if (json_object_has_member (root_object, "access_token")) + rest_oauth2_proxy_set_access_token (self, json_object_get_string_member (root_object, "access_token")); + if (json_object_has_member (root_object, "refresh_token")) + rest_oauth2_proxy_set_refresh_token (self, json_object_get_string_member (root_object, "refresh_token")); + + if (json_object_has_member (root_object, "expires_in") && json_object_has_member (root_object, "created_at")) + { + expires_in = json_object_get_int_member (root_object, "expires_in"); + created_at = json_object_get_int_member (root_object, "created_at"); + + rest_oauth2_proxy_set_expiration_date (self, g_date_time_new_from_unix_local (created_at+expires_in)); + } + else if (json_object_has_member (root_object, "expires_in")) + { + g_autoptr(GDateTime) now = g_date_time_new_now_utc (); + expires_in = json_object_get_int_member (root_object, "expires_in"); + rest_oauth2_proxy_set_expiration_date (self, g_date_time_add_seconds (now, expires_in)); + } + + g_task_return_boolean (task, TRUE); +} + +RestProxyCall * +rest_oauth2_proxy_new_call (RestProxy *proxy) +{ + RestOAuth2Proxy *self = (RestOAuth2Proxy *)proxy; + RestProxyCall *call; + g_autofree gchar *auth; + + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL); + + auth = g_strdup_printf ("Bearer %s", rest_oauth2_proxy_get_access_token (self)); + + call = g_object_new (REST_TYPE_OAUTH2_PROXY_CALL, "proxy", proxy, NULL); + rest_proxy_call_add_header (call, "Authorization", auth); + + return call; +} + +/** + * rest_oauth2_proxy_new: + * + * Create a new #RestOAuth2Proxy. + * + * Returns: (transfer full): a newly created #RestOAuth2Proxy + */ +RestOAuth2Proxy * +rest_oauth2_proxy_new (const gchar *authurl, + const gchar *tokenurl, + const gchar *redirecturl, + const gchar *client_id, + const gchar *client_secret, + const gchar *baseurl) +{ + return g_object_new (REST_TYPE_OAUTH2_PROXY, + "url-format", baseurl, + "auth-url", authurl, + "token-url", tokenurl, + "redirect-uri", redirecturl, + "client-id", client_id, + "client-secret", client_secret, + NULL); +} + +static void +rest_oauth2_proxy_finalize (GObject *object) +{ + RestOAuth2Proxy *self = (RestOAuth2Proxy *)object; + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_clear_pointer (&priv->authurl, g_free); + g_clear_pointer (&priv->tokenurl, g_free); + g_clear_pointer (&priv->redirect_uri, g_free); + g_clear_pointer (&priv->client_id, g_free); + g_clear_pointer (&priv->client_secret, g_free); + g_clear_pointer (&priv->access_token, g_free); + g_clear_pointer (&priv->refresh_token, g_free); + g_clear_pointer (&priv->expiration_date, g_date_time_unref); + + G_OBJECT_CLASS (rest_oauth2_proxy_parent_class)->finalize (object); +} + +static void +rest_oauth2_proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + RestOAuth2Proxy *self = REST_OAUTH2_PROXY (object); + + switch (prop_id) + { + case PROP_AUTH_URL: + g_value_set_string (value, rest_oauth2_proxy_get_auth_url (self)); + break; + case PROP_TOKEN_URL: + g_value_set_string (value, rest_oauth2_proxy_get_token_url (self)); + break; + case PROP_REDIRECT_URI: + g_value_set_string (value, rest_oauth2_proxy_get_redirect_uri (self)); + break; + case PROP_CLIENT_ID: + g_value_set_string (value, rest_oauth2_proxy_get_client_id (self)); + break; + case PROP_CLIENT_SECRET: + g_value_set_string (value, rest_oauth2_proxy_get_client_secret (self)); + break; + case PROP_ACCESS_TOKEN: + g_value_set_string (value, rest_oauth2_proxy_get_access_token (self)); + break; + case PROP_REFRESH_TOKEN: + g_value_set_string (value, rest_oauth2_proxy_get_refresh_token (self)); + break; + case PROP_EXPIRATION_DATE: + g_value_set_boxed (value, rest_oauth2_proxy_get_expiration_date (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +rest_oauth2_proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + RestOAuth2Proxy *self = REST_OAUTH2_PROXY (object); + + switch (prop_id) + { + case PROP_AUTH_URL: + rest_oauth2_proxy_set_auth_url (self, g_value_get_string (value)); + break; + case PROP_TOKEN_URL: + rest_oauth2_proxy_set_token_url (self, g_value_get_string (value)); + break; + case PROP_REDIRECT_URI: + rest_oauth2_proxy_set_redirect_uri (self, g_value_get_string (value)); + break; + case PROP_CLIENT_ID: + rest_oauth2_proxy_set_client_id (self, g_value_get_string (value)); + break; + case PROP_CLIENT_SECRET: + rest_oauth2_proxy_set_client_secret (self, g_value_get_string (value)); + break; + case PROP_ACCESS_TOKEN: + rest_oauth2_proxy_set_access_token (self, g_value_get_string (value)); + break; + case PROP_REFRESH_TOKEN: + rest_oauth2_proxy_set_refresh_token (self, g_value_get_string (value)); + break; + case PROP_EXPIRATION_DATE: + rest_oauth2_proxy_set_expiration_date (self, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +rest_oauth2_proxy_class_init (RestOAuth2ProxyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + RestOAuth2ProxyClass *oauth2_class = REST_OAUTH2_PROXY_CLASS (klass); + RestProxyClass *proxy_class = REST_PROXY_CLASS (klass); + + object_class->finalize = rest_oauth2_proxy_finalize; + object_class->get_property = rest_oauth2_proxy_get_property; + object_class->set_property = rest_oauth2_proxy_set_property; + oauth2_class->parse_access_token = rest_oauth2_proxy_parse_access_token; + proxy_class->new_call = rest_oauth2_proxy_new_call; + + properties [PROP_AUTH_URL] = + g_param_spec_string ("auth-url", + "AuthUrl", + "AuthUrl", + "", + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_TOKEN_URL] = + g_param_spec_string ("token-url", + "TokenUrl", + "TokenUrl", + "", + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_REDIRECT_URI] = + g_param_spec_string ("redirect-uri", + "RedirectUri", + "RedirectUri", + "", + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_CLIENT_ID] = + g_param_spec_string ("client-id", + "ClientId", + "ClientId", + "", + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_CLIENT_SECRET] = + g_param_spec_string ("client-secret", + "ClientSecret", + "ClientSecret", + "", + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_ACCESS_TOKEN] = + g_param_spec_string ("access-token", + "AccessToken", + "AccessToken", + NULL, + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_REFRESH_TOKEN] = + g_param_spec_string ("refresh-token", + "RefreshToken", + "RefreshToken", + NULL, + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_EXPIRATION_DATE] = + g_param_spec_boxed ("expiration-date", + "ExpirationDate", + "ExpirationDate", + G_TYPE_DATE_TIME, + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +rest_oauth2_proxy_init (RestOAuth2Proxy *self) +{ +} + +/** + * rest_oauth2_proxy_build_authorization_url: + * @self: a #RestOAuth2Proxy + * @code_challenge: the code challenge (see #RestPkceCodeChallenge) + * @scope: (nullable): the requesting scope of the resource + * @state: (out): a CRSF token which should be verified from the redirect_uri + * + * + * Returns: (transfer full): the authorization url which should be shown in a WebView in order to accept/decline the request + * to authorize the application + * + * Since: 0.8 + */ +gchar * +rest_oauth2_proxy_build_authorization_url (RestOAuth2Proxy *self, + const gchar *code_challenge, + const gchar *scope, + gchar **state) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + g_autoptr(GHashTable) params = NULL; + g_autoptr(GUri) auth = NULL; + g_autoptr(GUri) authorization_url = NULL; + g_autofree gchar *params_string; + + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL); + + if (state != NULL) + *state = random_string (10); + params = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (params, "response_type", "code"); + g_hash_table_insert (params, "client_id", priv->client_id); + g_hash_table_insert (params, "redirect_uri", priv->redirect_uri); + if (state != NULL) + g_hash_table_insert (params, "state", *state); + g_hash_table_insert (params, "code_challenge", (gchar *)code_challenge); + g_hash_table_insert (params, "code_challenge_method", "S256"); + if (scope) + g_hash_table_insert (params, "scope", (gchar *)scope); + + params_string = soup_form_encode_hash (params); + auth = g_uri_parse (priv->authurl, G_URI_FLAGS_NONE, NULL); + authorization_url = g_uri_build (G_URI_FLAGS_ENCODED, + g_uri_get_scheme (auth), + NULL, + g_uri_get_host (auth), + g_uri_get_port (auth), + g_uri_get_path (auth), + params_string, + NULL); + return g_uri_to_string (authorization_url); +} + +static void +rest_oauth2_proxy_fetch_access_token_cb (SoupMessage *msg, + GBytes *body, + GError *error, + gpointer user_data) +{ + g_autoptr(GTask) task = user_data; + RestOAuth2Proxy *self; + + g_assert (G_IS_TASK (task)); + + self = g_task_get_source_object (task); + + if (error) + { + g_task_return_error (task, error); + return; + } + + REST_OAUTH2_PROXY_GET_CLASS (self)->parse_access_token (self, body, g_steal_pointer (&task)); +} + +void +rest_oauth2_proxy_fetch_access_token_async (RestOAuth2Proxy *self, + const gchar *authorization_code, + const gchar *code_verifier, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + g_autoptr(SoupMessage) msg = NULL; + g_autoptr(GTask) task = NULL; + g_autoptr(GHashTable) params = NULL; + + g_return_if_fail (REST_IS_OAUTH2_PROXY (self)); + g_return_if_fail (authorization_code != NULL); + + task = g_task_new (self, cancellable, callback, user_data); + params = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (params, "client_id", priv->client_id); + g_hash_table_insert (params, "grant_type", "authorization_code"); + g_hash_table_insert (params, "code", (gchar *)authorization_code); + g_hash_table_insert (params, "redirect_uri", priv->redirect_uri); + g_hash_table_insert (params, "code_verifier", (gchar *)code_verifier); + +#if WITH_SOUP_2 + msg = soup_form_request_new_from_hash (SOUP_METHOD_POST, priv->tokenurl, params); +#else + msg = soup_message_new_from_encoded_form (SOUP_METHOD_POST, priv->tokenurl, soup_form_encode_hash (params)); +#endif + + _rest_proxy_queue_message (REST_PROXY (self), +#if WITH_SOUP_2 + g_steal_pointer (&msg), +#else + msg, +#endif + cancellable, rest_oauth2_proxy_fetch_access_token_cb, g_steal_pointer (&task)); + +} + +/** + * rest_oauth2_proxy_fetch_access_token_finish: + * @self: an #RestOauth2Proxy + * @result: a #GAsyncResult provided to callback + * @error: a location for a #GError, or %NULL + * + * Returns: + */ +gboolean +rest_oauth2_proxy_fetch_access_token_finish (RestOAuth2Proxy *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), FALSE); + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +gboolean +rest_oauth2_proxy_refresh_access_token (RestOAuth2Proxy *self, + GError **error) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + g_autoptr(SoupMessage) msg = NULL; + g_autoptr(GHashTable) params = NULL; + g_autoptr(GTask) task = NULL; + GBytes *payload; + + task = g_task_new (self, NULL, NULL, NULL); + + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), FALSE); + + if (priv->refresh_token == NULL) + { + *error = g_error_new (REST_OAUTH2_ERROR, + REST_OAUTH2_ERROR_NO_REFRESH_TOKEN, + "No refresh token available"); + return FALSE; + } + + params = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (params, "client_id", priv->client_id); + g_hash_table_insert (params, "refresh_token", priv->refresh_token); + g_hash_table_insert (params, "redirect_uri", priv->redirect_uri); + g_hash_table_insert (params, "grant_type", "refresh_token"); + +#if WITH_SOUP_2 + msg = soup_form_request_new_from_hash (SOUP_METHOD_POST, priv->tokenurl, params); +#else + msg = soup_message_new_from_encoded_form (SOUP_METHOD_POST, priv->tokenurl, soup_form_encode_hash (params)); +#endif + payload = _rest_proxy_send_message (REST_PROXY (self), msg, NULL, error); + if (error && *error) + { + return FALSE; + } + + REST_OAUTH2_PROXY_GET_CLASS (self)->parse_access_token (self, payload, g_steal_pointer (&task)); + return TRUE; +} + +static void +rest_oauth2_proxy_refresh_access_token_cb (SoupMessage *msg, + GBytes *payload, + GError *error, + gpointer user_data) +{ + g_autoptr(GTask) task = user_data; + RestOAuth2Proxy *self; + + g_assert (G_IS_TASK (task)); + + self = g_task_get_source_object (task); + + if (error) + { + g_task_return_error (task, error); + return; + } + + REST_OAUTH2_PROXY_GET_CLASS (self)->parse_access_token (self, payload, g_steal_pointer (&task)); +} + +void +rest_oauth2_proxy_refresh_access_token_async (RestOAuth2Proxy *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + g_autoptr(SoupMessage) msg = NULL; + g_autoptr(GHashTable) params = NULL; + g_autoptr(GTask) task = NULL; + + task = g_task_new (self, cancellable, callback, user_data); + + g_return_if_fail (REST_IS_OAUTH2_PROXY (self)); + + if (priv->refresh_token == NULL) + { + g_task_return_new_error (task, + REST_OAUTH2_ERROR, + REST_OAUTH2_ERROR_NO_REFRESH_TOKEN, + "No refresh token available"); + return; + } + + params = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (params, "client_id", priv->client_id); + g_hash_table_insert (params, "refresh_token", priv->refresh_token); + g_hash_table_insert (params, "redirect_uri", priv->redirect_uri); + g_hash_table_insert (params, "grant_type", "refresh_token"); + +#if WITH_SOUP_2 + msg = soup_form_request_new_from_hash (SOUP_METHOD_POST, priv->tokenurl, params); +#else + msg = soup_message_new_from_encoded_form (SOUP_METHOD_POST, priv->tokenurl, soup_form_encode_hash (params)); +#endif + _rest_proxy_queue_message (REST_PROXY (self), +#if WITH_SOUP_2 + g_steal_pointer (&msg), +#else + msg, +#endif + cancellable, + rest_oauth2_proxy_refresh_access_token_cb, + g_steal_pointer (&task)); +} + +/** + * rest_oauth2_proxy_refresh_access_token_finish: + * @self: an #RestOauth2Proxy + * @result: a #GAsyncResult provided to callback + * @error: a location for a #GError, or %NULL + * + * Returns: + */ +gboolean +rest_oauth2_proxy_refresh_access_token_finish (RestOAuth2Proxy *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), FALSE); + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +const gchar * +rest_oauth2_proxy_get_auth_url (RestOAuth2Proxy *self) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL); + + return priv->authurl; +} + +void +rest_oauth2_proxy_set_auth_url (RestOAuth2Proxy *self, + const gchar *authurl) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_if_fail (REST_IS_OAUTH2_PROXY (self)); + + if (g_strcmp0 (priv->authurl, authurl) != 0) + { + g_clear_pointer (&priv->authurl, g_free); + priv->authurl = g_strdup (authurl); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_AUTH_URL]); + } +} + +const gchar * +rest_oauth2_proxy_get_token_url (RestOAuth2Proxy *self) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL); + + return priv->tokenurl; +} + +void +rest_oauth2_proxy_set_token_url (RestOAuth2Proxy *self, + const gchar *tokenurl) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_if_fail (REST_IS_OAUTH2_PROXY (self)); + + if (g_strcmp0 (priv->tokenurl, tokenurl) != 0) + { + g_clear_pointer (&priv->tokenurl, g_free); + priv->tokenurl = g_strdup (tokenurl); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TOKEN_URL]); + } +} + +const gchar * +rest_oauth2_proxy_get_redirect_uri (RestOAuth2Proxy *self) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL); + + return priv->redirect_uri; +} + +void +rest_oauth2_proxy_set_redirect_uri (RestOAuth2Proxy *self, + const gchar *redirect_uri) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_if_fail (REST_IS_OAUTH2_PROXY (self)); + + if (g_strcmp0 (priv->redirect_uri, redirect_uri) != 0) + { + g_clear_pointer (&priv->redirect_uri, g_free); + priv->redirect_uri = g_strdup (redirect_uri); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_REDIRECT_URI]); + } +} + +const gchar * +rest_oauth2_proxy_get_client_id (RestOAuth2Proxy *self) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL); + + return priv->client_id; +} + +void +rest_oauth2_proxy_set_client_id (RestOAuth2Proxy *self, + const gchar *client_id) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_if_fail (REST_IS_OAUTH2_PROXY (self)); + + if (g_strcmp0 (priv->client_id, client_id) != 0) + { + g_clear_pointer (&priv->client_id, g_free); + priv->client_id = g_strdup (client_id); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CLIENT_ID]); + } +} + +const gchar * +rest_oauth2_proxy_get_client_secret (RestOAuth2Proxy *self) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL); + + return priv->client_secret; +} + +void +rest_oauth2_proxy_set_client_secret (RestOAuth2Proxy *self, + const gchar *client_secret) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_if_fail (REST_IS_OAUTH2_PROXY (self)); + + if (g_strcmp0 (priv->client_secret, client_secret) != 0) + { + g_clear_pointer (&priv->client_secret, g_free); + priv->client_secret = g_strdup (client_secret); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CLIENT_SECRET]); + } +} + +const gchar * +rest_oauth2_proxy_get_access_token (RestOAuth2Proxy *self) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL); + + return priv->access_token; +} + +void +rest_oauth2_proxy_set_access_token (RestOAuth2Proxy *self, + const gchar *access_token) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_if_fail (REST_IS_OAUTH2_PROXY (self)); + + if (g_strcmp0 (priv->access_token, access_token) != 0) + { + g_clear_pointer (&priv->access_token, g_free); + priv->access_token = g_strdup (access_token); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACCESS_TOKEN]); + } +} + +const gchar * +rest_oauth2_proxy_get_refresh_token (RestOAuth2Proxy *self) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL); + + return priv->refresh_token; +} + +void +rest_oauth2_proxy_set_refresh_token (RestOAuth2Proxy *self, + const gchar *refresh_token) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_if_fail (REST_IS_OAUTH2_PROXY (self)); + + if (g_strcmp0 (priv->refresh_token, refresh_token) != 0) + { + g_clear_pointer (&priv->refresh_token, g_free); + priv->refresh_token = g_strdup (refresh_token); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_REFRESH_TOKEN]); + } +} + +GDateTime * +rest_oauth2_proxy_get_expiration_date (RestOAuth2Proxy *self) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL); + + return priv->expiration_date; +} + +void +rest_oauth2_proxy_set_expiration_date (RestOAuth2Proxy *self, + GDateTime *expiration_date) +{ + RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self); + + g_return_if_fail (REST_IS_OAUTH2_PROXY (self)); + + g_clear_pointer (&priv->expiration_date, g_date_time_unref); + priv->expiration_date = g_date_time_ref (expiration_date); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EXPIRATION_DATE]); +} diff --git a/rest/rest-oauth2-proxy.h b/rest/rest-oauth2-proxy.h new file mode 100644 index 0000000..dd4148e --- /dev/null +++ b/rest/rest-oauth2-proxy.h @@ -0,0 +1,101 @@ +/* rest-oauth2-proxy.h + * + * Copyright 2021 Günther Wagner <info@gunibert.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include <rest/rest-proxy.h> + +G_BEGIN_DECLS + +#define REST_TYPE_OAUTH2_PROXY (rest_oauth2_proxy_get_type()) + +G_DECLARE_DERIVABLE_TYPE (RestOAuth2Proxy, rest_oauth2_proxy, REST, OAUTH2_PROXY, RestProxy) + +struct _RestOAuth2ProxyClass +{ + RestProxyClass parent_class; + + void (*parse_access_token) (RestOAuth2Proxy *self, + GBytes *payload, + GTask *task); + + gpointer padding[8]; +}; + +enum { + REST_OAUTH2_ERROR_NO_REFRESH_TOKEN, + REST_OAUTH2_ERROR_ACCESS_TOKEN_EXPIRED, +}; + +#define REST_OAUTH2_ERROR rest_oauth2_error_quark () +GQuark rest_oauth2_error_quark (); + +RestOAuth2Proxy *rest_oauth2_proxy_new (const gchar *authurl, + const gchar *tokenurl, + const gchar *redirecturl, + const gchar *client_id, + const gchar *client_secret, + const gchar *baseurl); +gchar *rest_oauth2_proxy_build_authorization_url (RestOAuth2Proxy *self, + const gchar *code_challenge, + const gchar *scope, + gchar **state); +void rest_oauth2_proxy_fetch_access_token_async (RestOAuth2Proxy *self, + const gchar *authorization_code, + const gchar *code_verifier, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean rest_oauth2_proxy_fetch_access_token_finish (RestOAuth2Proxy *self, + GAsyncResult *result, + GError **error); +gboolean rest_oauth2_proxy_refresh_access_token (RestOAuth2Proxy *self, + GError **error); +void rest_oauth2_proxy_refresh_access_token_async (RestOAuth2Proxy *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean rest_oauth2_proxy_refresh_access_token_finish (RestOAuth2Proxy *self, + GAsyncResult *result, + GError **error); +const gchar *rest_oauth2_proxy_get_auth_url (RestOAuth2Proxy *self); +void rest_oauth2_proxy_set_auth_url (RestOAuth2Proxy *self, + const gchar *tokenurl); +const gchar *rest_oauth2_proxy_get_token_url (RestOAuth2Proxy *self); +void rest_oauth2_proxy_set_token_url (RestOAuth2Proxy *self, + const gchar *tokenurl); +const gchar *rest_oauth2_proxy_get_redirect_uri (RestOAuth2Proxy *self); +void rest_oauth2_proxy_set_redirect_uri (RestOAuth2Proxy *self, + const gchar *redirect_uri); +const gchar *rest_oauth2_proxy_get_client_id (RestOAuth2Proxy *self); +void rest_oauth2_proxy_set_client_id (RestOAuth2Proxy *self, + const gchar *client_id); +const gchar *rest_oauth2_proxy_get_client_secret (RestOAuth2Proxy *self); +void rest_oauth2_proxy_set_client_secret (RestOAuth2Proxy *self, + const gchar *client_secret); +const gchar *rest_oauth2_proxy_get_access_token (RestOAuth2Proxy *self); +void rest_oauth2_proxy_set_access_token (RestOAuth2Proxy *self, + const gchar *access_token); +const gchar *rest_oauth2_proxy_get_refresh_token (RestOAuth2Proxy *self); +void rest_oauth2_proxy_set_refresh_token (RestOAuth2Proxy *self, + const gchar *refresh_token); +GDateTime *rest_oauth2_proxy_get_expiration_date (RestOAuth2Proxy *self); +void rest_oauth2_proxy_set_expiration_date (RestOAuth2Proxy *self, + GDateTime *expiration_date); + +G_END_DECLS diff --git a/rest/rest-param.c b/rest/rest-param.c index 8ad105f..1f6f73c 100644 --- a/rest/rest-param.c +++ b/rest/rest-param.c @@ -89,7 +89,7 @@ rest_param_new_full (const char *name, param = g_slice_new0 (RestParam); if (use == REST_MEMORY_COPY) { - data = g_memdup (data, length); + data = g_memdup2 (data, length); use = REST_MEMORY_TAKE; } diff --git a/rest/rest-params.c b/rest/rest-params.c index f246cc2..f2ac286 100644 --- a/rest/rest-params.c +++ b/rest/rest-params.c @@ -30,18 +30,7 @@ * @see_also: #RestParam, #RestProxyCall. */ -/* - * RestParams is an alias for GHashTable achieved by opaque types in the public - * headers and casting internally. This has several limitations, mainly - * supporting multiple parameters with the same name and preserving the ordering - * of parameters. - * - * These are not requirements for the bulk of the web services, but this - * limitation does mean librest can't be used for a few web services. - * - * TODO: this should be a list to support multiple parameters with the same - * name. - */ +G_DEFINE_BOXED_TYPE (RestParams, rest_params, rest_params_ref, rest_params_unref) /** * rest_params_new: @@ -53,11 +42,13 @@ RestParams * rest_params_new (void) { - /* The key is a string that is owned by the RestParam, so we don't need to - explicitly free it on removal. */ - return (RestParams *) - g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, (GDestroyNotify)rest_param_unref); + RestParams *self; + + self = g_slice_new0 (RestParams); + self->ref_count = 1; + self->params = NULL; + + return self; } /** @@ -67,13 +58,73 @@ rest_params_new (void) * Destroy the #RestParams and the #RestParam objects that it contains. **/ void -rest_params_free (RestParams *params) +rest_params_free (RestParams *self) { - GHashTable *hash = (GHashTable *)params; + g_assert (self); + g_assert_cmpint (self->ref_count, ==, 0); - g_return_if_fail (params); + g_list_free_full (g_steal_pointer (&self->params), (GDestroyNotify) rest_param_unref); + + g_slice_free (RestParams, self); +} + +/** + * rest_params_copy: + * @self: a #RestParams + * + * Makes a deep copy of a #RestParams. + * + * Returns: (transfer full): A newly created #RestParams with the same + * contents as @self + */ +RestParams * +rest_params_copy (RestParams *self) +{ + RestParams *copy; + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (self->ref_count, NULL); + + copy = rest_params_new (); + copy->params = g_list_copy_deep (self->params, (GCopyFunc) rest_param_ref, NULL); + + return copy; +} + +/** + * rest_params_ref: + * @self: A #RestParams + * + * Increments the reference count of @self by one. + * + * Returns: (transfer full): @self + */ +RestParams * +rest_params_ref (RestParams *self) +{ + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (self->ref_count, NULL); + + g_atomic_int_inc (&self->ref_count); - g_hash_table_destroy (hash); + return self; +} + +/** + * rest_params_unref: + * @self: A #RestParams + * + * Decrements the reference count of @self by one, freeing the structure when + * the reference count reaches zero. + */ +void +rest_params_unref (RestParams *self) +{ + g_return_if_fail (self); + g_return_if_fail (self->ref_count); + + if (g_atomic_int_dec_and_test (&self->ref_count)) + rest_params_free (self); } /** @@ -84,14 +135,25 @@ rest_params_free (RestParams *params) * Add @param to @params. **/ void -rest_params_add (RestParams *params, RestParam *param) +rest_params_add (RestParams *self, + RestParam *param) { - GHashTable *hash = (GHashTable *)params; - - g_return_if_fail (params); + g_return_if_fail (self); g_return_if_fail (param); - g_hash_table_replace (hash, (gpointer)rest_param_get_name (param), param); + self->params = g_list_append (self->params, param); +} + +static gint +rest_params_find (gconstpointer self, + gconstpointer name) +{ + const RestParam *e = self; + const char *n = name; + const char *n2 = rest_param_get_name ((RestParam *)e); + + if (g_strcmp0 (n2, n) == 0) return 0; + return -1; } /** @@ -105,14 +167,13 @@ rest_params_add (RestParams *params, RestParam *param) * doesn't exist **/ RestParam * -rest_params_get (RestParams *params, const char *name) +rest_params_get (RestParams *self, + const char *name) { - GHashTable *hash = (GHashTable *)params; - - g_return_val_if_fail (params, NULL); + g_return_val_if_fail (self, NULL); g_return_val_if_fail (name, NULL); - return g_hash_table_lookup (hash, name); + return g_list_find_custom (self->params, name, rest_params_find)->data; } /** @@ -123,14 +184,16 @@ rest_params_get (RestParams *params, const char *name) * Remove the #RestParam called @name. **/ void -rest_params_remove (RestParams *params, const char *name) +rest_params_remove (RestParams *self, + const char *name) { - GHashTable *hash = (GHashTable *)params; + GList *elem = NULL; - g_return_if_fail (params); + g_return_if_fail (self); g_return_if_fail (name); - g_hash_table_remove (hash, name); + elem = g_list_find_custom (self->params, name, rest_params_find); + self->params = g_list_remove (self->params, elem->data); } /** @@ -143,27 +206,22 @@ rest_params_remove (RestParams *params, const char *name) * Returns: %TRUE if all of the parameters are simple strings, %FALSE otherwise. **/ gboolean -rest_params_are_strings (RestParams *params) +rest_params_are_strings (RestParams *self) { - GHashTable *hash = (GHashTable *)params; - GHashTableIter iter; - RestParam *param; - - g_return_val_if_fail (params, FALSE); + g_return_val_if_fail (self, FALSE); - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, NULL, (gpointer)¶m)) { - if (!rest_param_is_string (param)) - return FALSE; - } + for (GList *cur = self->params; cur; cur = g_list_next (cur)) + { + if (!rest_param_is_string (cur->data)) + return FALSE; + } return TRUE; - } /** * rest_params_as_string_hash_table: - * @params: a valid #RestParams + * @self: a valid #RestParams * * Create a new #GHashTable which contains the name and value of all string * (content type of text/plain) parameters. @@ -174,23 +232,19 @@ rest_params_are_strings (RestParams *params) * Returns: (element-type utf8 Rest.Param) (transfer container): a new #GHashTable. **/ GHashTable * -rest_params_as_string_hash_table (RestParams *params) +rest_params_as_string_hash_table (RestParams *self) { - GHashTable *hash, *strings; - GHashTableIter iter; - const char *name = NULL; - RestParam *param = NULL; + GHashTable *strings; - g_return_val_if_fail (params, NULL); + g_return_val_if_fail (self, NULL); - hash = (GHashTable *)params; strings = g_hash_table_new (g_str_hash, g_str_equal); - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, (gpointer)&name, (gpointer)¶m)) { - if (rest_param_is_string (param)) - g_hash_table_insert (strings, (gpointer)name, (gpointer)rest_param_get_content (param)); - } + for (GList *cur = self->params; cur; cur = g_list_next (cur)) + { + if (rest_param_is_string (cur->data)) + g_hash_table_insert (strings, (gpointer)rest_param_get_name (cur->data), (gpointer)rest_param_get_content (cur->data)); + } return strings; } @@ -214,12 +268,14 @@ rest_params_as_string_hash_table (RestParams *params) * ]| **/ void -rest_params_iter_init (RestParamsIter *iter, RestParams *params) +rest_params_iter_init (RestParamsIter *iter, + RestParams *params) { g_return_if_fail (iter); g_return_if_fail (params); - g_hash_table_iter_init ((GHashTableIter *)iter, (GHashTable *)params); + iter->params = params; + iter->position = -1; } /** @@ -235,9 +291,21 @@ rest_params_iter_init (RestParamsIter *iter, RestParams *params) * Returns: %FALSE if the end of the #RestParams has been reached, %TRUE otherwise. **/ gboolean -rest_params_iter_next (RestParamsIter *iter, const char **name, RestParam **param) +rest_params_iter_next (RestParamsIter *iter, + const char **name, + RestParam **param) { + GList *cur = NULL; + g_return_val_if_fail (iter, FALSE); - return g_hash_table_iter_next ((GHashTableIter *)iter, (gpointer)name, (gpointer)param); + iter->position++; + cur = g_list_nth (iter->params->params, iter->position); + + if (cur == NULL) return FALSE; + + *param = cur->data; + *name = rest_param_get_name (*param); + return TRUE; } + diff --git a/rest/rest-params.h b/rest/rest-params.h index caace9d..9f53f77 100644 --- a/rest/rest-params.h +++ b/rest/rest-params.h @@ -20,34 +20,52 @@ * */ -#ifndef _REST_PARAMS -#define _REST_PARAMS +#pragma once #include <glib-object.h> #include "rest-param.h" G_BEGIN_DECLS -typedef struct _RestParams RestParams; -typedef struct _GHashTableIter RestParamsIter; - -RestParams * rest_params_new (void); - -void rest_params_free (RestParams *params); +#define REST_TYPE_PARAMS (rest_params_get_type ()) -void rest_params_add (RestParams *params, RestParam *param); +typedef struct _RestParams RestParams; +typedef struct _RestParamsIter RestParamsIter; -RestParam *rest_params_get (RestParams *params, const char *name); +struct _RestParams +{ + /*< private >*/ + guint ref_count; -void rest_params_remove (RestParams *params, const char *name); + GList *params; +}; -gboolean rest_params_are_strings (RestParams *params); +struct _RestParamsIter +{ + /*< private >*/ + RestParams *params; + gint position; +}; -GHashTable * rest_params_as_string_hash_table (RestParams *params); +GType rest_params_get_type (void) G_GNUC_CONST; +RestParams *rest_params_new (void); +RestParams *rest_params_copy (RestParams *self); +RestParams *rest_params_ref (RestParams *self); +void rest_params_unref (RestParams *self); +void rest_params_add (RestParams *params, + RestParam *param); +RestParam *rest_params_get (RestParams *params, + const char *name); +void rest_params_remove (RestParams *params, + const char *name); +gboolean rest_params_are_strings (RestParams *params); +GHashTable *rest_params_as_string_hash_table (RestParams *params); +void rest_params_iter_init (RestParamsIter *iter, + RestParams *params); +gboolean rest_params_iter_next (RestParamsIter *iter, + const char **name, + RestParam **param); -void rest_params_iter_init (RestParamsIter *iter, RestParams *params); -gboolean rest_params_iter_next (RestParamsIter *iter, const char **name, RestParam **param); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (RestParams, rest_params_unref) G_END_DECLS - -#endif /* _REST_PARAMS */ diff --git a/rest/rest-pkce-code-challenge.c b/rest/rest-pkce-code-challenge.c new file mode 100644 index 0000000..0d936f8 --- /dev/null +++ b/rest/rest-pkce-code-challenge.c @@ -0,0 +1,121 @@ +/* rest-pkce-code-challenge.c + * + * Copyright 2021 Günther Wagner <info@gunibert.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "rest-pkce-code-challenge.h" +#include "rest-utils.h" + +G_DEFINE_BOXED_TYPE (RestPkceCodeChallenge, rest_pkce_code_challenge, rest_pkce_code_challenge_copy, rest_pkce_code_challenge_free) + +struct _RestPkceCodeChallenge +{ + gchar *code_verifier; + gchar *code_challenge; +}; + +/** + * rest_pkce_code_challenge_new_random: + * + * Creates a new #RestPkceCodeChallenge. + * + * Returns: (transfer full): A newly created #RestPkceCodeChallenge + */ +RestPkceCodeChallenge * +rest_pkce_code_challenge_new_random (void) +{ + RestPkceCodeChallenge *self; + gint length = g_random_int_range (43, 128); + gsize digest_len = 200; + guchar code_verifier_sha256[200]; + GChecksum *sha256 = g_checksum_new (G_CHECKSUM_SHA256); + + self = g_slice_new0 (RestPkceCodeChallenge); + self->code_verifier = random_string (length); + g_checksum_update (sha256, (guchar *)self->code_verifier, -1); + g_checksum_get_digest (sha256, (guchar *)&code_verifier_sha256, &digest_len); + + self->code_challenge = g_base64_encode (code_verifier_sha256, digest_len); + g_strdelimit (self->code_challenge, "=", '\0'); + g_strdelimit (self->code_challenge, "+", '-'); + g_strdelimit (self->code_challenge, "/", '_'); + + return self; +} + +/** + * rest_pkce_code_challenge_copy: + * @self: a #RestPkceCodeChallenge + * + * Makes a deep copy of a #RestPkceCodeChallenge. + * + * Returns: (transfer full): A newly created #RestPkceCodeChallenge with the same + * contents as @self + */ +RestPkceCodeChallenge * +rest_pkce_code_challenge_copy (RestPkceCodeChallenge *self) +{ + RestPkceCodeChallenge *copy; + + g_return_val_if_fail (self, NULL); + + copy = g_slice_new0 (RestPkceCodeChallenge); + copy->code_verifier = self->code_verifier; + copy->code_challenge = self->code_challenge; + + return copy; +} + +/** + * rest_pkce_code_challenge_free: + * @self: a #RestPkceCodeChallenge + * + * Frees a #RestPkceCodeChallenge allocated using rest_pkce_code_challenge_new() + * or rest_pkce_code_challenge_copy(). + */ +void +rest_pkce_code_challenge_free (RestPkceCodeChallenge *self) +{ + g_return_if_fail (self); + + g_slice_free (RestPkceCodeChallenge, self); +} + +/** + * rest_pkce_code_challenge_get_challenge: + * + * Returns the Code Challenge for the Pkce verification. + * + * Returns: the code_challenge + */ +const gchar * +rest_pkce_code_challenge_get_challenge (RestPkceCodeChallenge *self) +{ + return self->code_challenge; +} + +/** + * rest_pkce_code_challenge_get_verifier: + * + * Returns the Code Verifier for the Pkce verification. + * + * Returns: the code_verifier + */ +const gchar * +rest_pkce_code_challenge_get_verifier (RestPkceCodeChallenge *self) +{ + return self->code_verifier; +} diff --git a/rest/rest-pkce-code-challenge.h b/rest/rest-pkce-code-challenge.h new file mode 100644 index 0000000..9ce3907 --- /dev/null +++ b/rest/rest-pkce-code-challenge.h @@ -0,0 +1,44 @@ +/* rest-pkce-code-challenge.h + * + * Copyright 2021 Günther Wagner <info@gunibert.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define REST_TYPE_PKCE_CODE_CHALLENGE (rest_pkce_code_challenge_get_type ()) + +/** + * RestPkceCodeChallenge: + * + * In order to play a Pkce Code Verification during a OAuth2 authorization + * you need this structure which handles the algorithmic part. + */ +typedef struct _RestPkceCodeChallenge RestPkceCodeChallenge; + +GType rest_pkce_code_challenge_get_type (void) G_GNUC_CONST; +RestPkceCodeChallenge *rest_pkce_code_challenge_new_random (void); +RestPkceCodeChallenge *rest_pkce_code_challenge_copy (RestPkceCodeChallenge *self); +void rest_pkce_code_challenge_free (RestPkceCodeChallenge *self); +const gchar *rest_pkce_code_challenge_get_challenge (RestPkceCodeChallenge *self); +const gchar *rest_pkce_code_challenge_get_verifier (RestPkceCodeChallenge *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (RestPkceCodeChallenge, rest_pkce_code_challenge_free) + +G_END_DECLS diff --git a/rest/rest-proxy-call.c b/rest/rest-proxy-call.c index 62b00da..791a7dd 100644 --- a/rest/rest-proxy-call.c +++ b/rest/rest-proxy-call.c @@ -148,7 +148,7 @@ rest_proxy_call_dispose (GObject *object) g_clear_object (&priv->cancellable); } - g_clear_pointer (&priv->params, rest_params_free); + g_clear_pointer (&priv->params, rest_params_unref); g_clear_pointer (&priv->headers, g_hash_table_unref); g_clear_pointer (&priv->response_headers, g_hash_table_unref); g_clear_object (&priv->proxy); @@ -238,6 +238,8 @@ rest_proxy_call_set_method (RestProxyCall *call, * @call: The #RestProxyCall * * Get the HTTP method to use when making the call, for example GET or POST. + * + * Returns: (transfer none): the HTTP method */ const char * rest_proxy_call_get_method (RestProxyCall *call) @@ -1005,19 +1007,23 @@ _call_message_call_completed_cb (SoupMessage *message, GError *error, gpointer user_data) { - GTask *task = user_data; + g_autoptr(GTask) task = user_data; RestProxyCall *call; call = REST_PROXY_CALL (g_task_get_source_object (task)); + if (error) + { + g_task_return_error (task, error); + return; + } + finish_call (call, message, payload, &error); if (error != NULL) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); - - g_object_unref (task); } /** @@ -1158,6 +1164,8 @@ _continuous_call_message_sent_cb (GObject *source, * * You may unref the call after calling this function since there is an * internal reference, or you may unref in the callback. + * + * Returns: %TRUE on success */ gboolean rest_proxy_call_continuous (RestProxyCall *call, @@ -1491,11 +1499,7 @@ rest_proxy_call_get_payload_length (RestProxyCall *call) g_return_val_if_fail (REST_IS_PROXY_CALL (call), 0); payload = GET_PRIVATE (call)->payload; -#ifdef WITH_SOUP_2 - return payload ? g_bytes_get_size (payload) - 1 : 0; -#else return payload ? g_bytes_get_size (payload) : 0; -#endif } /** diff --git a/rest/rest-proxy.c b/rest/rest-proxy.c index 171f6cb..d8dbfc6 100644 --- a/rest/rest-proxy.c +++ b/rest/rest-proxy.c @@ -31,8 +31,6 @@ #include "rest-private.h" -#define GET_PRIVATE(o) rest_proxy_get_instance_private(REST_PROXY(o)) - typedef struct _RestProxyPrivate RestProxyPrivate; struct _RestProxyPrivate { @@ -74,16 +72,14 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -static gboolean _rest_proxy_simple_run_valist (RestProxy *proxy, - char **payload, - goffset *len, - GError **error, - va_list params); - -static RestProxyCall *_rest_proxy_new_call (RestProxy *proxy); - -static gboolean _rest_proxy_bind_valist (RestProxy *proxy, - va_list params); +static gboolean _rest_proxy_simple_run_valist (RestProxy *proxy, + char **payload, + goffset *len, + GError **error, + va_list params); +static RestProxyCall *_rest_proxy_new_call (RestProxy *proxy); +static gboolean _rest_proxy_bind_valist (RestProxy *proxy, + va_list params); GQuark rest_proxy_error_quark (void) @@ -97,7 +93,8 @@ rest_proxy_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - RestProxyPrivate *priv = GET_PRIVATE (object); + RestProxy *self = REST_PROXY (object); + RestProxyPrivate *priv = rest_proxy_get_instance_private (self); switch (property_id) { case PROP_URL_FORMAT: @@ -145,7 +142,8 @@ rest_proxy_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - RestProxyPrivate *priv = GET_PRIVATE (object); + RestProxy *self = REST_PROXY (object); + RestProxyPrivate *priv = rest_proxy_get_instance_private (self); switch (property_id) { case PROP_URL_FORMAT: @@ -199,7 +197,8 @@ rest_proxy_set_property (GObject *object, static void rest_proxy_dispose (GObject *object) { - RestProxyPrivate *priv = GET_PRIVATE (object); + RestProxy *self = REST_PROXY (object); + RestProxyPrivate *priv = rest_proxy_get_instance_private (self); g_clear_object (&priv->session); @@ -226,7 +225,7 @@ authenticate (RestProxy *self, gboolean retrying, SoupSession *session) { - RestProxyPrivate *priv = GET_PRIVATE (self); + RestProxyPrivate *priv = rest_proxy_get_instance_private (self); RestProxyAuth *rest_auth; gboolean try_auth; @@ -241,7 +240,8 @@ authenticate (RestProxy *self, static void rest_proxy_constructed (GObject *object) { - RestProxyPrivate *priv = GET_PRIVATE (object); + RestProxy *self = REST_PROXY (object); + RestProxyPrivate *priv = rest_proxy_get_instance_private (self); if (!priv->disable_cookies) { SoupSessionFeature *cookie_jar = @@ -270,7 +270,8 @@ rest_proxy_constructed (GObject *object) static void rest_proxy_finalize (GObject *object) { - RestProxyPrivate *priv = GET_PRIVATE (object); + RestProxy *self = REST_PROXY (object); + RestProxyPrivate *priv = rest_proxy_get_instance_private (self); g_free (priv->url); g_free (priv->url_format); @@ -440,14 +441,17 @@ transform_tls_database_to_ssl_ca_file (GBinding *binding, static void rest_proxy_init (RestProxy *self) { - RestProxyPrivate *priv = GET_PRIVATE (self); + RestProxyPrivate *priv = rest_proxy_get_instance_private (self); +#ifdef REST_SYSTEM_CA_FILE GTlsDatabase *tls_database; +#endif #ifndef WITH_SOUP_2 priv->ssl_strict = TRUE; #endif priv->session = soup_session_new (); + soup_session_remove_feature_by_type (priv->session, SOUP_TYPE_AUTH_MANAGER); #ifdef REST_SYSTEM_CA_FILE /* with ssl-strict (defaults TRUE) setting ssl-ca-file forces all @@ -532,7 +536,7 @@ static gboolean _rest_proxy_bind_valist (RestProxy *proxy, va_list params) { - RestProxyPrivate *priv = GET_PRIVATE (proxy); + RestProxyPrivate *priv = rest_proxy_get_instance_private (proxy); g_return_val_if_fail (proxy != NULL, FALSE); g_return_val_if_fail (priv->url_format != NULL, FALSE); @@ -583,7 +587,7 @@ rest_proxy_set_user_agent (RestProxy *proxy, const gchar * rest_proxy_get_user_agent (RestProxy *proxy) { - RestProxyPrivate *priv = GET_PRIVATE (proxy); + RestProxyPrivate *priv = rest_proxy_get_instance_private (proxy); g_return_val_if_fail (REST_IS_PROXY (proxy), NULL); @@ -613,9 +617,10 @@ rest_proxy_get_user_agent (RestProxy *proxy) * Since: 0.7.92 */ void -rest_proxy_add_soup_feature (RestProxy *proxy, SoupSessionFeature *feature) +rest_proxy_add_soup_feature (RestProxy *proxy, + SoupSessionFeature *feature) { - RestProxyPrivate *priv = GET_PRIVATE (proxy); + RestProxyPrivate *priv = rest_proxy_get_instance_private (proxy); g_return_if_fail (REST_IS_PROXY(proxy)); g_return_if_fail (feature != NULL); @@ -659,7 +664,7 @@ rest_proxy_new_call (RestProxy *proxy) gboolean _rest_proxy_get_binding_required (RestProxy *proxy) { - RestProxyPrivate *priv = GET_PRIVATE (proxy); + RestProxyPrivate *priv = rest_proxy_get_instance_private (proxy); g_return_val_if_fail (REST_IS_PROXY (proxy), FALSE); @@ -669,14 +674,14 @@ _rest_proxy_get_binding_required (RestProxy *proxy) const gchar * _rest_proxy_get_bound_url (RestProxy *proxy) { - RestProxyPrivate *priv = GET_PRIVATE (proxy); + RestProxyPrivate *priv = rest_proxy_get_instance_private (proxy); g_return_val_if_fail (REST_IS_PROXY (proxy), NULL); if (!priv->url && !priv->binding_required) - { - priv->url = g_strdup (priv->url_format); - } + { + priv->url = g_strdup (priv->url_format); + } return priv->url; } @@ -763,7 +768,7 @@ message_finished_cb (SoupSession *session, GError *error = NULL; body = g_bytes_new (message->response_body->data, - message->response_body->length + 1); + message->response_body->length); data->callback (message, body, error, data->user_data); g_free (data); } @@ -791,7 +796,7 @@ _rest_proxy_queue_message (RestProxy *proxy, RestMessageFinishedCallback callback, gpointer user_data) { - RestProxyPrivate *priv = GET_PRIVATE (proxy); + RestProxyPrivate *priv = rest_proxy_get_instance_private (proxy); RestMessageQueueData *data; g_return_if_fail (REST_IS_PROXY (proxy)); @@ -841,7 +846,7 @@ _rest_proxy_send_message_async (RestProxy *proxy, GAsyncReadyCallback callback, gpointer user_data) { - RestProxyPrivate *priv = GET_PRIVATE (proxy); + RestProxyPrivate *priv = rest_proxy_get_instance_private (proxy); GTask *task; task = g_task_new (proxy, cancellable, callback, user_data); @@ -868,7 +873,7 @@ _rest_proxy_cancel_message (RestProxy *proxy, SoupMessage *message) { #ifdef WITH_SOUP_2 - RestProxyPrivate *priv = GET_PRIVATE (proxy); + RestProxyPrivate *priv = rest_proxy_get_instance_private (proxy); g_return_if_fail (REST_IS_PROXY (proxy)); g_return_if_fail (SOUP_IS_MESSAGE (message)); @@ -885,7 +890,7 @@ _rest_proxy_send_message (RestProxy *proxy, GCancellable *cancellable, GError **error) { - RestProxyPrivate *priv = GET_PRIVATE (proxy); + RestProxyPrivate *priv = rest_proxy_get_instance_private (proxy); GBytes *body; g_return_val_if_fail (REST_IS_PROXY (proxy), NULL); @@ -894,7 +899,7 @@ _rest_proxy_send_message (RestProxy *proxy, #ifdef WITH_SOUP_2 soup_session_send_message (priv->session, message); body = g_bytes_new (message->response_body->data, - message->response_body->length + 1); + message->response_body->length); #else body = soup_session_send_and_read (priv->session, message, diff --git a/rest/rest-proxy.h b/rest/rest-proxy.h index bb6d7b0..4b2a813 100644 --- a/rest/rest-proxy.h +++ b/rest/rest-proxy.h @@ -160,41 +160,32 @@ typedef enum { GQuark rest_proxy_error_quark (void); -RestProxy *rest_proxy_new (const gchar *url_format, - gboolean binding_required); - -RestProxy * -rest_proxy_new_with_authentication (const gchar *url_format, - gboolean binding_required, - const gchar *username, - const gchar *password); - -gboolean rest_proxy_bind (RestProxy *proxy, - ...); - -gboolean rest_proxy_bind_valist (RestProxy *proxy, - va_list params); - -void rest_proxy_set_user_agent (RestProxy *proxy, const char *user_agent); - -const gchar *rest_proxy_get_user_agent (RestProxy *proxy); - -void rest_proxy_add_soup_feature (RestProxy *proxy, - SoupSessionFeature *feature); - -RestProxyCall *rest_proxy_new_call (RestProxy *proxy); - -G_GNUC_NULL_TERMINATED -gboolean rest_proxy_simple_run (RestProxy *proxy, - gchar **payload, - goffset *len, - GError **error, - ...); -gboolean rest_proxy_simple_run_valist (RestProxy *proxy, - gchar **payload, - goffset *len, - GError **error, - va_list params); +RestProxy *rest_proxy_new (const gchar *url_format, + gboolean binding_required); +RestProxy *rest_proxy_new_with_authentication (const gchar *url_format, + gboolean binding_required, + const gchar *username, + const gchar *password); +gboolean rest_proxy_bind (RestProxy *proxy, + ...); +gboolean rest_proxy_bind_valist (RestProxy *proxy, + va_list params); +void rest_proxy_set_user_agent (RestProxy *proxy, + const char *user_agent); +const gchar *rest_proxy_get_user_agent (RestProxy *proxy); +void rest_proxy_add_soup_feature (RestProxy *proxy, + SoupSessionFeature *feature); +RestProxyCall *rest_proxy_new_call (RestProxy *proxy); +gboolean rest_proxy_simple_run (RestProxy *proxy, + gchar **payload, + goffset *len, + GError **error, + ...) G_GNUC_NULL_TERMINATED; +gboolean rest_proxy_simple_run_valist (RestProxy *proxy, + gchar **payload, + goffset *len, + GError **error, + va_list params); G_END_DECLS #endif /* _REST_PROXY */ diff --git a/rest/rest-utils.c b/rest/rest-utils.c new file mode 100644 index 0000000..939d49e --- /dev/null +++ b/rest/rest-utils.c @@ -0,0 +1,44 @@ +/* rest-utils.c + * + * Copyright 2021 Günther Wagner <info@gunibert.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "rest-utils.h" + +/** + * random_string: + * @length: the length of the random string + * + * Creates a random string from a given alphabeth with length @length + * + * Returns: (transfer full): a random string + */ +gchar * +random_string (guint length) +{ + g_autoptr(GRand) rand = g_rand_new (); + gchar *buffer = g_slice_alloc0 (sizeof (gchar) * length + 1); + gchar alphabeth[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~"; + + for (guint i = 0; i < length; i++) + { + buffer[i] = alphabeth[g_rand_int (rand) % (sizeof (alphabeth) - 1)]; + } + buffer[length] = '\0'; + + return buffer; +} + diff --git a/rest-extras/flickr-proxy-private.h b/rest/rest-utils.h index 5932946..cd61145 100644 --- a/rest-extras/flickr-proxy-private.h +++ b/rest/rest-utils.h @@ -1,10 +1,7 @@ -/* - * librest - RESTful web services access - * Copyright (c) 2008, 2009, Intel Corporation. +/* rest-utils.h + * + * Copyright 2021 Günther Wagner <info@gunibert.de> * - * Authors: Rob Bradford <rob@linux.intel.com> - * Ross Burton <ross@linux.intel.com> - * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. @@ -17,17 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ -#include "flickr-proxy.h" +#pragma once + +#include <glib.h> -#define FLICKR_PROXY_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), FLICKR_TYPE_PROXY, FlickrProxyPrivate)) +G_BEGIN_DECLS -struct _FlickrProxyPrivate { - char *api_key; - char *shared_secret; - char *token; -}; +gchar *random_string (guint length); +G_END_DECLS diff --git a/rest/rest-xml-node.h b/rest/rest-xml-node.h index 335bd64..05a4056 100644 --- a/rest/rest-xml-node.h +++ b/rest/rest-xml-node.h @@ -21,8 +21,7 @@ * */ -#ifndef _REST_XML_NODE -#define _REST_XML_NODE +#pragma once #include <glib-object.h> @@ -39,6 +38,8 @@ G_BEGIN_DECLS * @attrs: a #GHashTable of string name to string values for the attributes of * the element. * @next: the sibling #RestXmlNode with the same name + * + * The #RestXmlNode contains a parsed XmlNode for easy consumption */ typedef struct _RestXmlNode RestXmlNode; struct _RestXmlNode { @@ -54,20 +55,21 @@ struct _RestXmlNode { GType rest_xml_node_get_type (void); -RestXmlNode *rest_xml_node_ref (RestXmlNode *node); -void rest_xml_node_unref (RestXmlNode *node); -const gchar *rest_xml_node_get_attr (RestXmlNode *node, - const gchar *attr_name); -RestXmlNode *rest_xml_node_find (RestXmlNode *start, - const gchar *tag); +RestXmlNode *rest_xml_node_ref (RestXmlNode *node); +void rest_xml_node_unref (RestXmlNode *node); +const gchar *rest_xml_node_get_attr (RestXmlNode *node, + const gchar *attr_name); +RestXmlNode *rest_xml_node_find (RestXmlNode *start, + const gchar *tag); +RestXmlNode *rest_xml_node_add_child (RestXmlNode *parent, + const char *tag); +char *rest_xml_node_print (RestXmlNode *node); +void rest_xml_node_add_attr (RestXmlNode *node, + const char *attribute, + const char *value); +void rest_xml_node_set_content (RestXmlNode *node, + const char *value); -RestXmlNode *rest_xml_node_add_child (RestXmlNode *parent, const char *tag); -char *rest_xml_node_print (RestXmlNode *node); -void rest_xml_node_add_attr (RestXmlNode *node, - const char *attribute, - const char *value); -void rest_xml_node_set_content (RestXmlNode *node, const char *value); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (RestXmlNode, rest_xml_node_unref) G_END_DECLS - -#endif /* _REST_XML_NODE */ diff --git a/rest/rest-xml-parser.h b/rest/rest-xml-parser.h index cceda02..cee2570 100644 --- a/rest/rest-xml-parser.h +++ b/rest/rest-xml-parser.h @@ -20,8 +20,7 @@ * */ -#ifndef _REST_XML_PARSER -#define _REST_XML_PARSER +#pragma once #include <glib-object.h> #include <rest/rest-xml-node.h> @@ -31,15 +30,19 @@ G_BEGIN_DECLS #define REST_TYPE_XML_PARSER rest_xml_parser_get_type() G_DECLARE_DERIVABLE_TYPE (RestXmlParser, rest_xml_parser, REST, XML_PARSER, GObject) +/** + * RestXmlParser: + * + * A Xml Parser for Rest Responses + */ + struct _RestXmlParserClass { GObjectClass parent_class; }; -RestXmlParser *rest_xml_parser_new (void); -RestXmlNode *rest_xml_parser_parse_from_data (RestXmlParser *parser, - const gchar *data, - goffset len); +RestXmlParser *rest_xml_parser_new (void); +RestXmlNode *rest_xml_parser_parse_from_data (RestXmlParser *parser, + const gchar *data, + goffset len); G_END_DECLS - -#endif /* _REST_XML_PARSER */ diff --git a/rest/rest.h b/rest/rest.h new file mode 100644 index 0000000..9bc964c --- /dev/null +++ b/rest/rest.h @@ -0,0 +1,35 @@ +/* rest.h + * + * Copyright 2021 Günther Wagner <info@gunibert.de> + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +#pragma once + +#include <glib.h> + +G_BEGIN_DECLS + +#define REST_INSIDE +# include "rest-proxy.h" +# include "rest-proxy-call.h" +# include "rest-oauth2-proxy.h" +# include "rest-utils.h" +# include "rest-pkce-code-challenge.h" +#undef REST_INSIDE + +G_END_DECLS diff --git a/tests/Makefile.am b/tests/Makefile.am deleted file mode 100644 index 5ffdd46..0000000 --- a/tests/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -TESTS = proxy proxy-continuous threaded oauth oauth-async oauth2 flickr lastfm xml custom-serialize - -AM_CPPFLAGS = $(SOUP_CFLAGS) -I$(top_srcdir) $(GCOV_CFLAGS) -AM_LDFLAGS = $(SOUP_LIBS) $(GCOV_LDFLAGS) \ - ../rest/librest-@API_VERSION@.la ../rest-extras/librest-extras-@API_VERSION@.la - -check_PROGRAMS = $(TESTS) - -proxy_SOURCES = proxy.c -proxy_continuous_SOURCES = proxy-continuous.c -threaded_SOURCES = threaded.c -oauth_SOURCES = oauth.c -oauth_async_SOURCES = oauth-async.c -oauth2_SOURCES = oauth2.c -flickr_SOURCES = flickr.c -lastfm_SOURCES = lastfm.c -xml_SOURCES = xml.c -custom_serialize_SOURCES = custom-serialize.c diff --git a/tests/flickr.c b/tests/flickr.c index 11301fa..2d1ee99 100644 --- a/tests/flickr.c +++ b/tests/flickr.c @@ -108,6 +108,18 @@ test_flickr () } +static void +test_build_login_url (void) +{ + RestProxy *p = flickr_proxy_new ("api", "secret"); + g_autofree gchar *login_url = flickr_proxy_build_login_url (FLICKR_PROXY (p), NULL, "read"); + + g_assert_cmpstr (login_url, ==, "http://flickr.com/services/auth/?api_key=api&perms=read&api_sig=55e7647bc1a6e512172b8fda472a64a8"); + + login_url = flickr_proxy_build_login_url (FLICKR_PROXY (p), "746563215463214621", "read"); + + g_assert_cmpstr (login_url, ==, "http://flickr.com/services/auth/?frob=746563215463214621&api_key=api&perms=read&api_sig=bcabfd22f3beb489aeb3605b8c9e0441"); +} int main (int argc, char **argv) @@ -115,6 +127,7 @@ main (int argc, char **argv) g_test_init (&argc, &argv, NULL); g_test_add_func ("/flickr/flickr", test_flickr); + g_test_add_func ("/flickr/test_build_login_url", test_build_login_url); return g_test_run (); } diff --git a/tests/helper/test-server.c b/tests/helper/test-server.c new file mode 100644 index 0000000..1ddcb67 --- /dev/null +++ b/tests/helper/test-server.c @@ -0,0 +1,121 @@ +/* test-server.c + * + * Copyright 2021 Günther Wagner <info@gunibert.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "test-server.h" + +static GMutex server_start_mutex; +static GCond server_start_cond; + +SoupServer * +test_server_new () +{ + SoupServer *server = soup_server_new ("tls-certificate", NULL, NULL); + return server; +} + +gchar * +test_server_get_uri (SoupServer *server, + const char *scheme, + const char *host) +{ + GSList *uris, *u; +#ifdef WITH_SOUP_2 + SoupURI *uri, *ret_uri = NULL; +#else + GUri *uri, *ret_uri = NULL; +#endif + + uris = soup_server_get_uris (server); + for (u = uris; u; u = u->next) { + uri = u->data; + +#ifdef WITH_SOUP_2 + if (scheme && strcmp (soup_uri_get_scheme (uri), scheme) != 0) + continue; + if (host && strcmp (soup_uri_get_host (uri), host) != 0) + continue; + + ret_uri = soup_uri_copy (uri); +#else + if (scheme && strcmp (g_uri_get_scheme (uri), scheme) != 0) + continue; + if (host && strcmp (g_uri_get_host (uri), host) != 0) + continue; + + ret_uri = g_uri_ref (uri); +#endif + break; + } + +#ifdef WITH_SOUP_2 + g_slist_free_full (uris, (GDestroyNotify)soup_uri_free); + return soup_uri_to_string (ret_uri, FALSE); +#else + g_slist_free_full (uris, (GDestroyNotify)g_uri_unref); + return g_uri_to_string (ret_uri); +#endif +} + +static gpointer +run_server_thread (gpointer user_data) +{ + SoupServer *server = user_data; + GMainContext *context; + GMainLoop *loop; + GError *error = NULL; + + context = g_main_context_new (); + g_main_context_push_thread_default (context); + loop = g_main_loop_new (context, FALSE); + g_object_set_data (G_OBJECT (server), "GMainLoop", loop); + + // TODO: error handling + soup_server_listen_local (server, 0, 0, &error); + if (error != NULL) + g_error ("%s", error->message); + + g_mutex_lock (&server_start_mutex); + g_cond_signal (&server_start_cond); + g_mutex_unlock (&server_start_mutex); + + g_main_loop_run (loop); + g_main_loop_unref (loop); + + g_print("%s\n", "Shutting down server..."); + + soup_server_disconnect (server); + + g_main_context_pop_thread_default (context); + g_main_context_unref (context); + + return NULL; +} + +void +test_server_run_in_thread (SoupServer *server) +{ + GThread *thread; + + g_mutex_lock (&server_start_mutex); + + thread = g_thread_new ("server_thread", run_server_thread, server); + g_cond_wait (&server_start_cond, &server_start_mutex); + g_mutex_unlock (&server_start_mutex); + + g_object_set_data (G_OBJECT (server), "thread", thread); +} diff --git a/rest/oauth2-proxy-private.h b/tests/helper/test-server.h index 565c1c3..ee25600 100644 --- a/rest/oauth2-proxy-private.h +++ b/tests/helper/test-server.h @@ -1,10 +1,6 @@ -/* - * librest - RESTful web services access - * Copyright (c) 2008, 2009, 2010 Intel Corporation. +/* test-server.h * - * Authors: Rob Bradford <rob@linux.intel.com> - * Ross Burton <ross@linux.intel.com> - * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> + * Copyright 2021 Günther Wagner <info@gunibert.de> * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, @@ -18,17 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ -#ifndef _OAUTH2_PROXY_PRIVATE -#define _OAUTH2_PROXY_PRIVATE -#include "oauth2-proxy.h" +#pragma once + +#include <glib.h> +#include <libsoup/soup.h> + +G_BEGIN_DECLS -struct _OAuth2ProxyPrivate { - char *client_id; - char *auth_endpoint; - char *access_token; -}; +SoupServer *test_server_new (void); +void test_server_run_in_thread (SoupServer *server); +gchar *test_server_get_uri (SoupServer *server, + const char *scheme, + const char *host); -#endif /* _OAUTH2_PROXY_PRIVATE */ +G_END_DECLS diff --git a/tests/meson.build b/tests/meson.build index 0fe80e8..b3087a7 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -3,9 +3,10 @@ test_suites = { 'proxy', 'proxy-continuous', 'threaded', - 'oauth2', 'xml', 'custom-serialize', + 'oauth2', + 'params', ], 'rest-extras': [ 'flickr', @@ -20,16 +21,22 @@ test_deps = [ librest_extras_dep, ] +test_env = [ + 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), + 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), +] + foreach suite, test_names : test_suites foreach name : test_names test_bin = executable(name, - '@0@.c'.format(name), + ['@0@.c'.format(name), 'helper/test-server.c'], dependencies: test_deps, include_directories: config_h_inc, ) test(name, test_bin, suite: suite, + env: test_env, ) endforeach endforeach diff --git a/tests/oauth2.c b/tests/oauth2.c index 12721bc..a9b6e9d 100644 --- a/tests/oauth2.c +++ b/tests/oauth2.c @@ -1,8 +1,6 @@ -/* - * librest - RESTful web services access - * Copyright (c) 2010 Intel Corporation. +/* oauth2.c * - * Authors: Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> + * Copyright 2021 Günther Wagner <info@gunibert.de> * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, @@ -16,54 +14,333 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ -#include <rest/oauth2-proxy.h> -#include <string.h> +#include <glib.h> +#include "rest/rest.h" +#include "helper/test-server.h" +#ifdef WITH_SOUP_2 +static void +server_callback (SoupServer *server, + SoupMessage *msg, + const gchar *path, + GHashTable *query, + SoupClientContext *client, + gpointer user_data) +#else static void -test_url_no_fragment () +server_callback (SoupServer *server, + SoupServerMessage *msg, + const gchar *path, + GHashTable *query, + gpointer user_data) +#endif { - char *token = oauth2_proxy_extract_access_token ("http://example.com"); + if (g_strcmp0 (path, "/token") == 0) + { + gchar *json = "{" + "\"access_token\":\"2YotnFZFEjr1zCsicMWpAA\"," + "\"token_type\":\"example\"," + "\"expires_in\":3600," + "\"refresh_token\":\"tGzv3JOkF0XG5Qx2TlKWIA\"," + "\"example_parameter\":\"example_value\"" + "}"; +#ifdef WITH_SOUP_2 + soup_message_set_status (msg, SOUP_STATUS_OK); + soup_message_set_response (msg, "application/json", SOUP_MEMORY_COPY, json, strlen(json)); +#else + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + soup_server_message_set_response (msg, "application/json", SOUP_MEMORY_COPY, json, strlen(json)); +#endif - g_assert_null (token); + return; + } + else if (g_strcmp0 (path, "/api/bearer") == 0) + { +#ifdef WITH_SOUP_2 + const gchar *authorization = soup_message_headers_get_one (msg->request_headers, "Authorization"); + soup_message_set_status (msg, SOUP_STATUS_OK); + soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, authorization, strlen(authorization)); +#else + SoupMessageHeaders *headers = soup_server_message_get_request_headers (msg); + const gchar *authorization = soup_message_headers_get_one (headers, "Authorization"); + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + soup_server_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, authorization, strlen(authorization)); +#endif + return; + } + else if (g_strcmp0 (path, "/api/invalid") == 0) + { + gchar *resp = "{" + "\"error\":\"invalid_grant\"" + "}"; +#ifdef WITH_SOUP_2 + soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST); + soup_message_set_response (msg, "application/json", SOUP_MEMORY_COPY, resp, strlen(resp)); +#else + soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL); + soup_server_message_set_response (msg, "application/json", SOUP_MEMORY_COPY, resp, strlen(resp)); +#endif + return; + } } static void -test_url_fragment_no_access_token () +test_authorization_url (gconstpointer url) { - char *token = oauth2_proxy_extract_access_token ("http://example.com/foo?foo=1#bar"); + g_autoptr(RestProxy) proxy = REST_PROXY (rest_oauth2_proxy_new ("http://www.example.com/auth", + "http://www.example.com/token", + "http://www.example.com", + "client-id", + "client-secret", + "http://www.example.com/api")); + g_autoptr(RestPkceCodeChallenge) pkce = rest_pkce_code_challenge_new_random (); - g_assert_null (token); - g_free (token); + gchar *authorization_url = rest_oauth2_proxy_build_authorization_url (REST_OAUTH2_PROXY (proxy), + rest_pkce_code_challenge_get_challenge (pkce), + NULL, + NULL); + g_autofree gchar *expected = g_strdup_printf ("http://www.example.com/auth?code_challenge_method=S256&redirect_uri=http%%3A%%2F%%2Fwww.example.com&client_id=client-id&code_challenge=%s&response_type=code", rest_pkce_code_challenge_get_challenge (pkce)); + g_assert_cmpstr (authorization_url, ==, expected); } static void -test_access_token_simple () +test_fetch_access_token_finished (GObject *object, + GAsyncResult *result, + gpointer user_data) { - char *token = oauth2_proxy_extract_access_token ("http://example.com/foo?foo=1#access_token=1234567890_12.34561abcdefg&bar"); + g_autoptr(GError) error = NULL; + gboolean *finished = user_data; + + g_assert (G_IS_OBJECT (object)); + g_assert (G_IS_ASYNC_RESULT (result)); - g_assert_cmpstr (token, ==, "1234567890_12.34561abcdefg"); - g_free (token); + rest_oauth2_proxy_fetch_access_token_finish (REST_OAUTH2_PROXY (object), result, &error); + g_assert_no_error (error); + + *finished = TRUE; } -static void test_url_encoding_access_token () +static void +test_fetch_access_token (gconstpointer url) { - char *token = oauth2_proxy_extract_access_token ("http://example.com/foo?foo=1#access_token=1234567890%5F12%2E34561abcdefg&bar"); + GMainContext *async_context = g_main_context_ref_thread_default (); + g_autoptr(GError) error = NULL; + + g_autofree gchar *tokenurl = g_strdup_printf ("%stoken", (gchar *)url); + g_autofree gchar *baseurl = g_strdup_printf ("%sapi", (gchar *)url); + + gboolean finished = FALSE; + g_autoptr(RestProxy) proxy = REST_PROXY (rest_oauth2_proxy_new ("http://www.example.com/auth", + tokenurl, + "http://www.example.com", + "client-id", + "client-secret", + baseurl)); + rest_oauth2_proxy_fetch_access_token_async (REST_OAUTH2_PROXY (proxy), + "1234567890", + "code_verifier", + NULL, + test_fetch_access_token_finished, + &finished); + while (!finished) { + g_main_context_iteration (async_context, TRUE); + } + + g_assert_cmpstr ("2YotnFZFEjr1zCsicMWpAA", ==, rest_oauth2_proxy_get_access_token (REST_OAUTH2_PROXY (proxy))); + g_assert_cmpstr ("tGzv3JOkF0XG5Qx2TlKWIA", ==, rest_oauth2_proxy_get_refresh_token (REST_OAUTH2_PROXY (proxy))); + + g_autoptr(RestProxyCall) call = rest_proxy_new_call (proxy); + rest_proxy_call_set_method (call, "GET"); + rest_proxy_call_set_function (call, "bearer"); + rest_proxy_call_sync (call, &error); + g_assert_no_error (error); + g_autofree gchar *payload = g_strndup (rest_proxy_call_get_payload (call), rest_proxy_call_get_payload_length (call)); + g_assert_cmpstr ("Bearer 2YotnFZFEjr1zCsicMWpAA", ==, payload); - g_assert_cmpstr (token, ==, "1234567890_12.34561abcdefg"); - g_free (token); + g_main_context_unref (async_context); } -int -main (int argc, char **argv) +static void +test_refresh_access_token_finished_error (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GError) error = NULL; + gboolean *finished = user_data; + + g_assert (G_IS_OBJECT (object)); + g_assert (G_IS_ASYNC_RESULT (result)); + + rest_oauth2_proxy_refresh_access_token_finish (REST_OAUTH2_PROXY (object), result, &error); + g_assert_error (error, REST_OAUTH2_ERROR, REST_OAUTH2_ERROR_NO_REFRESH_TOKEN); + + *finished = TRUE; +} + +static void +test_refresh_access_token_finished (GObject *object, + GAsyncResult *result, + gpointer user_data) { + g_autoptr(GError) error = NULL; + gboolean *finished = user_data; + + g_assert (G_IS_OBJECT (object)); + g_assert (G_IS_ASYNC_RESULT (result)); + + rest_oauth2_proxy_refresh_access_token_finish (REST_OAUTH2_PROXY (object), result, &error); + g_assert_no_error (error); + + *finished = TRUE; +} + +static void +test_refresh_access_token (gconstpointer url) +{ + GMainContext *async_context = g_main_context_ref_thread_default (); + + g_autofree gchar *tokenurl = g_strdup_printf ("%stoken", (gchar *)url); + g_autofree gchar *baseurl = g_strdup_printf ("%sapi", (gchar *)url); + + gboolean finished = FALSE; + g_autoptr(RestProxy) proxy = REST_PROXY (rest_oauth2_proxy_new ("http://www.example.com/auth", + tokenurl, + "http://www.example.com", + "client-id", + "client-secret", + baseurl)); + + // Test error if no refresh token is set (note assertion happens in callback) + rest_oauth2_proxy_refresh_access_token_async (REST_OAUTH2_PROXY (proxy), NULL, test_refresh_access_token_finished_error, &finished); + + while (!finished) { + g_main_context_iteration (async_context, TRUE); + } + + // Test if refresh token gets refreshed + rest_oauth2_proxy_set_refresh_token (REST_OAUTH2_PROXY (proxy), "refresh_token"); + rest_oauth2_proxy_refresh_access_token_async (REST_OAUTH2_PROXY (proxy), NULL, test_refresh_access_token_finished, &finished); + + finished = FALSE; + + while (!finished) { + g_main_context_iteration (async_context, TRUE); + } + + g_assert_cmpstr ("2YotnFZFEjr1zCsicMWpAA", ==, rest_oauth2_proxy_get_access_token (REST_OAUTH2_PROXY (proxy))); + g_assert_cmpstr ("tGzv3JOkF0XG5Qx2TlKWIA", ==, rest_oauth2_proxy_get_refresh_token (REST_OAUTH2_PROXY (proxy))); + + g_main_context_unref (async_context); +} + +static void +test_refresh_access_token_sync (gconstpointer url) +{ + g_autoptr(GError) error = NULL; + + g_autofree gchar *tokenurl = g_strdup_printf ("%stoken", (gchar *)url); + g_autofree gchar *baseurl = g_strdup_printf ("%sapi", (gchar *)url); + + g_autoptr(RestProxy) proxy = REST_PROXY (rest_oauth2_proxy_new ("http://www.example.com/auth", + tokenurl, + "http://www.example.com", + "client-id", + "client-secret", + baseurl)); + + rest_oauth2_proxy_refresh_access_token (REST_OAUTH2_PROXY (proxy), &error); + g_assert_error (error, REST_OAUTH2_ERROR, REST_OAUTH2_ERROR_NO_REFRESH_TOKEN); + error = NULL; + + rest_oauth2_proxy_set_refresh_token (REST_OAUTH2_PROXY (proxy), "refresh_token"); + rest_oauth2_proxy_refresh_access_token (REST_OAUTH2_PROXY (proxy), &error); + + g_assert_cmpstr ("2YotnFZFEjr1zCsicMWpAA", ==, rest_oauth2_proxy_get_access_token (REST_OAUTH2_PROXY (proxy))); + g_assert_cmpstr ("tGzv3JOkF0XG5Qx2TlKWIA", ==, rest_oauth2_proxy_get_refresh_token (REST_OAUTH2_PROXY (proxy))); + + rest_oauth2_proxy_set_refresh_token (REST_OAUTH2_PROXY (proxy), "refresh_token"); + rest_oauth2_proxy_refresh_access_token (REST_OAUTH2_PROXY (proxy), NULL); + + g_assert_cmpstr ("2YotnFZFEjr1zCsicMWpAA", ==, rest_oauth2_proxy_get_access_token (REST_OAUTH2_PROXY (proxy))); + g_assert_cmpstr ("tGzv3JOkF0XG5Qx2TlKWIA", ==, rest_oauth2_proxy_get_refresh_token (REST_OAUTH2_PROXY (proxy))); +} + +static void +test_access_token_expired (gconstpointer url) +{ + g_autofree gchar *tokenurl = g_strdup_printf ("%stoken", (gchar *)url); + g_autofree gchar *baseurl = g_strdup_printf ("%sapi", (gchar *)url); + g_autoptr(GError) error = NULL; + + g_autoptr(RestProxy) proxy = REST_PROXY (rest_oauth2_proxy_new ("http://www.example.com/auth", + tokenurl, + "http://www.example.com", + "client-id", + "client-secret", + baseurl)); + GDateTime *now = g_date_time_new_now_local (); + rest_oauth2_proxy_set_expiration_date (REST_OAUTH2_PROXY (proxy), now); + + g_autoptr(RestProxyCall) call = rest_proxy_new_call (proxy); + rest_proxy_call_set_method (call, "GET"); + rest_proxy_call_set_function (call, "/expired"); + rest_proxy_call_sync (call, &error); + g_assert_error (error, REST_OAUTH2_ERROR, REST_OAUTH2_ERROR_ACCESS_TOKEN_EXPIRED); +} + +static void +test_access_token_invalid (gconstpointer url) +{ + GMainContext *async_context = g_main_context_ref_thread_default (); + g_autofree gchar *tokenurl = g_strdup_printf ("%stoken", (gchar *)url); + g_autofree gchar *baseurl = g_strdup_printf ("%sapi", (gchar *)url); + g_autoptr(GError) error = NULL; + gboolean finished = FALSE; + + g_autoptr(RestProxy) proxy = REST_PROXY (rest_oauth2_proxy_new ("http://www.example.com/auth", + tokenurl, + "http://www.example.com", + "client-id", + "client-secret", + baseurl)); + + rest_oauth2_proxy_fetch_access_token_async (REST_OAUTH2_PROXY (proxy), "1234567890", "code_verifier", NULL, test_fetch_access_token_finished, &finished); + while (!finished) { + g_main_context_iteration (async_context, TRUE); + } + + g_autoptr(RestProxyCall) call = rest_proxy_new_call (proxy); + rest_proxy_call_set_method (call, "GET"); + rest_proxy_call_set_function (call, "/invalid"); + rest_proxy_call_sync (call, &error); + g_assert_cmpint (rest_proxy_call_get_status_code (call), ==, SOUP_STATUS_BAD_REQUEST); + + + g_main_context_unref (async_context); +} + +gint +main (gint argc, + gchar *argv[]) +{ + SoupServer *server; + g_autofree gchar *url; + g_test_init (&argc, &argv, NULL); - g_test_add_func ("/oauth2/url-no-fragment", test_url_no_fragment); - g_test_add_func ("/oauth2/url-fragment-no-access-token", test_url_fragment_no_access_token); - g_test_add_func ("/oauth2/access-token-simple", test_access_token_simple); - g_test_add_func ("/oauth2/access-token-url-encoding", test_url_encoding_access_token); + + server = test_server_new (); + soup_server_add_handler (server, NULL, server_callback, NULL, NULL); + test_server_run_in_thread (server); + url = test_server_get_uri (server, "http", NULL); + + g_test_add_data_func ("/oauth2/authorization_url", url, test_authorization_url); + g_test_add_data_func ("/oauth2/fetch_access_token", url, test_fetch_access_token); + g_test_add_data_func ("/oauth2/refresh_access_token", url, test_refresh_access_token); + g_test_add_data_func ("/oauth2/refresh_access_token_sync", url, test_refresh_access_token_sync); + g_test_add_data_func ("/oauth2/access_token_expired", url, test_access_token_expired); + g_test_add_data_func ("/oauth2/access_token_invalid", url, test_access_token_invalid); return g_test_run (); } diff --git a/tests/params.c b/tests/params.c new file mode 100644 index 0000000..e477ba9 --- /dev/null +++ b/tests/params.c @@ -0,0 +1,141 @@ +#include <glib.h> +#include "rest/rest-params.h" +#include "rest/rest-param.h" +#include <glib-object.h> + +static void +test_params (void) +{ + RestParamsIter iter; + RestParam *param; + const char *name; + gint pos = 0; + g_autoptr(RestParams) params = NULL; + + struct { + char *name; + char *value; + } data[] = { + { + .name = "name1", + .value = "value1" + }, + { + .name = "name2", + .value = "value2" + } + }; + + params = rest_params_new (); + for (gint i = 0; i < sizeof (data)/sizeof (data[0]); i++) + { + RestParam *p = rest_param_new_string (data[i].name, REST_MEMORY_COPY, data[i].value); + rest_params_add (params, p); + } + + rest_params_iter_init (&iter, params); + while (rest_params_iter_next (&iter, &name, ¶m)) + { + g_assert_cmpstr (data[pos].name, ==, name); + g_assert_cmpstr (data[pos].value, ==, rest_param_get_content (param)); + pos++; + } + + rest_params_remove (params, "name2"); + pos = 0; + rest_params_iter_init (&iter, params); + while (rest_params_iter_next (&iter, &name, ¶m)) + { + g_assert_cmpstr (data[pos].name, ==, name); + g_assert_cmpstr (data[pos].value, ==, rest_param_get_content (param)); + pos++; + } +} + +static void +test_params_get (void) +{ + g_autoptr(RestParams) params; + RestParam *p1; + + struct { + char *name; + char *value; + } data[] = { + { + .name = "name1", + .value = "value1" + }, + { + .name = "name2", + .value = "value2" + } + }; + + params = rest_params_new (); + for (gint i = 0; i < sizeof (data)/sizeof (data[0]); i++) + { + RestParam *p = rest_param_new_string (data[i].name, REST_MEMORY_COPY, data[i].value); + rest_params_add (params, p); + } + + p1 = rest_params_get (params, "name2"); + + g_assert_cmpstr (rest_param_get_name (p1), ==, "name2"); + g_assert_cmpstr (rest_param_get_content (p1), ==, "value2"); +} + +static void +test_params_is_string (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(RestParams) params; + g_autofree char *file; + gsize length; + gchar *contents; + RestParam *p; + + struct { + char *name; + char *value; + } data[] = { + { + .name = "name1", + .value = "value1" + }, + { + .name = "name2", + .value = "value2" + } + }; + + params = rest_params_new (); + for (gint i = 0; i < sizeof (data)/sizeof (data[0]); i++) + { + RestParam *p = rest_param_new_string (data[i].name, REST_MEMORY_COPY, data[i].value); + rest_params_add (params, p); + } + + g_assert_true (rest_params_are_strings (params)); + + file = g_test_build_filename (G_TEST_DIST, "test-media.png", NULL); + g_file_get_contents(file, &contents, &length, &error); + + p = rest_param_new_full ("nostring", REST_MEMORY_COPY, contents, length, "image/png", "test-media.png"); + rest_params_add (params, p); + + g_assert_false (rest_params_are_strings (params)); +} + +gint +main (gint argc, + gchar *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func("/rest/params", test_params); + g_test_add_func("/rest/params_get", test_params_get); + g_test_add_func("/rest/params_is_strings", test_params_is_string); + + return g_test_run (); +} diff --git a/tests/proxy-continuous.c b/tests/proxy-continuous.c index efc3de1..f489af5 100644 --- a/tests/proxy-continuous.c +++ b/tests/proxy-continuous.c @@ -154,7 +154,7 @@ continuous () GSList *uris; - server = soup_server_new (NULL); + server = soup_server_new (NULL, NULL); soup_server_listen_local (server, 0, 0, &error); g_assert_no_error (error); diff --git a/tests/proxy.c b/tests/proxy.c index d6c05a5..a6d034a 100644 --- a/tests/proxy.c +++ b/tests/proxy.c @@ -35,49 +35,98 @@ #include <stdlib.h> #include <libsoup/soup.h> #include <rest/rest-proxy.h> +#include "helper/test-server.h" #if SOUP_CHECK_VERSION (2, 28, 0) /* Avoid deprecation warning with newer libsoup */ #define soup_message_headers_get soup_message_headers_get_one #endif -#define PORT 8080 - -static int errors = 0; - -SoupServer *server; -GMainLoop *server_loop; - -static void #ifdef WITH_SOUP_2 -server_callback (SoupServer *server, SoupMessage *msg, - const char *path, GHashTable *query, - SoupClientContext *client, gpointer user_data) -#else -server_callback (SoupServer *server, SoupServerMessage *msg, - const char *path, GHashTable *query, gpointer user_data) -#endif +static void +server_callback (SoupServer *server, + SoupMessage *msg, + const gchar *path, + GHashTable *query, + SoupClientContext *client, + gpointer user_data) { - if (g_str_equal (path, "/ping")) { -#ifdef WITH_SOUP_2 + if (g_str_equal (path, "/ping") && g_strcmp0 ("GET", msg->method) == 0) { soup_message_set_status (msg, SOUP_STATUS_OK); -#else - soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); -#endif + } + else if (g_str_equal (path, "/ping") && g_strcmp0 ("POST", msg->method) == 0) { + soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND); } else if (g_str_equal (path, "/echo")) { const char *value; value = g_hash_table_lookup (query, "value"); -#ifdef WITH_SOUP_2 soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, value, strlen (value)); soup_message_set_status (msg, SOUP_STATUS_OK); + } + else if (g_str_equal (path, "/reverse")) { + char *value; + + value = g_strdup (g_hash_table_lookup (query, "value")); + g_strreverse (value); + soup_message_set_response (msg, "text/plain", SOUP_MEMORY_TAKE, + value, strlen (value)); + soup_message_set_status (msg, SOUP_STATUS_OK); + } + else if (g_str_equal (path, "/status")) { + const char *value; + int status; + + value = g_hash_table_lookup (query, "status"); + if (value) { + status = atoi (value); + soup_message_set_status (msg, status ?: SOUP_STATUS_INTERNAL_SERVER_ERROR); + } else { + soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + } + } + else if (g_str_equal (path, "/useragent/none")) { + SoupMessageHeaders *request_headers = msg->request_headers; + + if (soup_message_headers_get (request_headers, "User-Agent") == NULL) { + soup_message_set_status (msg, SOUP_STATUS_OK); + } else { + soup_message_set_status (msg, SOUP_STATUS_EXPECTATION_FAILED); + } + } + else if (g_str_equal (path, "/useragent/testsuite")) { + SoupMessageHeaders *request_headers = msg->request_headers; + const char *value; + value = soup_message_headers_get (request_headers, "User-Agent"); + if (g_strcmp0 (value, "TestSuite-1.0") == 0) { + soup_message_set_status (msg, SOUP_STATUS_OK); + } else { + soup_message_set_status (msg, SOUP_STATUS_EXPECTATION_FAILED); + } + } +} #else +static void +server_callback (SoupServer *server, + SoupServerMessage *msg, + const char *path, + GHashTable *query, + gpointer user_data) +{ + if (g_str_equal (path, "/ping") && g_strcmp0 ("GET", soup_server_message_get_method (msg)) == 0) { + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + } + else if (g_str_equal (path, "/ping") && g_strcmp0 ("POST", soup_server_message_get_method (msg)) == 0) { + soup_server_message_set_status (msg, SOUP_STATUS_NOT_FOUND, NULL); + } + else if (g_str_equal (path, "/echo")) { + const char *value; + + value = g_hash_table_lookup (query, "value"); soup_server_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, value, strlen (value)); soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); -#endif } else if (g_str_equal (path, "/reverse")) { char *value; @@ -85,15 +134,9 @@ server_callback (SoupServer *server, SoupServerMessage *msg, value = g_strdup (g_hash_table_lookup (query, "value")); g_strreverse (value); -#ifdef WITH_SOUP_2 - soup_message_set_response (msg, "text/plain", SOUP_MEMORY_TAKE, - value, strlen (value)); - soup_message_set_status (msg, SOUP_STATUS_OK); -#else soup_server_message_set_response (msg, "text/plain", SOUP_MEMORY_TAKE, value, strlen (value)); soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); -#endif } else if (g_str_equal (path, "/status")) { const char *value; @@ -102,304 +145,190 @@ server_callback (SoupServer *server, SoupServerMessage *msg, value = g_hash_table_lookup (query, "status"); if (value) { status = atoi (value); -#ifdef WITH_SOUP_2 - soup_message_set_status (msg, status ?: SOUP_STATUS_INTERNAL_SERVER_ERROR); -#else soup_server_message_set_status (msg, status ?: SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL); -#endif } else { -#ifdef WITH_SOUP_2 - soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); -#else soup_server_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL); -#endif } } else if (g_str_equal (path, "/useragent/none")) { -#ifdef WITH_SOUP_2 - SoupMessageHeaders *request_headers = msg->request_headers; -#else SoupMessageHeaders *request_headers = soup_server_message_get_request_headers (msg); -#endif if (soup_message_headers_get (request_headers, "User-Agent") == NULL) { -#ifdef WITH_SOUP_2 - soup_message_set_status (msg, SOUP_STATUS_OK); -#else soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); -#endif } else { -#ifdef WITH_SOUP_2 - soup_message_set_status (msg, SOUP_STATUS_EXPECTATION_FAILED); -#else soup_server_message_set_status (msg, SOUP_STATUS_EXPECTATION_FAILED, NULL); -#endif } } else if (g_str_equal (path, "/useragent/testsuite")) { -#ifdef WITH_SOUP_2 - SoupMessageHeaders *request_headers = msg->request_headers; -#else SoupMessageHeaders *request_headers = soup_server_message_get_request_headers (msg); -#endif const char *value; value = soup_message_headers_get (request_headers, "User-Agent"); if (g_strcmp0 (value, "TestSuite-1.0") == 0) { -#ifdef WITH_SOUP_2 - soup_message_set_status (msg, SOUP_STATUS_OK); -#else soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); -#endif } else { -#ifdef WITH_SOUP_2 - soup_message_set_status (msg, SOUP_STATUS_EXPECTATION_FAILED); -#else soup_server_message_set_status (msg, SOUP_STATUS_EXPECTATION_FAILED, NULL); -#endif - } } } +#endif + static void -ping_test (RestProxy *proxy) +ping_test (gconstpointer data) { - RestProxyCall *call; + RestProxy *proxy = (RestProxy *)data; + g_autoptr(RestProxyCall) call; GError *error = NULL; call = rest_proxy_new_call (proxy); rest_proxy_call_set_function (call, "ping"); - - if (!rest_proxy_call_sync (call, &error)) { - g_printerr ("2: Call failed: %s\n", error->message); - g_error_free (error); - errors++; - g_object_unref (call); - return; - } - g_assert(error == NULL); - - if (rest_proxy_call_get_status_code (call) != SOUP_STATUS_OK) { - g_printerr ("wrong response code\n"); - errors++; - return; - } - - if (rest_proxy_call_get_payload_length (call) != 0) { - g_printerr ("wrong length returned\n"); - errors++; - return; - } + rest_proxy_call_set_method (call, "GET"); + rest_proxy_call_sync (call, &error); + g_assert_no_error (error); + g_assert_cmpint (rest_proxy_call_get_status_code (call), ==, SOUP_STATUS_OK); + g_assert_cmpint (rest_proxy_call_get_payload_length (call), ==, 0); g_object_unref (call); + call = rest_proxy_new_call (proxy); + rest_proxy_call_set_function (call, "ping"); + rest_proxy_call_set_method (call, "POST"); + rest_proxy_call_sync (call, &error); + g_assert_error (error, REST_PROXY_ERROR, 404); + g_assert_cmpint (rest_proxy_call_get_status_code (call), ==, SOUP_STATUS_NOT_FOUND); } static void -echo_test (RestProxy *proxy) +echo_test (gconstpointer data) { - RestProxyCall *call; + RestProxy *proxy = (RestProxy *)data; + g_autoptr(RestProxyCall) call; GError *error = NULL; call = rest_proxy_new_call (proxy); rest_proxy_call_set_function (call, "echo"); rest_proxy_call_add_param (call, "value", "echome"); - - if (!rest_proxy_call_sync (call, &error)) { - g_printerr ("3: Call failed: %s\n", error->message); - g_error_free (error); - errors++; - g_object_unref (call); - return; - } - g_assert(error == NULL); - - if (rest_proxy_call_get_status_code (call) != SOUP_STATUS_OK) { - g_printerr ("wrong response code\n"); - errors++; - g_object_unref (call); - return; - } - if (rest_proxy_call_get_payload_length (call) != 6) { - g_printerr ("wrong length returned\n"); - errors++; - g_object_unref (call); - return; - } - if (g_strcmp0 ("echome", rest_proxy_call_get_payload (call)) != 0) { - g_printerr ("wrong string returned\n"); - errors++; - g_object_unref (call); - return; - } - g_object_unref (call); + rest_proxy_call_sync (call, &error); + g_assert_no_error (error); + g_assert_cmpint (rest_proxy_call_get_status_code (call), ==, SOUP_STATUS_OK); + g_assert_cmpint (rest_proxy_call_get_payload_length (call), ==, 6); + g_assert_cmpstr (rest_proxy_call_get_payload (call), ==, "echome"); } static void -reverse_test (RestProxy *proxy) +reverse_test (gconstpointer data) { - RestProxyCall *call; + RestProxy *proxy = (RestProxy *)data; + g_autoptr(RestProxyCall) call; GError *error = NULL; call = rest_proxy_new_call (proxy); rest_proxy_call_set_function (call, "reverse"); rest_proxy_call_add_param (call, "value", "reverseme"); - - if (!rest_proxy_call_sync (call, &error)) { - g_printerr ("4: Call failed: %s\n", error->message); - g_error_free (error); - errors++; - g_object_unref (call); - return; - } - g_assert(error == NULL); - - if (rest_proxy_call_get_status_code (call) != SOUP_STATUS_OK) { - g_printerr ("wrong response code\n"); - errors++; - g_object_unref (call); - return; - } - if (rest_proxy_call_get_payload_length (call) != 9) { - g_printerr ("wrong length returned\n"); - errors++; - g_object_unref (call); - return; - } - if (g_strcmp0 ("emesrever", rest_proxy_call_get_payload (call)) != 0) { - g_printerr ("wrong string returned\n"); - errors++; - g_object_unref (call); - return; - } - g_object_unref (call); + rest_proxy_call_sync (call, &error); + g_assert_no_error (error); + g_assert_cmpint (rest_proxy_call_get_status_code (call), ==, SOUP_STATUS_OK); + g_assert_cmpint (rest_proxy_call_get_payload_length (call), ==, 9); + g_assert_cmpstr (rest_proxy_call_get_payload (call), ==, "emesrever"); } static void status_ok_test (RestProxy *proxy, guint status) { - RestProxyCall *call; + g_autoptr(RestProxyCall) call; + g_autofree gchar *status_str; GError *error = NULL; - char *status_str; call = rest_proxy_new_call (proxy); rest_proxy_call_set_function (call, "status"); status_str = g_strdup_printf ("%d", status); rest_proxy_call_add_param (call, "status", status_str); - g_free (status_str); - - if (!rest_proxy_call_sync (call, &error)) { - g_printerr ("1: Call failed: %s\n", error->message); - g_error_free (error); - errors++; - g_object_unref (call); - return; - } - g_assert(error == NULL); - - if (rest_proxy_call_get_status_code (call) != status) { - g_printerr ("wrong response code, got %d\n", rest_proxy_call_get_status_code (call)); - errors++; - return; - } + rest_proxy_call_sync (call, &error); + g_assert_no_error (error); + g_assert_cmpint (rest_proxy_call_get_status_code (call), ==, status); +} - g_object_unref (call); +static void +status_test (gconstpointer data) +{ + RestProxy *proxy = (RestProxy *)data; + status_ok_test (proxy, SOUP_STATUS_OK); + status_ok_test (proxy, SOUP_STATUS_NO_CONTENT); } static void status_error_test (RestProxy *proxy, guint status) { - RestProxyCall *call; - GError *error = NULL; - char *status_str; + g_autoptr(RestProxyCall) call; + g_autoptr(GError) error = NULL; + g_autofree gchar *status_str; call = rest_proxy_new_call (proxy); rest_proxy_call_set_function (call, "status"); status_str = g_strdup_printf ("%d", status); rest_proxy_call_add_param (call, "status", status_str); - g_free (status_str); - - if (rest_proxy_call_sync (call, &error)) { - g_printerr ("Call succeeded should have failed"); - errors++; - g_object_unref (call); - return; - } - g_error_free (error); - - if (rest_proxy_call_get_status_code (call) != status) { - g_printerr ("wrong response code, got %d\n", rest_proxy_call_get_status_code (call)); - errors++; - return; - } + rest_proxy_call_sync (call, &error); + g_assert_error (error, REST_PROXY_ERROR, status); + g_assert_cmpint (rest_proxy_call_get_status_code (call), ==, status); +} - g_object_unref (call); +static void +status_test_error (gconstpointer data) +{ + RestProxy *proxy = (RestProxy *)data; + status_error_test (proxy, SOUP_STATUS_BAD_REQUEST); + status_error_test (proxy, SOUP_STATUS_NOT_IMPLEMENTED); } static void test_status_ok (RestProxy *proxy, const char *function) { - RestProxyCall *call; - GError *error = NULL; + g_autoptr(RestProxyCall) call; + g_autoptr(GError) error = NULL; call = rest_proxy_new_call (proxy); rest_proxy_call_set_function (call, function); - - if (!rest_proxy_call_sync (call, &error)) { - g_printerr ("%s call failed: %s\n", function, error->message); - g_error_free (error); - errors++; - g_object_unref (call); - return; - } - g_assert(error == NULL); - - if (rest_proxy_call_get_status_code (call) != SOUP_STATUS_OK) { - g_printerr ("wrong response code, got %d\n", rest_proxy_call_get_status_code (call)); - errors++; - return; - } - - g_object_unref (call); + rest_proxy_call_sync (call, &error); + g_assert_no_error (error); + g_assert_cmpint (rest_proxy_call_get_status_code (call), ==, SOUP_STATUS_OK); } -static void * -server_thread_func (gpointer data) +static void +test_user_agent (gconstpointer data) { - server_loop = g_main_loop_new (NULL, TRUE); - /*SoupServer *server = soup_server_new (NULL);*/ - soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - - soup_server_listen_local (server, PORT, 0, NULL); - g_main_loop_run (server_loop); - - return NULL; + RestProxy *proxy = (RestProxy *)data; + test_status_ok (proxy, "useragent/none"); + rest_proxy_set_user_agent (proxy, "TestSuite-1.0"); + test_status_ok (proxy, "useragent/testsuite"); } int -main (int argc, char **argv) +main (int argc, + gchar **argv) { - char *url; RestProxy *proxy; + SoupServer *server; + gint ret; + gchar *uri; - server = soup_server_new (NULL); - g_thread_new ("Server Thread", server_thread_func, NULL); + g_test_init (&argc, &argv, NULL); - url = g_strdup_printf ("http://127.0.0.1:%d/", PORT); - proxy = rest_proxy_new (url, FALSE); - g_free (url); + server = test_server_new (); + soup_server_add_handler (server, NULL, server_callback, NULL, NULL); + test_server_run_in_thread (server); + uri = test_server_get_uri (server, "http", NULL); - ping_test (proxy); - echo_test (proxy); - reverse_test (proxy); - status_ok_test (proxy, SOUP_STATUS_OK); - status_ok_test (proxy, SOUP_STATUS_NO_CONTENT); - status_error_test (proxy, SOUP_STATUS_BAD_REQUEST); - status_error_test (proxy, SOUP_STATUS_NOT_IMPLEMENTED); + proxy = rest_proxy_new (uri, FALSE); - test_status_ok (proxy, "useragent/none"); - rest_proxy_set_user_agent (proxy, "TestSuite-1.0"); - test_status_ok (proxy, "useragent/testsuite"); + g_test_add_data_func ("/proxy/ping", proxy, ping_test); + g_test_add_data_func ("/proxy/echo", proxy, echo_test); + g_test_add_data_func ("/proxy/reverse", proxy, reverse_test); + g_test_add_data_func ("/proxy/status_ok_test", proxy, status_test); + g_test_add_data_func ("/proxy/status_error_test", proxy, status_test_error); + g_test_add_data_func ("/proxy/user_agent", proxy, test_user_agent); + + ret = g_test_run (); + + g_main_context_unref (g_main_context_default ()); - g_main_loop_quit (server_loop); - return errors != 0; + return ret; } diff --git a/tests/test-media.png b/tests/test-media.png Binary files differnew file mode 100644 index 0000000..2dbbccc --- /dev/null +++ b/tests/test-media.png diff --git a/tests/threaded.c b/tests/threaded.c index 411361c..385156c 100644 --- a/tests/threaded.c +++ b/tests/threaded.c @@ -95,7 +95,7 @@ static void ping () int i; GSList *uris; - server = soup_server_new (NULL); + server = soup_server_new (NULL, NULL); soup_server_listen_local (server, 0, 0, &error); g_assert_no_error (error); |