diff options
author | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2012-02-16 16:09:16 -0600 |
---|---|---|
committer | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2012-02-16 16:09:16 -0600 |
commit | 4462592a15abe78aac66fe7edbf7ed409adb27f5 (patch) | |
tree | 175743f295c5503970a3155a5d3332f9c8df7b2d /src | |
download | libcaldav-4462592a15abe78aac66fe7edbf7ed409adb27f5.tar.gz libcaldav-4462592a15abe78aac66fe7edbf7ed409adb27f5.zip |
Initial import
Diffstat (limited to 'src')
48 files changed, 12232 insertions, 0 deletions
diff --git a/src/.svn/all-wcprops b/src/.svn/all-wcprops new file mode 100644 index 0000000..7bf56a4 --- /dev/null +++ b/src/.svn/all-wcprops @@ -0,0 +1,137 @@ +K 25 +svn:wc:ra_dav:version-url +V 40 +/svnroot/libcaldav/!svn/ver/42/trunk/src +END +delete-caldav-object.c +K 25 +svn:wc:ra_dav:version-url +V 63 +/svnroot/libcaldav/!svn/ver/41/trunk/src/delete-caldav-object.c +END +caldav.h +K 25 +svn:wc:ra_dav:version-url +V 49 +/svnroot/libcaldav/!svn/ver/39/trunk/src/caldav.h +END +get-display-name.c +K 25 +svn:wc:ra_dav:version-url +V 59 +/svnroot/libcaldav/!svn/ver/41/trunk/src/get-display-name.c +END +Makefile.in +K 25 +svn:wc:ra_dav:version-url +V 52 +/svnroot/libcaldav/!svn/ver/42/trunk/src/Makefile.in +END +delete-caldav-object.h +K 25 +svn:wc:ra_dav:version-url +V 62 +/svnroot/libcaldav/!svn/ver/1/trunk/src/delete-caldav-object.h +END +get-display-name.h +K 25 +svn:wc:ra_dav:version-url +V 58 +/svnroot/libcaldav/!svn/ver/1/trunk/src/get-display-name.h +END +get-caldav-report.c +K 25 +svn:wc:ra_dav:version-url +V 60 +/svnroot/libcaldav/!svn/ver/41/trunk/src/get-caldav-report.c +END +modify-caldav-object.c +K 25 +svn:wc:ra_dav:version-url +V 63 +/svnroot/libcaldav/!svn/ver/41/trunk/src/modify-caldav-object.c +END +lock-caldav-object.c +K 25 +svn:wc:ra_dav:version-url +V 61 +/svnroot/libcaldav/!svn/ver/41/trunk/src/lock-caldav-object.c +END +add-caldav-object.c +K 25 +svn:wc:ra_dav:version-url +V 60 +/svnroot/libcaldav/!svn/ver/41/trunk/src/add-caldav-object.c +END +get-caldav-report.h +K 25 +svn:wc:ra_dav:version-url +V 60 +/svnroot/libcaldav/!svn/ver/39/trunk/src/get-caldav-report.h +END +md5.c +K 25 +svn:wc:ra_dav:version-url +V 45 +/svnroot/libcaldav/!svn/ver/1/trunk/src/md5.c +END +options-caldav-server.c +K 25 +svn:wc:ra_dav:version-url +V 64 +/svnroot/libcaldav/!svn/ver/41/trunk/src/options-caldav-server.c +END +caldav-utils.c +K 25 +svn:wc:ra_dav:version-url +V 55 +/svnroot/libcaldav/!svn/ver/42/trunk/src/caldav-utils.c +END +Makefile.am +K 25 +svn:wc:ra_dav:version-url +V 52 +/svnroot/libcaldav/!svn/ver/39/trunk/src/Makefile.am +END +modify-caldav-object.h +K 25 +svn:wc:ra_dav:version-url +V 62 +/svnroot/libcaldav/!svn/ver/1/trunk/src/modify-caldav-object.h +END +lock-caldav-object.h +K 25 +svn:wc:ra_dav:version-url +V 60 +/svnroot/libcaldav/!svn/ver/9/trunk/src/lock-caldav-object.h +END +add-caldav-object.h +K 25 +svn:wc:ra_dav:version-url +V 59 +/svnroot/libcaldav/!svn/ver/1/trunk/src/add-caldav-object.h +END +caldav.c +K 25 +svn:wc:ra_dav:version-url +V 49 +/svnroot/libcaldav/!svn/ver/39/trunk/src/caldav.c +END +md5.h +K 25 +svn:wc:ra_dav:version-url +V 45 +/svnroot/libcaldav/!svn/ver/1/trunk/src/md5.h +END +options-caldav-server.h +K 25 +svn:wc:ra_dav:version-url +V 64 +/svnroot/libcaldav/!svn/ver/10/trunk/src/options-caldav-server.h +END +caldav-utils.h +K 25 +svn:wc:ra_dav:version-url +V 55 +/svnroot/libcaldav/!svn/ver/34/trunk/src/caldav-utils.h +END diff --git a/src/.svn/entries b/src/.svn/entries new file mode 100644 index 0000000..ac8f411 --- /dev/null +++ b/src/.svn/entries @@ -0,0 +1,776 @@ +10 + +dir +42 +https://libcaldav.svn.sourceforge.net/svnroot/libcaldav/trunk/src +https://libcaldav.svn.sourceforge.net/svnroot/libcaldav + + + +2010-05-29T15:45:56.236020Z +42 +miras + + + + + + + + + + + + + + +6bb92309-7149-0410-b568-bc30dfd0a1d5 + +delete-caldav-object.c +file + + + + +2010-05-31T07:07:58.000000Z +fab35eb73b0fb9581aa487decd83b6c6 +2010-05-25T20:53:04.081178Z +41 +miras + + + + + + + + + + + + + + + + + + + + + +8643 + +caldav.h +file + + + + +2010-05-31T07:07:58.000000Z +8b27bf73c1c7bb060ad21312f5f9af05 +2010-05-23T20:43:55.305076Z +39 +miras + + + + + + + + + + + + + + + + + + + + + +11331 + +get-display-name.c +file + + + + +2010-05-31T07:07:58.000000Z +e605700922c480f35815279a20484248 +2010-05-25T20:53:04.081178Z +41 +miras + + + + + + + + + + + + + + + + + + + + + +4631 + +Makefile.in +file + + + + +2010-05-31T07:07:58.000000Z +dd17920f8308f34a5cb6f4c546fb59ea +2010-05-29T15:45:56.236020Z +42 +miras + + + + + + + + + + + + + + + + + + + + + +21254 + +delete-caldav-object.h +file + + + + +2010-05-31T07:07:58.000000Z +a73e19682e34e9c903940f3f0fd48435 +2008-03-29T18:04:01.076545Z +1 +miras + + + + + + + + + + + + + + + + + + + + + +1244 + +get-display-name.h +file + + + + +2010-05-31T07:07:58.000000Z +78c67f8d08738e976c2decb360d90eac +2008-03-29T18:04:01.076545Z +1 +miras + + + + + + + + + + + + + + + + + + + + + +1240 + +get-caldav-report.c +file + + + + +2010-05-31T07:07:58.000000Z +fd4ef5b191fc0b3a7ea76b14a078e06c +2010-05-25T20:53:04.081178Z +41 +miras + + + + + + + + + + + + + + + + + + + + + +8572 + +modify-caldav-object.c +file + + + + +2010-05-31T07:07:58.000000Z +18119fb7cabfe0a193035aad23ccacef +2010-05-25T20:53:04.081178Z +41 +miras + + + + + + + + + + + + + + + + + + + + + +8992 + +lock-caldav-object.c +file + + + + +2010-05-31T07:07:58.000000Z +d94186940ab1caf2a060ae6bc4a836ca +2010-05-25T20:53:04.081178Z +41 +miras + + + + + + + + + + + + + + + + + + + + + +9360 + +add-caldav-object.c +file + + + + +2010-05-31T07:07:58.000000Z +9272e45b03647c432efbc8d8a6217537 +2010-05-25T20:53:04.081178Z +41 +miras + + + + + + + + + + + + + + + + + + + + + +4341 + +get-caldav-report.h +file + + + + +2010-05-31T07:07:58.000000Z +4063fdac318ff1c4b8d5845fd6d526f3 +2010-05-23T20:43:55.305076Z +39 +miras + + + + + + + + + + + + + + + + + + + + + +1609 + +md5.c +file + + + + +2010-05-31T07:07:58.000000Z +4c393b2163ae5ab0d8461709e4680b68 +2008-03-29T18:04:01.076545Z +1 +miras + + + + + + + + + + + + + + + + + + + + + +11719 + +options-caldav-server.c +file + + + + +2010-05-31T07:07:58.000000Z +bca50708fda598af1cf6c3171bf01e0d +2010-05-25T20:53:04.081178Z +41 +miras + + + + + + + + + + + + + + + + + + + + + +4545 + +Makefile.am +file + + + + +2010-05-31T07:07:58.000000Z +d4e689953457ac1f09d9bc85d4d3e855 +2010-05-23T20:43:55.305076Z +39 +miras + + + + + + + + + + + + + + + + + + + + + +1098 + +modify-caldav-object.h +file + + + + +2010-05-31T07:07:58.000000Z +7369d16e16cb3d208a3d576f69685f76 +2008-03-29T18:04:01.076545Z +1 +miras + + + + + + + + + + + + + + + + + + + + + +1244 + +caldav-utils.c +file + + + + +2010-05-31T07:07:58.000000Z +fb4b55bfc19b10aba28e8e047a9493bc +2010-05-29T15:45:56.236020Z +42 +miras + + + + + + + + + + + + + + + + + + + + + +17444 + +lock-caldav-object.h +file + + + + +2010-05-31T07:07:58.000000Z +1f07eba06bb4fe89eb8c42a6fb67df55 +2008-04-07T22:11:32.666799Z +9 +miras + + + + + + + + + + + + + + + + + + + + + +2088 + +add-caldav-object.h +file + + + + +2010-05-31T07:07:58.000000Z +4f4e1336005bc1eb283696d92ce6075f +2008-03-29T18:04:01.076545Z +1 +miras + + + + + + + + + + + + + + + + + + + + + +1236 + +caldav.c +file + + + + +2010-05-31T07:07:58.000000Z +6307ba82867c515caa761ea0e03c9e0d +2010-05-23T20:43:55.305076Z +39 +miras + + + + + + + + + + + + + + + + + + + + + +21199 + +md5.h +file + + + + +2010-05-31T07:07:58.000000Z +5238bb8df57b98358aa184bb97d686d5 +2008-03-29T18:04:01.076545Z +1 +miras + + + + + + + + + + + + + + + + + + + + + +1517 + +options-caldav-server.h +file + + + + +2010-05-31T07:07:58.000000Z +100c2c473b064d0b0c99135a3f8c8992 +2010-02-27T11:17:33.222356Z +10 +miras + + + + + + + + + + + + + + + + + + + + + +1774 + +caldav-utils.h +file + + + + +2010-05-31T07:07:58.000000Z +790120ed34d83d7a82dd21e6543c65fc +2010-04-07T22:56:57.177660Z +34 +miras + + + + + + + + + + + + + + + + + + + + + +5300 + diff --git a/src/.svn/text-base/Makefile.am.svn-base b/src/.svn/text-base/Makefile.am.svn-base new file mode 100644 index 0000000..59d3126 --- /dev/null +++ b/src/.svn/text-base/Makefile.am.svn-base @@ -0,0 +1,53 @@ +AUTOMAKE_OPTIONS = gnu + +INCLUDES = @CURL_CFLAGS@ @GLIB_CFLAGS@ \ + -I$(top_srcdir) -I$(top_builddir) + +if STATIC_LINK +noinst_LTLIBRARIES = libcaldav.la +endif + +if DYNAMIC_LINK +lib_LTLIBRARIES = libcaldav.la +endif + +libcaldav_la_LDFLAGS = -version-info @LIBVERSION@ + +libcaldav_la_SOURCES = \ + caldav.h \ + caldav.c \ + add-caldav-object.c \ + add-caldav-object.h \ + delete-caldav-object.c \ + delete-caldav-object.h \ + modify-caldav-object.c \ + modify-caldav-object.h \ + get-caldav-report.c \ + get-caldav-report.h \ + get-display-name.c \ + get-display-name.h \ + caldav-utils.c \ + caldav-utils.h \ + md5.c \ + md5.h \ + options-caldav-server.c \ + options-caldav-server.h \ + lock-caldav-object.c \ + lock-caldav-object.h \ + get-freebusy-report.c \ + get-freebusy-report.h + +libcaldav_includedir=$(includedir)/libcaldav-@VERSION@ +libcaldav_include_HEADERS = caldav.h + +noinst_HEADERS = \ + add-caldav-object.h \ + delete-caldav-object.h \ + modify-caldav-object.h \ + get-caldav-report.h \ + caldav-utils.h + +libcaldav_la_LIBADD = \ + @CURL_LIBS@ \ + @GLIB_LIBS@ + diff --git a/src/.svn/text-base/Makefile.in.svn-base b/src/.svn/text-base/Makefile.in.svn-base new file mode 100644 index 0000000..5803fc2 --- /dev/null +++ b/src/.svn/text-base/Makefile.in.svn-base @@ -0,0 +1,649 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = src +DIST_COMMON = $(libcaldav_include_HEADERS) $(noinst_HEADERS) \ + $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_prog_doxygen.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(libcaldav_includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +libcaldav_la_DEPENDENCIES = +am_libcaldav_la_OBJECTS = caldav.lo add-caldav-object.lo \ + delete-caldav-object.lo modify-caldav-object.lo \ + get-caldav-report.lo get-display-name.lo caldav-utils.lo \ + md5.lo options-caldav-server.lo lock-caldav-object.lo \ + get-freebusy-report.lo +libcaldav_la_OBJECTS = $(am_libcaldav_la_OBJECTS) +libcaldav_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libcaldav_la_LDFLAGS) $(LDFLAGS) -o $@ +@DYNAMIC_LINK_TRUE@am_libcaldav_la_rpath = -rpath $(libdir) +@STATIC_LINK_TRUE@am_libcaldav_la_rpath = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libcaldav_la_SOURCES) +DIST_SOURCES = $(libcaldav_la_SOURCES) +HEADERS = $(libcaldav_include_HEADERS) $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CURL_CFLAGS = @CURL_CFLAGS@ +CURL_LIBS = @CURL_LIBS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DX_CONFIG = @DX_CONFIG@ +DX_DOCDIR = @DX_DOCDIR@ +DX_DOT = @DX_DOT@ +DX_DOXYGEN = @DX_DOXYGEN@ +DX_DVIPS = @DX_DVIPS@ +DX_EGREP = @DX_EGREP@ +DX_ENV = @DX_ENV@ +DX_FLAG_chi = @DX_FLAG_chi@ +DX_FLAG_chm = @DX_FLAG_chm@ +DX_FLAG_doc = @DX_FLAG_doc@ +DX_FLAG_dot = @DX_FLAG_dot@ +DX_FLAG_html = @DX_FLAG_html@ +DX_FLAG_man = @DX_FLAG_man@ +DX_FLAG_pdf = @DX_FLAG_pdf@ +DX_FLAG_ps = @DX_FLAG_ps@ +DX_FLAG_rtf = @DX_FLAG_rtf@ +DX_FLAG_xml = @DX_FLAG_xml@ +DX_HHC = @DX_HHC@ +DX_LATEX = @DX_LATEX@ +DX_MAKEINDEX = @DX_MAKEINDEX@ +DX_PDFLATEX = @DX_PDFLATEX@ +DX_PERL = @DX_PERL@ +DX_PROJECT = @DX_PROJECT@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBVERSION = @LIBVERSION@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = gnu +INCLUDES = @CURL_CFLAGS@ @GLIB_CFLAGS@ \ + -I$(top_srcdir) -I$(top_builddir) + +@STATIC_LINK_TRUE@noinst_LTLIBRARIES = libcaldav.la +@DYNAMIC_LINK_TRUE@lib_LTLIBRARIES = libcaldav.la +libcaldav_la_LDFLAGS = -version-info @LIBVERSION@ +libcaldav_la_SOURCES = \ + caldav.h \ + caldav.c \ + add-caldav-object.c \ + add-caldav-object.h \ + delete-caldav-object.c \ + delete-caldav-object.h \ + modify-caldav-object.c \ + modify-caldav-object.h \ + get-caldav-report.c \ + get-caldav-report.h \ + get-display-name.c \ + get-display-name.h \ + caldav-utils.c \ + caldav-utils.h \ + md5.c \ + md5.h \ + options-caldav-server.c \ + options-caldav-server.h \ + lock-caldav-object.c \ + lock-caldav-object.h \ + get-freebusy-report.c \ + get-freebusy-report.h + +libcaldav_includedir = $(includedir)/libcaldav-@VERSION@ +libcaldav_include_HEADERS = caldav.h +noinst_HEADERS = \ + add-caldav-object.h \ + delete-caldav-object.h \ + modify-caldav-object.h \ + get-caldav-report.h \ + caldav-utils.h + +libcaldav_la_LIBADD = \ + @CURL_LIBS@ \ + @GLIB_LIBS@ + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libcaldav.la: $(libcaldav_la_OBJECTS) $(libcaldav_la_DEPENDENCIES) + $(libcaldav_la_LINK) $(am_libcaldav_la_rpath) $(libcaldav_la_OBJECTS) $(libcaldav_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/add-caldav-object.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/caldav-utils.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/caldav.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete-caldav-object.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get-caldav-report.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get-display-name.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get-freebusy-report.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock-caldav-object.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/modify-caldav-object.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options-caldav-server.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-libcaldav_includeHEADERS: $(libcaldav_include_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(libcaldav_includedir)" || $(MKDIR_P) "$(DESTDIR)$(libcaldav_includedir)" + @list='$(libcaldav_include_HEADERS)'; test -n "$(libcaldav_includedir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libcaldav_includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libcaldav_includedir)" || exit $$?; \ + done + +uninstall-libcaldav_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libcaldav_include_HEADERS)'; test -n "$(libcaldav_includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(libcaldav_includedir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(libcaldav_includedir)" && rm -f $$files + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libcaldav_includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-libcaldav_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES \ + uninstall-libcaldav_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \ + ctags distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES \ + install-libcaldav_includeHEADERS install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-libLTLIBRARIES \ + uninstall-libcaldav_includeHEADERS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/.svn/text-base/add-caldav-object.c.svn-base b/src/.svn/text-base/add-caldav-object.c.svn-base new file mode 100644 index 0000000..544c069 --- /dev/null +++ b/src/.svn/text-base/add-caldav-object.c.svn-base @@ -0,0 +1,131 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "add-caldav-object.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * Function for adding a new event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_add(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + gchar* url; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: text/calendar; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "If-None-Match: *"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + data.trace_ascii = settings->trace_ascii; + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + gchar* tmp = random_file_name(settings->file); + gchar* s = rebuild_url(settings, NULL); + if (g_str_has_suffix(s, "/")) { + url = g_strdup_printf("%slibcaldav-%s.ics", s, tmp); + } + else { + url = g_strdup_printf("%s/libcaldav-%s.ics", s, tmp); + } + g_free(s); + g_free(tmp); + curl_easy_setopt(curl, CURLOPT_URL, url); + tmp = g_strdup(settings->file); + g_free(settings->file); + settings->file = verify_uid(tmp); + g_free(tmp); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, settings->file); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(settings->file)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 201) { + error->str = g_strdup(chunk.memory); + error->code = code; + result = TRUE; + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_slist_free_all(http_header); + curl_easy_cleanup(curl); + return result; +} + diff --git a/src/.svn/text-base/add-caldav-object.h.svn-base b/src/.svn/text-base/add-caldav-object.h.svn-base new file mode 100644 index 0000000..0756e74 --- /dev/null +++ b/src/.svn/text-base/add-caldav-object.h.svn-base @@ -0,0 +1,35 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __ADD_CALDAV_OBJECT_H__ +#define __ADD_CALDAV_OBJECT_H__ + +#include "caldav-utils.h" +#include "caldav.h" + +/** + * Function for adding a new event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_add(caldav_settings* settings, caldav_error* error); + +#endif + diff --git a/src/.svn/text-base/caldav-utils.c.svn-base b/src/.svn/text-base/caldav-utils.c.svn-base new file mode 100644 index 0000000..50ce7ae --- /dev/null +++ b/src/.svn/text-base/caldav-utils.c.svn-base @@ -0,0 +1,713 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "caldav-utils.h" +#include "md5.h" +#include <glib.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <curl/curl.h> +#include <ctype.h> + +/** + * This function is burrowed from the libcurl documentation + * @param text + * @param stream + * @param ptr + * @param size + * @param nohex + */ +void dump(const char* text, FILE* stream, char* ptr, size_t size, char nohex) { + size_t i; + size_t c; + + unsigned int width=0x10; + + if(nohex) + /* without the hex output, we can fit more on screen */ + width = 0x40; + fprintf(stream, "%s, %zd bytes (0x%zx)\n", text, size, size); + for(i=0; i<size; i+= width) { + fprintf(stream, "%04zx: ", i); + if(!nohex) { + /* hex not disabled, show it */ + for(c = 0; c < width; c++) { + if(i+c < size) + fprintf(stream, "%02x ", ptr[i+c]); + else + fputs(" ", stream); + } + } + for(c = 0; (c < width) && (i+c < size); c++) { + /* check for 0D0A; if found, skip past and start a new line of output */ + if (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) { + i+=(c+2-width); + break; + } + fprintf(stream, "%c",(ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.'); + /* check again for 0D0A, to avoid an extra \n if it's at width */ + if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) { + i+=(c+3-width); + break; + } + } + fputc('\n', stream); /* newline */ + } + fflush(stream); +} + +/** + * This function is burrowed from the libcurl documentation + * @param handle + * @param type + * @param data + * @param size + * @param userp + * @return + */ +int my_trace(CURL* handle, curl_infotype type, char* data, size_t size, void* userp) { + struct config_data* config = (struct config_data *)userp; + const char* text; + (void)handle; /* prevent compiler warning */ + + switch (type) { + case CURLINFO_TEXT: + fprintf(stderr, "== Info: %s", data); + default: /* in case a new one is introduced to shock us */ + return 0; + case CURLINFO_HEADER_OUT: + text = "=> Send header"; + break; + case CURLINFO_DATA_OUT: + text = "=> Send data"; + break; + case CURLINFO_SSL_DATA_OUT: + text = "=> Send SSL data"; + break; + case CURLINFO_HEADER_IN: + text = "<= Recv header"; + break; + case CURLINFO_DATA_IN: + text = "<= Recv data"; + break; + case CURLINFO_SSL_DATA_IN: + text = "<= Recv SSL data"; + break; + } + dump(text, stderr, data, size, config->trace_ascii); + return 0; +} + +/** + * This function is burrowed from the libcurl documentation + * @param ptr + * @param size + * @return void* to memory region + */ +static void* myrealloc(void* ptr, size_t size) { +/* There might be a realloc() out there that doesn't like reallocing + * NULL pointers, so we take care of it here + * */ + if(ptr) + return realloc(ptr, size); + else + return malloc(size); +} + +/** + * This function is burrowed from the libcurl documentation + * @param ptr + * @param size + * @param nmemb + * @param data + * @return number of written bytes + */ +size_t WriteMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data) { + size_t realsize = size * nmemb; + struct MemoryStruct* mem = (struct MemoryStruct *)data; + mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1); + + if (mem->memory) { + memcpy(&(mem->memory[mem->size]), ptr, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + return realsize; +} + +/** + * This function is burrowed from the libcurl documentation + * @param ptr + * @param size + * @param nmemb + * @param data + * @return number of written bytes + */ +size_t WriteHeaderCallback(void* ptr, size_t size, size_t nmemb, void* data) { + size_t realsize = size * nmemb; + struct MemoryStruct* mem = (struct MemoryStruct *)data; + mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1); + + if (mem->memory) { + memcpy(&(mem->memory[mem->size]), ptr, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + return realsize; +} + +/* +size_t ReadMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data){ + struct MemoryStruct* mem = (struct MemoryStruct *)data; + + memcpy(ptr, mem->memory, mem->size); + return mem->size; +} +*/ + +/** + * Initialize caldav settings structure. + * @param settings @see caldav_settings + */ +void init_caldav_settings(caldav_settings* settings) { + settings->username = NULL; + settings->password = NULL; + settings->url = NULL; + settings->file = NULL; + settings->usehttps = FALSE; + settings->custom_cacert = NULL; + settings->verify_ssl_certificate = TRUE; + settings->debug = FALSE; + settings->trace_ascii = TRUE; + settings->ACTION = UNKNOWN; + settings->start = 0; + settings->end = 0; +} + +/** + * Free memory assigned to caldav settings structure. + * @param settings @see caldav_settings + */ +void free_caldav_settings(caldav_settings* settings) { + if (settings->username) { + g_free(settings->username); + settings->username = NULL; + } + if (settings->password) { + g_free(settings->password); + settings->password = NULL; + } + if (settings->url) { + g_free(settings->url); + settings->url = NULL; + } + if (settings->file) { + g_free(settings->file); + settings->file = NULL; + } + if (settings->custom_cacert) { + g_free(settings->custom_cacert); + settings->custom_cacert = NULL; + } + settings->verify_ssl_certificate = TRUE; + settings->usehttps = FALSE; + settings->debug = FALSE; + settings->trace_ascii = TRUE; + settings->ACTION = UNKNOWN; + settings->start = 0; + settings->end = 0; +} + +static gchar* place_after_hostname(const gchar* start, const gchar* stop) { + gchar* newpos = NULL; + gchar* pos = (gchar *) stop; + gboolean digit = TRUE; + + if (pos && stop && strcmp(start, pos) != 0) { + while (*pos != ':' && strcmp(start, pos) != 0) + --pos; + if (pos > start) { + gchar* tmp = (gchar *) pos + 1; + /* is pos++ a port number */ + while (*tmp != '/' && digit) { + if (isdigit(*tmp) != 0) { + digit = TRUE; + tmp++; + } + else + digit = FALSE; + } + if (digit) { + /* pos was a port number */ + while (*pos != '@' && strcmp(start, pos) != 0) + --pos; + if (strcmp(start, pos) != 0) + newpos = pos; + } + else { + while (*pos != '@' && pos != stop) + pos++; + if (pos != stop) + newpos = pos; + } + } + else { + /* is a username present */ + gchar* tmp = NULL; + while (*pos != '/' && pos != stop) { + if (*pos == '@') + tmp = pos; + pos++; + } + if (tmp && pos != stop) + newpos = tmp; + } + } + return newpos; +} + +/** + * Parse URL + * @param settings @see caldav_settings + * @param url String containing URL to collection + */ +void parse_url(caldav_settings* settings, const char* url) { + char* start; + char* pos; + char* end; + char* login; + + login = pos = end = start = NULL; + if (!url) + return; + if ((pos = strstr(url, "//")) != NULL) { + /* Does the URL use https ?*/ + if (!g_ascii_strncasecmp(url,"https",5) && settings->usehttps == FALSE) { + settings->usehttps=TRUE; + } + start = g_strdup(&(*(pos + 2))); + if ((pos = place_after_hostname(start, strrchr(start, '\0') - 1)) != NULL) { + /* username and/or password present */ + login = g_strndup(start, pos - start); + end = pos; + if ((pos = strrchr(login, ':')) != NULL) { + /* both username and password is present */ + settings->username = g_strndup(login, pos - login); + settings->password = g_strdup(++pos); + } + else { + /* only username present */ + settings->username = g_strdup(login); + settings->password = NULL; + } + g_free(login); + settings->url = g_strdup(++end); + } + else { + /* no username or password present */ + settings->url = g_strdup(start); + settings->username = NULL; + settings->password = NULL; + } + g_free(start); + } +} + +/** + * Find a specific HTTP header from last request + * @param header HTTP header to search for + * @param headers String of HTTP headers from last request + * @param lowcase Should string be returned in all lower case. + * @return The header found or NULL + */ +#define MAX_TOKENS 2 +gchar* get_response_header( + const char* header, gchar* headers, gboolean lowcase) { + gchar* line; + gchar* head = NULL; + gchar* oldhead = NULL; + gchar** buf; + gchar* header_list; + gchar* saveptr; + + header_list = g_strdup(headers); + line = strtok_r(header_list, "\r\n", &saveptr); + if (line != NULL) { + do { + buf = g_strsplit(line, ":", MAX_TOKENS); + if (buf[1] != NULL) { + if (g_ascii_strcasecmp(buf[0], header) == 0) { + if (head) { + oldhead = head; + head = g_strconcat(head, ", ", buf[1], NULL); + g_free(oldhead); + } + else + head = g_strdup(buf[1]); + if (head) + g_strstrip(head); + } + } + g_strfreev(buf); + } while ((line = strtok_r(NULL, "\r\n", &saveptr)) != NULL); + } + g_free(header_list); + if (head) + return (lowcase) ? g_ascii_strdown(head, -1) : head; + else + return NULL; +} + +static const char* VCAL_HEAD = +"BEGIN:VCALENDAR\r\n" +"PRODID:-//CalDAV Calendar//NONSGML libcaldav//EN\r\n" +"VERSION:2.0\r\n"; +static const char* VCAL_FOOT = "END:VCALENDAR"; + +/** + * Parse response from CalDAV server. Internal function. + * @param report Response from server + * @param element XML element to find + * @param type VCalendar element to find + * @param wrap Is this the final parsing or just a part + * @param recursive Stop after first match or not + * @return the parsed result + */ +static gchar* parse_caldav_report_wrap( + char* report, const char* element, const char* type, + gboolean wrap, gboolean recursive) { + char* pos; + char* start; + char* object; + char* tmp_report; + char* tmp; + gchar* response; + gchar* begin_type; + gchar* end_type; + gboolean keep_going = TRUE; + + begin_type = g_strdup_printf("BEGIN:%s", type); + end_type = g_strdup_printf("END:%s", type); + pos = start = object = response = NULL; + tmp_report = g_strdup(report); + while ((pos = strstr(tmp_report, element)) != NULL && keep_going) { + pos = strchr(pos, '>'); + if (!pos) { + break; + } + pos = &(*(pos + 1)); + pos = strstr(pos, begin_type); + if (!pos) { + break; + } + object = &(*(pos + strlen(begin_type))); + object = g_strchug(object); + start = g_strdup(object); + if ((pos = strstr(start, end_type)) == NULL) { + g_free(start); + break; + } + object = g_strndup(start, strlen(start) - strlen(pos)); + if (response) { + tmp = g_strdup(response); + g_free(response); + response = g_strdup_printf("%s%s\r\n%s%s\r\n", + tmp, begin_type, object, end_type); + g_free(tmp); + } + else { + if (wrap) + response = g_strdup_printf("%s%s\r\n%s%s\r\n", + VCAL_HEAD, begin_type, object, end_type); + else + response = g_strdup_printf("%s\r\n%s%s\r\n", + begin_type, object, end_type); + } + if (recursive) { + pos = strchr(pos, '>'); + g_free(tmp_report); + tmp_report = g_strdup(&(*(pos + 1))); + } + else { + keep_going = FALSE; + } + g_free(start); + g_free(object); + } + g_free(tmp_report); + g_free(begin_type); + g_free(end_type); + if (wrap) + if (response) { + object = g_strdup(response); + g_free(response); + response = g_strdup_printf("%s%s", object, VCAL_FOOT); + g_free(object); + } + return response; +} + +/** + * Parse response from CalDAV server + * @param report Response from server + * @param element XML element to find + * @param type VCalendar element to find + * @return the parsed result + */ +gchar* parse_caldav_report(char* report, const char* element, const char* type) { + gchar* response = NULL; + gchar* timezone = NULL; + gchar* temp = NULL; + + if (!report || !element || !type) + return NULL; + /* test for VTIMEZONE. + * Only the first found will be used and this will then + * be the time zone for the entire report + */ + timezone = parse_caldav_report_wrap( + report, element, "VTIMEZONE", FALSE, FALSE); + if (timezone) { + response = g_strdup_printf("%s%s", VCAL_HEAD, timezone); + g_free(timezone); + temp = parse_caldav_report_wrap(report, element, type, FALSE, TRUE); + if (temp) { + gchar* tmp = g_strdup(response); + g_free(response); + response = g_strdup_printf("%s%s%s", tmp, temp, VCAL_FOOT); + g_free(tmp); + g_free(temp); + } + else { + g_free(response); + return NULL; + } + } + else + response = parse_caldav_report_wrap(report, element, type, TRUE, TRUE); + return response; +} + +/** + * Convert a time_t variable to CalDAV DateTime + * @param time a specific date and time + * @return the CalDAV DateTime + */ +gchar* get_caldav_datetime(time_t* time) { + struct tm *current; + gchar* datetime; + + current = localtime(time); + datetime = g_strdup_printf("%d%.2d%.2dT%.2d%.2d%.2dZ", + current->tm_year + 1900, current->tm_mon + 1, current->tm_mday, + current->tm_hour, current->tm_min, current->tm_sec); + return datetime; +} + +/** + * Create a random text string, using MD5. @see caldav_md5_hex_digest() + * @param text some text to randomize + * @return MD5 hash of text + */ +gchar* random_file_name(gchar* text) { + unsigned char* name; + gchar md5sum[33]; + + name = (unsigned char *) g_strdup(text); + caldav_md5_hex_digest(md5sum, name); + g_free(name); + return g_strdup(md5sum); +} + +/** + * Does the event contain a UID element or not. If not add it. + * @param object A specific event + * @return event, eventually added UID + */ +gchar* verify_uid(gchar* object) { + gchar* uid; + gchar* newobj; + gchar* pos; + + newobj = g_strdup(object); + uid = get_response_header("uid", object, TRUE); + if (!uid) { + object = g_strdup(newobj); + g_free(newobj); + pos = strstr(object, "END:VEVENT"); + newobj = g_strndup(object, strlen(object) - strlen(pos)); + newobj = g_strchomp(newobj); + uid = random_file_name(object); + gchar*tmp = g_strdup(newobj); + g_free(newobj); + newobj = g_strdup_printf("%s\r\nUID:libcaldav-%s@tempuri.org\r\n%s", + tmp, uid, pos); + g_free(uid); + g_free(tmp); + g_free(object); + } + else + g_free(uid); + /*uid = g_strdup(newobj); + g_free(newobj);*/ + g_strchomp(newobj); + /*g_free(uid);*/ + return newobj; +} + +/** + * Fetch a URL from a XML element + * @param text String + * @return URL + */ +#define ELEM_HREF "href>" +gchar* get_url(gchar* text) { + gchar* pos; + gchar* url = NULL; + + if ((pos = strstr(text, ELEM_HREF)) == NULL) + return url; + pos = &(*(pos + strlen(ELEM_HREF))); + url = g_strndup(pos, strlen(pos) - strlen(strchr(pos, '<'))); + return url; +} + +/** + * Fetch any element from XML + * @param text String + * @param tag The element to look for + * @return element + */ +gchar* get_tag(const gchar* tag, gchar* text) { + gchar *pos; + gchar* res = NULL; + gchar* the_tag = NULL; + + /*printf("%s\n", text);*/ + the_tag = g_strdup_printf("<%s>", tag); + if ((pos = strstr(text, the_tag)) == NULL) { + g_free(the_tag); + return res; + } + pos = &(*(pos + strlen(the_tag))); + res = g_strndup(pos, strlen(pos) - strlen(strchr(pos, '<'))); + g_free(the_tag); + return res; +} + +/** + * Fetch the etag element from XML + * @param text String + * @return etag + */ +#define ELEM_ETAG "getetag" +gchar* get_etag(gchar* text) { + gchar* etag = NULL; + + etag = get_tag(ELEM_ETAG, text); + /* Maybe namespace prefixed */ + if (!etag) { + etag = get_tag("D:getetag", text); + } + return etag; +} + +/** + * Fetch host from URL + * @param url URL + * @return host + */ +gchar* get_host(gchar* url) { + gchar** buf; + gchar* result = NULL; + + buf = g_strsplit(url, "/", 2); + if (buf[0]) { + result = g_strdup(buf[0]); + } + g_strfreev(buf); + return result; +} + +/** + * rebuild a raw URL with https if needed from the settings + * @param settings caldav_settings + * @param uri URI to use instead of base + * @return URL + */ + +gchar* rebuild_url(caldav_settings* settings, gchar* uri){ + gchar* url = NULL; + gchar* mystr = NULL; + if (settings->usehttps) { + mystr = "https://"; + } else { + mystr = "http://"; + } + if (uri) + url = g_strdup_printf("%s%s", mystr, uri); + else + url = g_strdup_printf("%s%s", mystr,settings->url); + + return url; +} + +/** + * Prepare a curl connection + * @param settings caldav_settings + * @return CURL + */ +CURL* get_curl(caldav_settings* setting) { + CURL* curl; + gchar* userpwd = NULL; + gchar* url = NULL; + + curl = curl_easy_init(); + if (curl) { + if (setting->username) { + if (setting->password) + userpwd = g_strdup_printf("%s:%s", + setting->username, setting->password); + else + userpwd = g_strdup_printf("%s", setting->username); + curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd); + g_free(userpwd); + } + if (setting->verify_ssl_certificate) + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2); + else { + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + } + if (setting->custom_cacert) + curl_easy_setopt(curl, CURLOPT_CAINFO, setting->custom_cacert); + curl_easy_setopt(curl, CURLOPT_USERAGENT, __CALDAV_USERAGENT); + url = rebuild_url(setting, NULL); + curl_easy_setopt(curl, CURLOPT_URL, url); + g_free(url); + } + return (curl) ? curl : NULL; +} diff --git a/src/.svn/text-base/caldav-utils.h.svn-base b/src/.svn/text-base/caldav-utils.h.svn-base new file mode 100644 index 0000000..02c84d7 --- /dev/null +++ b/src/.svn/text-base/caldav-utils.h.svn-base @@ -0,0 +1,221 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __CALDAV_UTILS_H__ +#define __CALDAV_UTILS_H__ + +#include <glib.h> +#include <stdlib.h> +#include <curl/curl.h> +#include "caldav.h" + +/** + * @typedef struct _CALDAV_SETTINGS caldav_settings + * A pointer to a struct _CALDAV_SETTINGS + */ +typedef struct _CALDAV_SETTINGS caldav_settings; + +/** + * @struct _CALDAV_SETTINGS + * A struct used to exchange all user input between various parts + * of the library + */ +struct _CALDAV_SETTINGS { + gchar* username; + gchar* password; + gchar* url; + gchar* file; + gboolean usehttps; + gboolean verify_ssl_certificate; + gchar* custom_cacert; + gboolean debug; + gboolean use_locking; + char trace_ascii; + CALDAV_ACTION ACTION; + time_t start; + time_t end; +}; + +/** + * @typedef struct MemoryStruct memory_ptr + * A pointer to a struct MemoryStruct + */ +typedef struct MemoryStruct memory_ptr; + +/** + * @struct MemoryStruct + * Used to hold messages between the CalDAV server and the library + */ +struct MemoryStruct { + char *memory; + size_t size; +}; + +/** @struct config_data + * Used to exchange user options to the library + */ +struct config_data { + char trace_ascii; +}; + +/** + * This function is burrowed from the libcurl documentation + * @param text + * @param stream + * @param ptr + * @param size + * @param nohex + */ +void dump(const char* text, FILE* stream, char* ptr, size_t size, char nohex); + +/** + * This function is burrowed from the libcurl documentation + * @param handle + * @param type + * @param data + * @param size + * @param userp + * @return + */ +int my_trace(CURL* handle, curl_infotype type, char* data, size_t size, void* userp); + +/** + * This function is burrowed from the libcurl documentation + * @param ptr + * @param size + * @return void* to memory region + */ +size_t WriteMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data); + +/** + * This function is burrowed from the libcurl documentation + * @param ptr + * @param size + * @param nmemb + * @param data + * @return number of written bytes + */ +size_t WriteHeaderCallback(void* ptr, size_t size, size_t nmemb, void* data); + +/*size_t ReadMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data);*/ + +/** + * Initialize caldav settings structure. + * @param settings @see caldav_settings + */ +void init_caldav_settings(caldav_settings* settings); + +/** + * Free momory assigned to caldav settings structure. + * @param settings @see caldav_settings + */ +void free_caldav_settings(caldav_settings* settings); + +/** + * Parse URL + * @param settings @see caldav_settings + * @param url String containing URL to collection + */ +void parse_url(caldav_settings* settings, const char* url); + +/** + * Find a specific HTTP header from last request + * @param header HTTP header to search for + * @param headers String of HTTP headers from last request + * @param lowcase Should string be returned in all lower case. + * @return The header found or NULL + */ +gchar* get_response_header( + const char* header, gchar* headers, gboolean lowcase); + +/** + * Parse response from CalDAV server + * @param report Response from server + * @param element XML element to find + * @param type VCalendar element to find + * @return the parsed result + */ +gchar* parse_caldav_report(char* report, const char* element, const char* type); + +/** + * Convert a time_t variable to CalDAV DateTime + * @param time a specific date and time + * @return the CalDAV DateTime + */ +gchar* get_caldav_datetime(time_t* time); + +/** + * Create a random text string, using MD5. @see caldav_md5_hex_digest() + * @param text some text to randomize + * @return MD5 hash of text + */ +gchar* random_file_name(gchar* text); + +/** + * Does the event contain a UID element or not. If not add it. + * @param object A specific event + * @return event, eventually added UID + */ +gchar* verify_uid(gchar* object); + +/** + * Fetch a URL from a XML element + * @param text String + * @return URL + */ +gchar* get_url(gchar* text); + +/** + * Fetch host from URL + * @param url URL + * @return host + */ +gchar* get_host(gchar* url); + +/** + * Fetch the etag element from XML + * @param text String + * @return etag + */ +gchar* get_etag(gchar* text); + +/** + * Fetch any element from XML + * @param text String + * @return element + */ +gchar* get_tag(const gchar* tag, gchar* text); + + + +/** + * rebuild a raw URL with https if needed from the settings + * @param settings caldav_settings + * @return URL + */ +gchar* rebuild_url(caldav_settings* setting, gchar* uri); + +/** + * Prepare a curl connection + * @param settings caldav_settings + * @return CURL + */ +CURL* get_curl(caldav_settings* setting); + +#endif diff --git a/src/.svn/text-base/caldav.c.svn-base b/src/.svn/text-base/caldav.c.svn-base new file mode 100644 index 0000000..193429c --- /dev/null +++ b/src/.svn/text-base/caldav.c.svn-base @@ -0,0 +1,745 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "caldav.h" +#include "caldav-utils.h" +#include "get-caldav-report.h" +#include "add-caldav-object.h" +#include "delete-caldav-object.h" +#include "modify-caldav-object.h" +#include "get-display-name.h" +#include "options-caldav-server.h" +#include "get-freebusy-report.h" +#include <curl/curl.h> +#include <glib.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static void init_runtime(runtime_info* info) { + if (! info) + return; + if (! info->error) + info->error = g_new0(caldav_error, 1); + if (! info->options) { + info->options = g_new0(debug_curl, 1); + info->options->trace_ascii = 1; + info->options->debug = 0; + info->options->verify_ssl_certificate = TRUE; + info->options->use_locking = TRUE; + info->options->custom_cacert = NULL; + } +} + +/** + * @param curl An instance of libcurl. + * @param settings Defines CalDAV resource. Receiver is responsible for freeing + * the memory. URL is part of the structure. [http://][username:password@]host[:port]/url-path. + * See (RFC1738). + * @return FALSE (zero) mens URL does not reference a CalDAV calendar + * resource. TRUE if the URL does reference a CalDAV calendar resource. + */ +static gboolean test_caldav_enabled(CURL* curl, + caldav_settings* settings, + caldav_error* error) { + return caldav_getoptions(curl, settings, NULL, error, TRUE); +} + +/* + * @param settings An instance of caldav_settings. @see caldav_settings + * @return TRUE if there was an error. Error can be in libcurl, in libcaldav, + * or an error related to the CalDAV protocol. + */ +static gboolean make_caldav_call(caldav_settings* settings, + runtime_info* info) { + CURL* curl; + gboolean result = FALSE; + + g_return_val_if_fail(info != NULL, TRUE); + + curl = get_curl(settings); + if (!curl) { + info->error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + if (!test_caldav_enabled(curl, settings, info->error)) { + g_free(settings->file); + settings->file = NULL; + curl_easy_cleanup(curl); + return TRUE; + } + curl_easy_cleanup(curl); + switch (settings->ACTION) { + case GETALL: result = caldav_getall(settings, info->error); break; + case GET: result = caldav_getrange(settings, info->error); break; + case ADD: result = caldav_add(settings, info->error); break; + case DELETE: result = caldav_delete(settings, info->error); break; + case MODIFY: result = caldav_modify(settings, info->error); break; + case GETCALNAME: result = caldav_getname(settings, info->error); break; + case FREEBUSY: result = caldav_freebusy(settings, info->error); break; + default: break; + } + return result; +} + +/** + * Function for adding a new event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_add_object(const char* object, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + init_caldav_settings(&settings); + settings.file = g_strdup(object); + settings.ACTION = ADD; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for deleting an event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_delete_object(const char* object, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + init_caldav_settings(&settings); + settings.file = g_strdup(object); + settings.ACTION = DELETE; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for modifying an event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_modify_object(const char* object, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + init_caldav_settings(&settings); + settings.file = g_strdup(object); + settings.ACTION = MODIFY; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for getting a collection of events determined by time range. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param start time_t variable specifying start and end for range. Both + * are included in range. + * @param end time_t variable specifying start and end for range. Both + * are included in range. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_object(response *result, + time_t start, + time_t end, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + if (!result) { + result = malloc(sizeof(response *)); + memset(result, '\0', sizeof(response *)); + } + init_caldav_settings(&settings); + settings.ACTION = GET; + settings.start = start; + settings.end = end; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + result->msg = NULL; + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + result->msg = g_strdup(settings.file); + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for getting all events from the collection. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_getall_object(response* result, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + if (!result) { + result = malloc(sizeof(response *)); + memset(result, '\0', sizeof(response *)); + } + init_caldav_settings(&settings); + settings.ACTION = GETALL; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + result->msg = NULL; + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + result->msg = g_strdup(settings.file); + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for getting the stored display name for the collection. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_displayname(response* result, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + if (!result) { + result = malloc(sizeof(response *)); + memset(result, '\0', sizeof(response *)); + } + init_caldav_settings(&settings); + settings.ACTION = GETCALNAME; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + result->msg = NULL; + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + result->msg = g_strdup(settings.file); + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function to test wether a calendar resource is CalDAV enabled or not. + * @param URL Defines CalDAV resource. Receiver is responsible for + * freeing the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @result 0 (zero) means no CalDAV support, otherwise CalDAV support + * detechted. + */ +int caldav_enabled_resource(const char* URL, runtime_info* info) { + CURL* curl; + caldav_settings settings; + struct config_data data; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + init_caldav_settings(&settings); + + parse_url(&settings, URL); + curl = get_curl(&settings); + if (!curl) { + info->error->code = -1; + info->error->str = g_strdup("Could not initialize libcurl"); + settings.file = NULL; + return TRUE; + } + + if (info->options->trace_ascii) + data.trace_ascii = 1; + else + data.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + + if (info->options->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + gboolean res = test_caldav_enabled(curl, &settings, info->error); + free_caldav_settings(&settings); + curl_easy_cleanup(curl); + return (res && (info->error->code == 0 || info->error->code == 200)) ? 1 : 0; +} + +/** + * Function for getting free/busy information. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param start time_t variable specifying start and end for range. Both + * are included in range. + * @param end time_t variable specifying start and end for range. Both + * are included in range. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_freebusy(response *result, + time_t start, + time_t end, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + if (!result) { + result = malloc(sizeof(response *)); + memset(result, '\0', sizeof(response *)); + } + init_caldav_settings(&settings); + settings.ACTION = FREEBUSY; + settings.start = start; + settings.end = end; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + result->msg = NULL; + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + result->msg = g_strdup(settings.file); + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function which supports sending various options inside the library. + * @param curl_options A struct debug_curl. See debug_curl. + */ +void caldav_set_options(debug_curl curl_options) { +} + +/** + * @deprecated Function to call in case of errors. + * Caller provides a pointer to a local caldav_error structure. + * Caldav_get_error will initialize pointer if NULL. + * Caller is responsible for freeing returned memory. + * After the first call the internal error buffer is reset. + * @param lib_error A pointer to a struct _caldav_error. @see _caldav_error + * @return An initialized caldav_error pointer to memory where error + * messages can be found from the last call to the library. + */ +caldav_error* caldav_get_error(caldav_error* lib_error) { + if (!lib_error) { + lib_error = g_new0(caldav_error, 1); + } + return lib_error; +} + +/** + * Function for freeing memory for a previous initialization of a + * caldav_error. @see caldav_get_error() + * Caller provides a pointer to a local caldav_error structure. + * @param lib_error A pointer to a struct _caldav_error. @see _caldav_error + */ +void caldav_free_error(caldav_error* lib_error) { + if (lib_error->str) + g_free(lib_error->str); + g_free(lib_error); + lib_error = NULL; +} + +/** + * Function to call to get a list of supported CalDAV options for a server + * @param URL Defines CalDAV resource. Receiver is responsible for + * freeing the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @result A list of available options or NULL in case of any error. + */ +char** caldav_get_server_options(const char* URL, runtime_info* info) { + CURL* curl; + caldav_settings settings; + response server_options; + gchar** option_list = NULL; + gchar** tmp; + gboolean res = FALSE; + + g_return_val_if_fail(info != NULL, NULL); + + init_runtime(info); + tmp = option_list = NULL; + init_caldav_settings(&settings); + + parse_url(&settings, URL); + curl = get_curl(&settings); + if (!curl) { + info->error->code = -1; + info->error->str = g_strdup("Could not initialize libcurl"); + settings.file = NULL; + return NULL; + } + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + + res = caldav_getoptions(curl, &settings, &server_options, info->error, FALSE); + if (res) { + if (server_options.msg) { + option_list = g_strsplit(server_options.msg, ", ", 0); + tmp = &(*(option_list)); + while (*tmp) { + g_strstrip(*tmp++); + } + } + } + free_caldav_settings(&settings); + curl_easy_cleanup(curl); + return (option_list) ? option_list : NULL; +} + +/** + * Function for getting an initialized runtime_info structure + * @return runtime_info. @see runtime_info + */ +runtime_info* caldav_get_runtime_info() { + runtime_info* rt_info; + + rt_info = g_new0(runtime_info, 1); + rt_info->error = g_new0(caldav_error, 1); + rt_info->options = g_new0(debug_curl, 1); + + return rt_info; +} + +/** + * Function for freeing memory for a previous initialization of an info + * structure + * @param info Address to a pointer to a runtime_info structure. @see + * runtime_info + */ +void caldav_free_runtime_info(runtime_info** info) { + runtime_info* ri; + + if (*info) { + ri = *info; + if (ri->error) { + if (ri->error->str) + g_free(ri->error->str); + g_free(ri->error); + ri->error = NULL; + } + if (ri->options) { + if (ri->options->custom_cacert) + g_free(ri->options->custom_cacert); + g_free(ri->options); + ri->options = NULL; + } + g_free(ri); + *info = ri = NULL; + } +} + +/** + * Function for getting an initialized response structure + * @return response. @see _response + */ +response* caldav_get_response() { + response* r; + + r = g_new0(response, 1); + + return r; +} + +/** + * Function for freeing memory for a previous initialization of an response + * structure + * @param info Address to a pointer to a response structure. @see + * _response + */ +void caldav_free_response(response** resp) { + response* r; + + if (*resp) { + r = *resp; + if (r->msg) + g_free(r->msg); + g_free(r); + *resp = r = NULL; + } +} diff --git a/src/.svn/text-base/caldav.h.svn-base b/src/.svn/text-base/caldav.h.svn-base new file mode 100644 index 0000000..28cb24e --- /dev/null +++ b/src/.svn/text-base/caldav.h.svn-base @@ -0,0 +1,343 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/** + * @file caldav.h + * @brief interface to the caldav library. + * The library conforms to RFC4791. For further information follow this + * link http://www.ietf.org/rfc/rfc4791.txt + */ + +/** + * @mainpage + * This document is the documentation for the public interface to libcaldav. + * If you want to study the implementation look for the developers API. + * + * The libray and documentation is Copyright (c) 2008 Michael Rasmussen + * (mir@datanom.net) + * + * License for the source code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * License for the documentation. + * + * Permission is granted to copy, distribute and/or modify this document + * under the terms of the GNU Free Documentation License, Version 1.2 + * or any later version published by the Free Software Foundation; + * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + * Texts. + */ + +#ifndef __CALDAV_H__ +#define __CALDAV_H__ + +#include <time.h> + +/* For debug purposes */ +/** + * @typedef struct debug_curl + * A struct used to set internal options in the library + */ +typedef struct { + int trace_ascii; /** @var int trace_ascii + * 0 or 1 + */ + int debug; /** @var int debug + * 0 or 1 + */ + int verify_ssl_certificate; + int use_locking; + char* custom_cacert; +} debug_curl; + +/** + * @typedef struct _caldav_error caldav_error + * Pointer to a caldav_error structure + */ +typedef struct _caldav_error caldav_error; + +/** + * @struct _caldav_error + * A struct for storing error codes and messages + */ +struct _caldav_error { + long code; /** + * @var long code + * if < 0 internal error > 0 CalDAV protocol error. + */ + char* str; /** @var char* str + * For storing human readable error message + */ +}; + +/** + * @typedef struct runtime_info + * Pointer to a runtime structure holding debug and error information + */ +typedef struct { + caldav_error* error; + debug_curl* options; +} runtime_info; + +/* CalDAV is defined in RFC4791 */ + +/* Buffer to hold response */ +/** + * @typedef struct _response response + * Pointer to a _response structure + */ +typedef struct _response response; + +/** + * @struct _response + * A struct used for returning messages from the library to users + */ +struct _response { + char* msg; /** @var char* msg + * String for storing response + */ +}; + +/** + * @enum CALDAV_ACTION specifies supported CalDAV actions. + * UNKNOWN. An unknown action. + * ADD. Add a CalDAV calendar object. + * DELETE. Delete a CalDAV calendar object. + * MODIFY. Modify a CalDAV calendar object. + * GET. Get one or more CalDAV calendar object(s). + * GETALL. Get all CalDAV calendar objects. + */ +typedef enum { + UNKNOWN, + ADD, + DELETE, + FREEBUSY, + MODIFY, + GET, + GETALL, + GETCALNAME, + ISCALDAV, + OPTIONS +} CALDAV_ACTION; + +/** + * @enum CALDAV_RESPONSE specifies CalDAV error states. + * OK (HTTP 200). Request was satisfied. + * FORBIDDEN (HTTP 403). Access not allowed. Dont repeat request. + * CONFLICT (HTTP 409). Conflict between current state of CalDAV collection + * and request. Client must solve the conflict and then resend request. + * LOCKED (HTTP 423). Locking failed. + */ +typedef enum { + OK, + FORBIDDEN, + CONFLICT, + LOCKED, + NOTIMPLEMENTED +} CALDAV_RESPONSE; + + +#ifndef __CALDAV_USERAGENT +#define __CALDAV_USERAGENT "libcurl-agent/0.1" +#endif + + +/** + * Function for adding a new event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_add_object(const char* object, + const char* URL, + runtime_info* info); + +/** + * Function for deleting an event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_delete_object(const char* object, + const char* URL, + runtime_info* info); + +/** + * Function for modifying an event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_modify_object(const char* object, + const char* URL, + runtime_info* info); + +/** + * Function for getting a collection of events determined by time range. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param start time_t variable specifying start for range. Included in search. + * @param end time_t variable specifying end for range. Included in search. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_object(response* result, + time_t start, + time_t end, + const char* URL, + runtime_info* info); + +/** + * Function for getting all events from the collection. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_getall_object(response* result, + const char* URL, + runtime_info* info); + +/** + * Function for getting the stored display name for the collection. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_displayname(response* result, + const char* URL, + runtime_info* info); + +/** + * Function to test wether a calendar resource is CalDAV enabled or not. + * @param URL Defines CalDAV resource. Receiver is responsible for + * freeing the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @result 0 (zero) means no CalDAV support, otherwise CalDAV support + * detechted. + */ +int caldav_enabled_resource(const char* URL, runtime_info* info); + +/** + * Function for getting free/busy information. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param start time_t variable specifying start and end for range. Both + * are included in range. + * @param end time_t variable specifying start and end for range. Both + * are included in range. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_freebusy(response *result, + time_t start, + time_t end, + const char* URL, + runtime_info* info); + +/** + * @deprecated Always returns an initialized empty caldav_error + * Function to call in case of errors. + * Caller provides a pointer to a local caldav_error structure. + * Caldav_get_error will initialize pointer if NULL. + * Caller is responsible for freeing returned memory. + * After the first call the internal error buffer is reset. + * @param lib_error A pointer to a struct _caldav_error. @see _caldav_error + * @return An initialized caldav_error pointer to memory where error + * messages can be found from the last call to the library. + */ +caldav_error* caldav_get_error(caldav_error* lib_error); + +/** + * Function for freeing memory for a previous initialization of a + * caldav_error. @see caldav_get_error() + * Caller provides a pointer to a local caldav_error structure. + * @param lib_error A pointer to a struct _caldav_error. @see _caldav_error + */ +void caldav_free_error(caldav_error* lib_error); + +/* Setting various options in library */ + +/** + * @deprecated Does nothing + * Function which supports sending various options inside the library. + * @param curl_options A struct debug_curl. See debug_curl. + */ +void caldav_set_options(debug_curl curl_options); + +/** + * Function to call to get a list of supported CalDAV options for a server + * @param URL Defines CalDAV resource. Receiver is responsible for + * freeing the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @result A list of available options or NULL in case of any error. + */ +char** caldav_get_server_options(const char* URL, runtime_info* info); + +/** + * Function for getting an initialized runtime_info structure + * @return runtime_info. @see runtime_info + */ +runtime_info* caldav_get_runtime_info(); + +/** + * Function for freeing memory for a previous initialization of an info + * structure + * @param info Address to a pointer to a runtime_info structure. @see + * runtime_info + */ +void caldav_free_runtime_info(runtime_info** info); + +/** + * Function for getting an initialized response structure + * @return response. @see _response + */ +response* caldav_get_response(); + +/** + * Function for freeing memory for a previous initialization of an response + * structure + * @param info Address to a pointer to a response structure. @see + * _response + */ +void caldav_free_response(response** info); + +#endif diff --git a/src/.svn/text-base/delete-caldav-object.c.svn-base b/src/.svn/text-base/delete-caldav-object.c.svn-base new file mode 100644 index 0000000..adaa916 --- /dev/null +++ b/src/.svn/text-base/delete-caldav-object.c.svn-base @@ -0,0 +1,284 @@ +/* vim: set textwidth=80 tabstop=4: */ +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "delete-caldav-object.h" +#include "lock-caldav-object.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * A static literal string containing the first part of the calendar query. + * The actual UID to use for the query is added at runtime. + */ +static char* search_head = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>" +" <D:getetag/>" +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VEVENT\">" +" <C:prop-filter name=\"UID\">"; + +/** + * A static literal string containing the last part of the calendar query + */ +static char* search_tail = +"</C:prop-filter>" +" </C:comp-filter>" +" </C:comp-filter>" +" </C:filter>" +"</C:calendar-query>"; + +/** + * Function for deleting an event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_delete(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gchar* search; + gchar* uid; + gboolean LOCKSUPPORT = FALSE; + gchar* lock_token = NULL; + gboolean result = FALSE; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: infinity"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + gchar* file = g_strdup(settings->file); + if ((uid = get_response_header("uid", file, FALSE)) == NULL) { + g_free(file); + error->code = 1; + error->str = g_strdup("Error: Missing required UID for object"); + return TRUE; + } + g_free(file); + /* + * ICalendar server does not support collation + * <C:text-match collation=\"i;ascii-casemap\">%s</C:text-match> + */ + search = g_strdup_printf( + "%s\r\n<C:text-match>%s</C:text-match>\r\n%s", + search_head, uid, search_tail); + g_free(uid); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, search); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(search)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + g_free(search); + curl_slist_free_all(http_header); + http_header = NULL; + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 207) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + else { + /* enable uploading */ + gchar* url = NULL; + gchar* etag = NULL; + url = get_url(chunk.memory); + if (url) { + etag = get_etag(chunk.memory); + if (etag) { + gchar* host = get_host(settings->url); + if (host) { + file = g_strdup(url); + g_free(url); + url = g_strdup_printf("%s%s", host, file); + g_free(file); + g_free(host); + } + else { + g_free(etag); + g_free(url); + url = NULL; + } + } + else { + g_free(url); + url = NULL; + } + } + if (url) { + int lock = 0; + caldav_error lock_error; + + file = g_strdup(etag); + g_free(etag); + etag = g_strdup_printf("If-Match: %s", file); + g_free(file); + http_header = curl_slist_append(http_header, etag); + g_free(etag); + http_header = curl_slist_append(http_header, + "Content-Type: text/calendar; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append( + http_header, "Transfer-Encoding:"); + if (settings->use_locking) + LOCKSUPPORT = caldav_lock_support(settings, &lock_error); + else + LOCKSUPPORT = FALSE; + if (LOCKSUPPORT) { + lock_token = caldav_lock_object(url, settings, &lock_error); + if (lock_token) { + http_header = curl_slist_append( + http_header, g_strdup_printf( + "If: (%s)", lock_token)); + } + /* + * If error code is 423 (Resource is LOCKED) bail out + */ + else if (lock_error.code == 423) { + lock = -1; + } + /* + * If error code is 501 (Not implemented) we continue + * hoping for the best. + */ + else if (lock_error.code == 501) { + lock_token = g_strdup(""); + } + else { + lock = -1; + } + } + if (! LOCKSUPPORT || (LOCKSUPPORT && lock_token && lock_error.code != 423)) { + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + curl_easy_setopt(curl, CURLOPT_URL, rebuild_url(settings, url)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, 0); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (LOCKSUPPORT && lock_token) { + caldav_unlock_object( + lock_token, url, settings, &lock_error); + } + } + g_free(url); + g_free(lock_token); + if (res != 0 || lock < 0) { + /* Is this a lock_error don't change error*/ + if (lock == 0 || lock_error.code == 423) { + error->code = code; + error->str = g_strdup(chunk.memory); + } + else { + error->code = lock_error.code; + error->str = g_strdup(lock_error.str); + } + result = TRUE; + g_free(settings->file); + settings->file = NULL; + } + else { + long code; + res = curl_easy_getinfo( + curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 204) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + } + curl_slist_free_all(http_header); + } + else { + error->code = code; + if (chunk.memory) + error->str = g_strdup(chunk.memory); + else + error->str = g_strdup("No object found"); + result = TRUE; + } + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_cleanup(curl); + return result; +} diff --git a/src/.svn/text-base/delete-caldav-object.h.svn-base b/src/.svn/text-base/delete-caldav-object.h.svn-base new file mode 100644 index 0000000..7576a29 --- /dev/null +++ b/src/.svn/text-base/delete-caldav-object.h.svn-base @@ -0,0 +1,35 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __DELETE_CALDAV_OBJECT_H__ +#define __DELETE_CALDAV_OBJECT_H__ + +#include "caldav-utils.h" +#include "caldav.h" + +/** + * Function for deleting an event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_delete(caldav_settings* settings, caldav_error* error); + +#endif + diff --git a/src/.svn/text-base/get-caldav-report.c.svn-base b/src/.svn/text-base/get-caldav-report.c.svn-base new file mode 100644 index 0000000..52b4e28 --- /dev/null +++ b/src/.svn/text-base/get-caldav-report.c.svn-base @@ -0,0 +1,254 @@ +/* vim: set textwidth=80 tabstop=4 smarttab: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "get-caldav-report.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * A static literal string containing the calendar query for fetching + * all events from collection. + */ +static const char* getall_request = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>" +" <D:getetag/>" +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VEVENT\"/>" +" </C:comp-filter>" +" </C:filter>" +"</C:calendar-query>\r\n"; + +/** + * A static literal string containing the first part of the calendar query. + * The actual VEVENT to search for is added at runtime. + */ +static const char* getrange_request_head = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +/*"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>"*/ +"<C:calendar-query xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop xmlns:D=\"DAV:\">" +/*" <D:getetag/>"*/ +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VEVENT\">"; + +/** + * A static literal string containing the last part of the calendar query + */ +static const char* getrange_request_foot = +" </C:comp-filter>" +" </C:comp-filter>" +" </C:filter>" +"</C:calendar-query>\r\n"; + +/** + * Function for getting all events from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getall(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 1"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + data.trace_ascii = settings->trace_ascii; + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, getall_request); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(getall_request)); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 207) { + error->code = code; + error->str = g_strdup(headers.memory); + result = TRUE; + } + else { + gchar* report; + report = parse_caldav_report( + chunk.memory, "calendar-data", "VEVENT"); + settings->file = g_strdup(report); + g_free(report); + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_slist_free_all(http_header); + curl_easy_cleanup(curl); + return result; +} + +/** + * Function for getting all events within a time range from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getrange(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE + 1]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + gchar* request = NULL; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 1"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + request = g_strdup_printf( + "%s\r\n<C:time-range start=\"%s\"\r\n end=\"%s\"/>\r\n%s", + getrange_request_head, get_caldav_datetime(&settings->start), + get_caldav_datetime(&settings->end), getrange_request_foot); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(request)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + gchar* report; + report = parse_caldav_report(chunk.memory, "calendar-data", "VEVENT"); + settings->file = g_strdup(report); + g_free(report); + } + g_free(request); + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_slist_free_all(http_header); + curl_easy_cleanup(curl); + return result; +} + diff --git a/src/.svn/text-base/get-caldav-report.h.svn-base b/src/.svn/text-base/get-caldav-report.h.svn-base new file mode 100644 index 0000000..7bce7f3 --- /dev/null +++ b/src/.svn/text-base/get-caldav-report.h.svn-base @@ -0,0 +1,43 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __GET_CALDAV_REPORT_H__ +#define __GET_CALDAV_REPORT_H__ + +#include "caldav-utils.h" +#include "caldav.h" +#include <glib.h> + +/** + * Function for getting all events from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getall(caldav_settings* settings, caldav_error* error); + +/** + * Function for getting all events within a time range from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getrange(caldav_settings* settings, caldav_error* error); + +#endif diff --git a/src/.svn/text-base/get-display-name.c.svn-base b/src/.svn/text-base/get-display-name.c.svn-base new file mode 100644 index 0000000..c776713 --- /dev/null +++ b/src/.svn/text-base/get-display-name.c.svn-base @@ -0,0 +1,138 @@ +/* vim: set textwidth=80 tabstop=4 smarttab: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "get-caldav-report.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * A static literal string containing the calendar query for fetching + * the stored display name for the collection. + */ +static const char* getname_request = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<D:propfind xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>" +" <D:displayname/>" +" </D:prop>" +"</D:propfind>\r\n"; + +/** + * Function for getting the display name from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getname(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 0"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + data.trace_ascii = settings->trace_ascii; + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, getname_request); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(getname_request)); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PROPFIND"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 207) { + error->code = code; + error->str = g_strdup(headers.memory); + result = TRUE; + } + else { + gchar* displayname; + displayname = get_tag("displayname", chunk.memory); + /* Maybe namespace prefixed */ + if (!displayname) { + displayname = get_tag("D:displayname", chunk.memory); + } + settings->file = (displayname) ? + g_strdup(displayname) : g_strdup(""); + g_free(displayname); + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_slist_free_all(http_header); + curl_easy_cleanup(curl); + return result; +} + diff --git a/src/.svn/text-base/get-display-name.h.svn-base b/src/.svn/text-base/get-display-name.h.svn-base new file mode 100644 index 0000000..5c80272 --- /dev/null +++ b/src/.svn/text-base/get-display-name.h.svn-base @@ -0,0 +1,34 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __GET_DISPLAY_NAME_H__ +#define __GET_DISPLAY_NAME_H__ + +#include "caldav-utils.h" + +/** + * Function for getting the display name from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getname(caldav_settings* settings, caldav_error* error); + +#endif + diff --git a/src/.svn/text-base/lock-caldav-object.c.svn-base b/src/.svn/text-base/lock-caldav-object.c.svn-base new file mode 100644 index 0000000..1ddf926 --- /dev/null +++ b/src/.svn/text-base/lock-caldav-object.c.svn-base @@ -0,0 +1,292 @@ +/* vim: set textwidth=80 tabstop=4: */ +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "lock-caldav-object.h" +#include "options-caldav-server.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * A static literal string containing the lock query. + */ +static char* lock_query = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<D:lockinfo xmlns:D=\"DAV:\">" +" <D:lockscope><D:exclusive/></D:lockscope>" +" <D:locktype><D:write/></D:locktype>" +"</D:lockinfo>"; + +/** + * Function which requests a lock on a calendar resource + * @param URI The resource to request lock on. + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return The Lock-Token or NULL in case of error + */ +gchar* caldav_lock_object( + gchar* URI, caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gchar* lock_token = NULL; + gchar* url; + + if (! caldav_lock_support(settings, error)) + return lock_token; + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return lock_token; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Timeout: Second-300"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + if (settings->usehttps) { + url = g_strdup_printf("https://%s", URI); + } else { + url = g_strdup_printf("http://%s", URI); + } + curl_easy_setopt(curl, CURLOPT_URL, url); + g_free(url); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, lock_query); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(lock_query)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "LOCK"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + curl_slist_free_all(http_header); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 200) { + gchar* status = get_tag("status", chunk.memory); + if (status && strstr(status, "423") != NULL) { + error->code = 423; + error->str = g_strdup(status); + } + else { + error->code = code; + error->str = g_strdup(chunk.memory); + } + g_free(status); + } + else { + lock_token = get_response_header( + "Lock-Token", headers.memory, FALSE); + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_cleanup(curl); + return lock_token; +} + +/** + * Function which requests to have a lock removed from a calendar resource + * @param lock_token A privious aquired Lock-Token + * @param URI The resource to request unlock on. + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return False in case the lock could not be removed. True otherwise. + */ +gboolean caldav_unlock_object(gchar* lock_token, gchar* URI, + caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + gchar* url; + + if (! caldav_lock_support(settings, error)) + return result; + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + g_strdup_printf("Lock-Token: %s", lock_token)); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + if (settings->usehttps) { + url = g_strdup_printf("https://%s", URI); + } else { + url = g_strdup_printf("http://%s", URI); + } + curl_easy_setopt(curl, CURLOPT_URL, url); + g_free(url); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "UNLOCK"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + curl_slist_free_all(http_header); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 204) { + error->code = code; + error->str = g_strdup(chunk.memory); + } + else { + result = TRUE; + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_cleanup(curl); + return result; +} + +/** + * Function to test whether the server supports locking or not. Searching + * for PROP LOCK. If LOCK is present then according to RFC4791 PROP UNLOCK + * must also be present. + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return True if locking is supported by the server. False otherwise + */ +gboolean caldav_lock_support(caldav_settings* settings, caldav_error* error) { + gboolean found = FALSE; + gchar* url = NULL; + gchar* mystr = NULL; + runtime_info* info; + + info = caldav_get_runtime_info(); + info->options->verify_ssl_certificate = settings->verify_ssl_certificate; + info->options->custom_cacert = g_strdup(settings->custom_cacert); + if (settings->usehttps) { + mystr = g_strdup("https://"); + } else { + mystr = g_strdup("http://"); + } + + + if (settings->username && settings->password) { + url = g_strdup_printf("%s%s:%s@%s", + mystr, settings->username, settings->password, settings->url); + } + else if (settings->username) { + url = g_strdup_printf("%s%s@%s", + mystr, settings->username, settings->url); + } + else { + url = g_strdup_printf("%s%s", mystr, settings->url); + } + gchar** options = caldav_get_server_options(url, info); + g_free(url); + gchar** tmp = options; + caldav_free_runtime_info(&info); + while (*options) { + if (strcmp(*options++, "LOCK") == 0) { + found = TRUE; + break; + } + } + g_strfreev(tmp); + g_free(mystr); + return found; +} + + diff --git a/src/.svn/text-base/lock-caldav-object.h.svn-base b/src/.svn/text-base/lock-caldav-object.h.svn-base new file mode 100644 index 0000000..6dd884f --- /dev/null +++ b/src/.svn/text-base/lock-caldav-object.h.svn-base @@ -0,0 +1,55 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __LOCK_CALDAV_OBJECT_H__ +#define __LOCK_CALDAV_OBJECT_H__ + +#include "caldav-utils.h" +#include "caldav.h" + +/** + * Function which requests a lock on a calendar resource + * @param URI The resource to request lock on. + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return The Lock-Token or NULL in case of error + */ +gchar* caldav_lock_object( + gchar* URI, caldav_settings* settings, caldav_error* error); + +/** + * Function which requests to have a lock removed from a calendar resource + * @param lock_token A privious aquired Lock-Token + * @param URI The resource to request unlock on. + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return False in case the lock could not be removed. True otherwise. + */ +gboolean caldav_unlock_object(gchar* lock_token, gchar* URI, + caldav_settings* settings, caldav_error* error); + +/** + * Function to test whether the server supports locking or not + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return True if locking is supported by the server. False otherwise + */ +gboolean caldav_lock_support(caldav_settings* settings, caldav_error* error); + +#endif diff --git a/src/.svn/text-base/md5.c.svn-base b/src/.svn/text-base/md5.c.svn-base new file mode 100644 index 0000000..62d4b2d --- /dev/null +++ b/src/.svn/text-base/md5.c.svn-base @@ -0,0 +1,442 @@ +/** md5.c - MD5 Message-Digest Algorithm + * Copyright (C) 1995, 1996, 1998, 1999 Free Software Foundation, Inc. + * + * according to the definition of MD5 in RFC 1321 from April 1992. + * NOTE: This is *not* the same file as the one from glibc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 3, or (at your option) any + * later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. + * heavily modified for GnuPG by <werner.koch@guug.de>. + * modified again for Sylpheed by <wk@gnupg.org> 2001-02-11. + */ + + +/* Test values: + * "" D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E + * "a" 0C C1 75 B9 C0 F1 B6 A8 31 C3 99 E2 69 77 26 61 + * "abc 90 01 50 98 3C D2 4F B0 D6 96 3F 7D 28 E1 7F 72 + * "message digest" F9 6B 69 7D 7C B7 93 8D 52 5A 2F 31 AA F1 61 D0 + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "md5.h" + +/**************** + * Rotate a 32 bit integer by n bytes + */ +#if defined(__GNUC__) && defined(__i386__) +static inline u32 +rol( u32 x, int n) +{ + __asm__("roll %%cl,%0" + :"=r" (x) + :"0" (x),"c" (n)); + return x; +} +#else +#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) ) +#endif + + +static void +md5_init(MD5_CONTEXT *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->nblocks = 0; + ctx->count = 0; + ctx->finalized = 0; +} + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + + +/**************** + * transform n*64 bytes + */ +static void +transform(MD5_CONTEXT *ctx, const unsigned char *data) +{ + u32 correct_words[16]; + u32 A = ctx->A; + u32 B = ctx->B; + u32 C = ctx->C; + u32 D = ctx->D; + u32 *cwp = correct_words; + +#ifdef BIG_ENDIAN_HOST + { + int i; + unsigned char *p2; + const unsigned char *p1; + + for (i = 0, p1 = data, p2 = (unsigned char*)correct_words; + i < 16; i++, p2 += 4) { + p2[3] = *p1++; + p2[2] = *p1++; + p2[1] = *p1++; + p2[0] = *p1++; + } + } +#else + memcpy(correct_words, data, 64); +#endif + + +#define OP(a, b, c, d, s, T) \ + do { \ + a += FF (b, c, d) + (*cwp++) + T; \ + a = rol(a, s); \ + a += b; \ + } while (0) + + /* Before we start, one word about the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do { \ + a += f (b, c, d) + correct_words[k] + T; \ + a = rol(a, s); \ + a += b; \ + } while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Put checksum in context given as argument. */ + ctx->A += A; + ctx->B += B; + ctx->C += C; + ctx->D += D; +} + + + +/* The routine updates the message-digest context to + * account for the presence of each of the characters inBuf[0..inLen-1] + * in the message whose digest is being computed. + */ +static void +md5_update(MD5_CONTEXT *hd, const unsigned char *inbuf, size_t inlen) +{ + if (hd->count == 64) { /* flush the buffer */ + transform( hd, hd->buf ); + hd->count = 0; + hd->nblocks++; + } + if (!inbuf) + return; + if (hd->count) { + for (; inlen && hd->count < 64; inlen--) + hd->buf[hd->count++] = *inbuf++; + md5_update(hd, NULL, 0); + if (!inlen) + return; + } + + while (inlen >= 64) { + transform(hd, inbuf); + hd->count = 0; + hd->nblocks++; + inlen -= 64; + inbuf += 64; + } + + for (; inlen && hd->count < 64; inlen--) + hd->buf[hd->count++] = *inbuf++; +} + + + +/* The routine final terminates the message-digest computation and + * ends with the desired message digest in mdContext->digest[0...15]. + * The handle is prepared for a new MD5 cycle. + * Returns 16 bytes representing the digest. + */ + +static void +do_final(MD5_CONTEXT *hd) +{ + u32 t, msb, lsb; + unsigned char *p; + + md5_update(hd, NULL, 0); /* flush */ + + msb = 0; + t = hd->nblocks; + if ((lsb = t << 6) < t) /* multiply by 64 to make a byte count */ + msb++; + msb += t >> 26; + t = lsb; + if ((lsb = t + hd->count) < t) /* add the count */ + msb++; + t = lsb; + if ((lsb = t << 3) < t) /* multiply by 8 to make a bit count */ + msb++; + msb += t >> 29; + + if (hd->count < 56) { /* enough room */ + hd->buf[hd->count++] = 0x80; /* pad */ + while(hd->count < 56) + hd->buf[hd->count++] = 0; /* pad */ + } else { /* need one extra block */ + hd->buf[hd->count++] = 0x80; /* pad character */ + while (hd->count < 64) + hd->buf[hd->count++] = 0; + md5_update(hd, NULL, 0); /* flush */ + memset(hd->buf, 0, 56); /* fill next block with zeroes */ + } + + /* append the 64 bit count */ + hd->buf[56] = lsb ; + hd->buf[57] = lsb >> 8; + hd->buf[58] = lsb >> 16; + hd->buf[59] = lsb >> 24; + hd->buf[60] = msb ; + hd->buf[61] = msb >> 8; + hd->buf[62] = msb >> 16; + hd->buf[63] = msb >> 24; + transform(hd, hd->buf); + + p = hd->buf; +#ifdef BIG_ENDIAN_HOST +#define X(a) do { *p++ = hd->a ; *p++ = hd->a >> 8; \ + *p++ = hd->a >> 16; *p++ = hd->a >> 24; } while(0) +#else /* little endian */ + /*#define X(a) do { *(u32*)p = hd->##a ; p += 4; } while(0)*/ + /* Unixware's cpp doesn't like the above construct so we do it his way: + * (reported by Allan Clark) */ +#define X(a) do { *(u32*)p = (*hd).a ; p += 4; } while(0) +#endif + X(A); + X(B); + X(C); + X(D); +#undef X + hd->finalized = 1; +} + +static void +md5_final(unsigned char *digest, MD5_CONTEXT *ctx) +{ + if (!ctx->finalized) + do_final(ctx); + memcpy(digest, ctx->buf, 16); +} + +/* + * Creates a MD5 digest in hex fomrat (lowercase letters) from the + * string S. hextdigest but be buffer of at lease 33 bytes! + */ +static void +md5_hex_digest(char *hexdigest, const unsigned char *s) +{ + int i; + MD5_CONTEXT context; + unsigned char digest[16]; + + md5_init(&context); + md5_update(&context, s, strlen((gchar *) s)); + md5_final(digest, &context); + + for (i = 0; i < 16; i++) + sprintf(hexdigest + 2 * i, "%02x", digest[i]); +} + + +/* +** Function: md5_hmac +** taken from the file rfc2104.txt +** written by Martin Schaaf <mascha@ma-scha.de> +*/ +static void +md5_hmac(unsigned char *digest, + const unsigned char* text, int text_len, + const unsigned char* key, int key_len) +{ + MD5_CONTEXT context; + unsigned char k_ipad[64]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[64]; /* outer padding - + * key XORd with opad + */ + /* unsigned char tk[16]; */ + int i; + + /* start out by storing key in pads */ + memset(k_ipad, 0, sizeof k_ipad); + memset(k_opad, 0, sizeof k_opad); + if (key_len > 64) { + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + MD5_CONTEXT tctx; + + md5_init(&tctx); + md5_update(&tctx, key, key_len); + md5_final(k_ipad, &tctx); + md5_final(k_opad, &tctx); + } else { + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + + /* XOR key with ipad and opad values */ + for (i = 0; i < 64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* + * perform inner MD5 + */ + md5_init(&context); /* init context for 1st + * pass */ + md5_update(&context, k_ipad, 64); /* start with inner pad */ + md5_update(&context, text, text_len); /* then text of datagram */ + md5_final(digest, &context); /* finish up 1st pass */ + /* + * perform outer MD5 + */ + md5_init(&context); /* init context for 2nd + * pass */ + md5_update(&context, k_opad, 64); /* start with outer pad */ + md5_update(&context, digest, 16); /* then results of 1st + * hash */ + md5_final(digest, &context); /* finish up 2nd pass */ +} + + +static void +md5_hex_hmac(char *hexdigest, + const unsigned char* text, int text_len, + const unsigned char* key, int key_len) +{ + unsigned char digest[16]; + int i; + + md5_hmac(digest, text, text_len, key, key_len); + for (i = 0; i < 16; i++) + sprintf(hexdigest + 2 * i, "%02x", digest[i]); +} + +void caldav_md5_hex_digest(char *hexdigest, const unsigned char *s) { + md5_hex_digest(hexdigest, s); +} + +void caldav_md5_hex_hmac(char *hexdigest, + const unsigned char* text, int text_len, + const unsigned char* key, int key_len) { + md5_hex_hmac(hexdigest, text, text_len, key, key_len); +} diff --git a/src/.svn/text-base/md5.h.svn-base b/src/.svn/text-base/md5.h.svn-base new file mode 100644 index 0000000..7605214 --- /dev/null +++ b/src/.svn/text-base/md5.h.svn-base @@ -0,0 +1,52 @@ +/** + * md5.h - MD5 Message-Digest Algorithm + * Copyright (C) 1995, 1996, 1998, 1999 Free Software Foundation, Inc. + * + * according to the definition of MD5 in RFC 1321 from April 1992. + * NOTE: This is *not* the same file as the one from glibc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 3, or (at your option) any + * later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _MD5_HDR_ +#define _MD5_HDR_ + +#include <glib.h> + +/** + * @typedef u32 + * Kept this typedef for compatibility reasons + */ +#ifndef HAVE_U32_TYPEDEF + #undef u32 + typedef guint32 u32; + #define HAVE_U32_TYPEDEF +#endif + +typedef struct { /* Hmm, should be private */ + u32 A,B,C,D; + u32 nblocks; + unsigned char buf[64]; + int count; + int finalized; +} MD5_CONTEXT; + +void caldav_md5_hex_digest(char *hexdigest, const unsigned char *s); + +void caldav_md5_hex_hmac(char *hexdigest, + const unsigned char* text, int text_len, + const unsigned char* key, int key_len); + +#endif /* _MD5_HDR_ */ + diff --git a/src/.svn/text-base/modify-caldav-object.c.svn-base b/src/.svn/text-base/modify-caldav-object.c.svn-base new file mode 100644 index 0000000..f42f3ae --- /dev/null +++ b/src/.svn/text-base/modify-caldav-object.c.svn-base @@ -0,0 +1,296 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "modify-caldav-object.h" +#include "lock-caldav-object.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * A static literal string containing the first part of the calendar query. + * The actual UID to use for the query is added at runtime. + */ +static char* search_head = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>" +" <D:getetag/>" +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VEVENT\">" +" <C:prop-filter name=\"UID\">"; + +/** + * A static literal string containing the last part of the calendar query + */ +static char* search_tail = +"</C:prop-filter>" +" </C:comp-filter>" +" </C:comp-filter>" +" </C:filter>" +"</C:calendar-query>"; + +/** + * Function for modifying an event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_modify(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gchar* search; + gchar* uid; + gboolean result = FALSE; + gboolean LOCKSUPPORT = FALSE; + gchar* lock_token = NULL; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 1"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + gchar* file = g_strdup(settings->file); + if ((uid = get_response_header("uid", file, FALSE)) == NULL) { + g_free(file); + error->code = 1; + error->str = g_strdup("Error: Missing required UID for object"); + return TRUE; + } + g_free(file); + /* + * collation is not supported by ICalendar. + * <C:text-match collation=\"i;ascii-casemap\">%s</C:text-match> + */ + search = g_strdup_printf( + "%s\r\n<C:text-match>%s</C:text-match>\r\n%s", + search_head, uid, search_tail); + g_free(uid); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, search); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(search)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + curl_slist_free_all(http_header); + http_header = NULL; + g_free(search); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 207) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + else { + /* enable uploading */ + gchar* url = NULL; + gchar* etag = NULL; + url = get_url(chunk.memory); + if (url) { + etag = get_etag(chunk.memory); + if (etag) { + gchar* host = get_host(settings->url); + if (host) { + file = g_strdup(url); + g_free(url); + url = g_strdup_printf("%s%s", host, file); + g_free(file); + g_free(host); + } + else { + g_free(etag); + g_free(url); + url = NULL; + } + } + else { + g_free(url); + url = NULL; + } + if (url) { + int lock = 0; + caldav_error lock_error; + + file = g_strdup(etag); + g_free(etag); + etag = g_strdup_printf("If-Match: %s", file); + g_free(file); + http_header = curl_slist_append(http_header, etag); + g_free(etag); + http_header = curl_slist_append(http_header, + "Content-Type: text/calendar; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append( + http_header, "Transfer-Encoding:"); + if (settings->use_locking) + LOCKSUPPORT = caldav_lock_support(settings, &lock_error); + else + LOCKSUPPORT = FALSE; + if (LOCKSUPPORT) { + lock_token = caldav_lock_object(url, settings, &lock_error); + if (lock_token) { + http_header = curl_slist_append( + http_header, g_strdup_printf( + "If: (%s)", lock_token)); + } + /* + * If error code is 423 (Resource is LOCKED) bail out + */ + else if (lock_error.code == 423) { + lock = -1; + } + /* + * If error code is 501 (Not implemented) we continue + * hoping for the best. + */ + else if (lock_error.code == 501) { + lock_token = g_strdup(""); + } + else { + lock = -1; + } + } + if (! LOCKSUPPORT || (LOCKSUPPORT && lock_token && lock_error.code != 423)) { + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + curl_easy_setopt(curl, CURLOPT_URL, rebuild_url(settings, url)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, settings->file); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, + strlen(settings->file)); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + res = curl_easy_perform(curl); + if (LOCKSUPPORT && lock_token) { + caldav_unlock_object( + lock_token, url, settings, &lock_error); + } + } + g_free(url); + g_free(lock_token); + if (res != 0 || lock < 0) { + /* Is this a lock_error don't change error*/ + if (lock == 0 || lock_error.code == 423) { + error->code = code; + error->str = g_strdup(chunk.memory); + } + else { + error->code = lock_error.code; + error->str = g_strdup(lock_error.str); + } + result = TRUE; + g_free(settings->file); + settings->file = NULL; + } + else { + long code; + res = curl_easy_getinfo( + curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 204) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + } + curl_slist_free_all(http_header); + } + else { + error->code = code; + if (chunk.memory) + error->str = g_strdup(chunk.memory); + else + error->str = g_strdup("No object found"); + result = TRUE; + } + } + else { + /* + * No object found on server. Posible synchronization + * problem or a server side race condition + */ + error->code = 409; + error->str = g_strdup("No object found"); + result = TRUE; + } + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_cleanup(curl); + return result; +} + diff --git a/src/.svn/text-base/modify-caldav-object.h.svn-base b/src/.svn/text-base/modify-caldav-object.h.svn-base new file mode 100644 index 0000000..39ac718 --- /dev/null +++ b/src/.svn/text-base/modify-caldav-object.h.svn-base @@ -0,0 +1,34 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MODIFY_CALDAV_OBJECT_H__ +#define __MODIFY_CALDAV_OBJECT_H__ + +#include "caldav-utils.h" +#include "caldav.h" + +/** + * Function for modifying an event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_modify(caldav_settings* settings, caldav_error* error); + +#endif diff --git a/src/.svn/text-base/options-caldav-server.c.svn-base b/src/.svn/text-base/options-caldav-server.c.svn-base new file mode 100644 index 0000000..1d97d04 --- /dev/null +++ b/src/.svn/text-base/options-caldav-server.c.svn-base @@ -0,0 +1,135 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "options-caldav-server.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * Function for getting supported options from a server. + * @param curl A pointer to an initialized CURL instance + * @param settings struct containing the URL to the server. If authentication + * is required prior to making the call the credentials must be available + * via CURLOPT_USERPWD before calling. + * @param result A pointer to a struct _response. If test is true + * this variable can be NULL. Caller is responsible for freeing associated + * memory. + * @param error A pointer to caldav_error. @see caldav_error + * @param test if this is true response will be whether the server + * represented by the URL is a CalDAV collection or not. + * @return FALSE in case of error, TRUE otherwise. + */ +gboolean caldav_getoptions(CURL* curl, caldav_settings* settings, response* result, + caldav_error* error, gboolean test) { + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct MemoryStruct chunk; + struct MemoryStruct headers; + gboolean enabled = FALSE; + + if (! curl) + return FALSE; + + if (!error) { + error = (caldav_error *) malloc(sizeof(struct _caldav_error)); + memset(error, '\0', sizeof(struct _caldav_error)); + } + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "OPTIONS"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res == 0) { + gchar* head; + head = get_response_header("DAV", headers.memory, TRUE); + if (head && strstr(head, "calendar-access") != NULL) { + enabled = TRUE; + if (! test) { + result->msg = g_strdup( + get_response_header("Allow", headers.memory, FALSE)); + } + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code == 200) { + error->code = -1; + error->str = g_strdup("URL is not a CalDAV resource"); + } + else { + error->code = -1 * code; + error->str = g_strdup(headers.memory); + } + } + g_free(head); + } + else if ( + (res == CURLE_SSL_CONNECT_ERROR || + CURLE_PEER_FAILED_VERIFICATION || + CURLE_SSL_ENGINE_NOTFOUND || + CURLE_SSL_ENGINE_SETFAILED || + CURLE_SSL_CERTPROBLEM || + CURLE_SSL_CIPHER || + CURLE_SSL_CACERT || + CURLE_SSL_CACERT_BADFILE || + CURLE_SSL_CRL_BADFILE || + CURLE_SSL_ISSUER_ERROR) && settings->usehttps) { + error->code = -2; + error->str = g_strdup(error_buf); + } + else if (res == CURLE_COULDNT_RESOLVE_HOST) { + error->code = -3; + error->str = g_strdup("Could not resolve host"); + } + else if (res == CURLE_COULDNT_CONNECT) { + error->code = -4; + error->str = g_strdup("Unable to connect"); + } + else { + error->code = -1; + error->str = g_strdup("URL is not a CalDAV resource"); + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + return enabled; +} diff --git a/src/.svn/text-base/options-caldav-server.h.svn-base b/src/.svn/text-base/options-caldav-server.h.svn-base new file mode 100644 index 0000000..d17cba2 --- /dev/null +++ b/src/.svn/text-base/options-caldav-server.h.svn-base @@ -0,0 +1,43 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __OPTIONS_CALDAV_SERVER_H__ +#define __OPTIONS_CALDAV_SERVER_H__ + +#include "caldav-utils.h" +#include "caldav.h" + +/** + * Function for getting supported options from a server. + * @param curl A pointer to an initialized CURL instance + * @param settings struct containing the URL to the server. If authentication + * is required prior to making the call the credentials must be available + * via CURLOPT_USERPWD before calling. + * @param result A pointer to a struct _response. If test is true + * this variable can be NULL. Caller is responsible for freeing associated + * memory. + * @param error A pointer to caldav_error. @see caldav_error + * @param test if this is true response will be whether the server + * represented by the URL is a CalDAV collection or not. + * @return FALSE in case of error, TRUE otherwise. + */ +gboolean caldav_getoptions(CURL* curl, caldav_settings* settings, response* result, + caldav_error* error, gboolean test); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..59d3126 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,53 @@ +AUTOMAKE_OPTIONS = gnu + +INCLUDES = @CURL_CFLAGS@ @GLIB_CFLAGS@ \ + -I$(top_srcdir) -I$(top_builddir) + +if STATIC_LINK +noinst_LTLIBRARIES = libcaldav.la +endif + +if DYNAMIC_LINK +lib_LTLIBRARIES = libcaldav.la +endif + +libcaldav_la_LDFLAGS = -version-info @LIBVERSION@ + +libcaldav_la_SOURCES = \ + caldav.h \ + caldav.c \ + add-caldav-object.c \ + add-caldav-object.h \ + delete-caldav-object.c \ + delete-caldav-object.h \ + modify-caldav-object.c \ + modify-caldav-object.h \ + get-caldav-report.c \ + get-caldav-report.h \ + get-display-name.c \ + get-display-name.h \ + caldav-utils.c \ + caldav-utils.h \ + md5.c \ + md5.h \ + options-caldav-server.c \ + options-caldav-server.h \ + lock-caldav-object.c \ + lock-caldav-object.h \ + get-freebusy-report.c \ + get-freebusy-report.h + +libcaldav_includedir=$(includedir)/libcaldav-@VERSION@ +libcaldav_include_HEADERS = caldav.h + +noinst_HEADERS = \ + add-caldav-object.h \ + delete-caldav-object.h \ + modify-caldav-object.h \ + get-caldav-report.h \ + caldav-utils.h + +libcaldav_la_LIBADD = \ + @CURL_LIBS@ \ + @GLIB_LIBS@ + diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..5803fc2 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,649 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = src +DIST_COMMON = $(libcaldav_include_HEADERS) $(noinst_HEADERS) \ + $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_prog_doxygen.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(libcaldav_includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +libcaldav_la_DEPENDENCIES = +am_libcaldav_la_OBJECTS = caldav.lo add-caldav-object.lo \ + delete-caldav-object.lo modify-caldav-object.lo \ + get-caldav-report.lo get-display-name.lo caldav-utils.lo \ + md5.lo options-caldav-server.lo lock-caldav-object.lo \ + get-freebusy-report.lo +libcaldav_la_OBJECTS = $(am_libcaldav_la_OBJECTS) +libcaldav_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libcaldav_la_LDFLAGS) $(LDFLAGS) -o $@ +@DYNAMIC_LINK_TRUE@am_libcaldav_la_rpath = -rpath $(libdir) +@STATIC_LINK_TRUE@am_libcaldav_la_rpath = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libcaldav_la_SOURCES) +DIST_SOURCES = $(libcaldav_la_SOURCES) +HEADERS = $(libcaldav_include_HEADERS) $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CURL_CFLAGS = @CURL_CFLAGS@ +CURL_LIBS = @CURL_LIBS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DX_CONFIG = @DX_CONFIG@ +DX_DOCDIR = @DX_DOCDIR@ +DX_DOT = @DX_DOT@ +DX_DOXYGEN = @DX_DOXYGEN@ +DX_DVIPS = @DX_DVIPS@ +DX_EGREP = @DX_EGREP@ +DX_ENV = @DX_ENV@ +DX_FLAG_chi = @DX_FLAG_chi@ +DX_FLAG_chm = @DX_FLAG_chm@ +DX_FLAG_doc = @DX_FLAG_doc@ +DX_FLAG_dot = @DX_FLAG_dot@ +DX_FLAG_html = @DX_FLAG_html@ +DX_FLAG_man = @DX_FLAG_man@ +DX_FLAG_pdf = @DX_FLAG_pdf@ +DX_FLAG_ps = @DX_FLAG_ps@ +DX_FLAG_rtf = @DX_FLAG_rtf@ +DX_FLAG_xml = @DX_FLAG_xml@ +DX_HHC = @DX_HHC@ +DX_LATEX = @DX_LATEX@ +DX_MAKEINDEX = @DX_MAKEINDEX@ +DX_PDFLATEX = @DX_PDFLATEX@ +DX_PERL = @DX_PERL@ +DX_PROJECT = @DX_PROJECT@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBVERSION = @LIBVERSION@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = gnu +INCLUDES = @CURL_CFLAGS@ @GLIB_CFLAGS@ \ + -I$(top_srcdir) -I$(top_builddir) + +@STATIC_LINK_TRUE@noinst_LTLIBRARIES = libcaldav.la +@DYNAMIC_LINK_TRUE@lib_LTLIBRARIES = libcaldav.la +libcaldav_la_LDFLAGS = -version-info @LIBVERSION@ +libcaldav_la_SOURCES = \ + caldav.h \ + caldav.c \ + add-caldav-object.c \ + add-caldav-object.h \ + delete-caldav-object.c \ + delete-caldav-object.h \ + modify-caldav-object.c \ + modify-caldav-object.h \ + get-caldav-report.c \ + get-caldav-report.h \ + get-display-name.c \ + get-display-name.h \ + caldav-utils.c \ + caldav-utils.h \ + md5.c \ + md5.h \ + options-caldav-server.c \ + options-caldav-server.h \ + lock-caldav-object.c \ + lock-caldav-object.h \ + get-freebusy-report.c \ + get-freebusy-report.h + +libcaldav_includedir = $(includedir)/libcaldav-@VERSION@ +libcaldav_include_HEADERS = caldav.h +noinst_HEADERS = \ + add-caldav-object.h \ + delete-caldav-object.h \ + modify-caldav-object.h \ + get-caldav-report.h \ + caldav-utils.h + +libcaldav_la_LIBADD = \ + @CURL_LIBS@ \ + @GLIB_LIBS@ + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libcaldav.la: $(libcaldav_la_OBJECTS) $(libcaldav_la_DEPENDENCIES) + $(libcaldav_la_LINK) $(am_libcaldav_la_rpath) $(libcaldav_la_OBJECTS) $(libcaldav_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/add-caldav-object.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/caldav-utils.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/caldav.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete-caldav-object.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get-caldav-report.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get-display-name.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get-freebusy-report.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock-caldav-object.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/modify-caldav-object.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options-caldav-server.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-libcaldav_includeHEADERS: $(libcaldav_include_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(libcaldav_includedir)" || $(MKDIR_P) "$(DESTDIR)$(libcaldav_includedir)" + @list='$(libcaldav_include_HEADERS)'; test -n "$(libcaldav_includedir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libcaldav_includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libcaldav_includedir)" || exit $$?; \ + done + +uninstall-libcaldav_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libcaldav_include_HEADERS)'; test -n "$(libcaldav_includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(libcaldav_includedir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(libcaldav_includedir)" && rm -f $$files + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libcaldav_includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-libcaldav_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES \ + uninstall-libcaldav_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \ + ctags distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES \ + install-libcaldav_includeHEADERS install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-libLTLIBRARIES \ + uninstall-libcaldav_includeHEADERS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/add-caldav-object.c b/src/add-caldav-object.c new file mode 100644 index 0000000..68a4377 --- /dev/null +++ b/src/add-caldav-object.c @@ -0,0 +1,132 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "add-caldav-object.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * Function for adding a new event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_add(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + gchar* url; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: text/calendar; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "If-None-Match: *"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + data.trace_ascii = settings->trace_ascii; + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + gchar* tmp = random_file_name(settings->file); + gchar* s = rebuild_url(settings, NULL); + if (g_str_has_suffix(s, "/")) { + url = g_strdup_printf("%slibcaldav-%s.ics", s, tmp); + } + else { + url = g_strdup_printf("%s/libcaldav-%s.ics", s, tmp); + } + g_free(s); + g_free(tmp); + curl_easy_setopt(curl, CURLOPT_URL, url); + tmp = g_strdup(settings->file); + g_free(settings->file); + settings->file = verify_uid(tmp); + g_free(tmp); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, settings->file); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(settings->file)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 201) { + error->str = g_strdup(chunk.memory); + error->code = code; + result = TRUE; + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_slist_free_all(http_header); + curl_easy_cleanup(curl); + return result; +} + diff --git a/src/add-caldav-object.h b/src/add-caldav-object.h new file mode 100644 index 0000000..0756e74 --- /dev/null +++ b/src/add-caldav-object.h @@ -0,0 +1,35 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __ADD_CALDAV_OBJECT_H__ +#define __ADD_CALDAV_OBJECT_H__ + +#include "caldav-utils.h" +#include "caldav.h" + +/** + * Function for adding a new event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_add(caldav_settings* settings, caldav_error* error); + +#endif + diff --git a/src/caldav-utils.c b/src/caldav-utils.c new file mode 100644 index 0000000..aa3f02e --- /dev/null +++ b/src/caldav-utils.c @@ -0,0 +1,729 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "caldav-utils.h" +#include "md5.h" +#include <glib.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <curl/curl.h> +#include <ctype.h> + +/** + * This function is burrowed from the libcurl documentation + * @param text + * @param stream + * @param ptr + * @param size + * @param nohex + */ +void dump(const char* text, FILE* stream, char* ptr, size_t size, char nohex) { + size_t i; + size_t c; + + unsigned int width=0x10; + + if(nohex) + /* without the hex output, we can fit more on screen */ + width = 0x40; + fprintf(stream, "%s, %zd bytes (0x%zx)\n", text, size, size); + for(i=0; i<size; i+= width) { + fprintf(stream, "%04zx: ", i); + if(!nohex) { + /* hex not disabled, show it */ + for(c = 0; c < width; c++) { + if(i+c < size) + fprintf(stream, "%02x ", ptr[i+c]); + else + fputs(" ", stream); + } + } + for(c = 0; (c < width) && (i+c < size); c++) { + /* check for 0D0A; if found, skip past and start a new line of output */ + if (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) { + i+=(c+2-width); + break; + } + fprintf(stream, "%c",(ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.'); + /* check again for 0D0A, to avoid an extra \n if it's at width */ + if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) { + i+=(c+3-width); + break; + } + } + fputc('\n', stream); /* newline */ + } + fflush(stream); +} + +/** + * This function is burrowed from the libcurl documentation + * @param handle + * @param type + * @param data + * @param size + * @param userp + * @return + */ +int my_trace(CURL* handle, curl_infotype type, char* data, size_t size, void* userp) { + struct config_data* config = (struct config_data *)userp; + const char* text; + (void)handle; /* prevent compiler warning */ + + switch (type) { + case CURLINFO_TEXT: + fprintf(stderr, "== Info: %s", data); + default: /* in case a new one is introduced to shock us */ + return 0; + case CURLINFO_HEADER_OUT: + text = "=> Send header"; + break; + case CURLINFO_DATA_OUT: + text = "=> Send data"; + break; + case CURLINFO_SSL_DATA_OUT: + text = "=> Send SSL data"; + break; + case CURLINFO_HEADER_IN: + text = "<= Recv header"; + break; + case CURLINFO_DATA_IN: + text = "<= Recv data"; + break; + case CURLINFO_SSL_DATA_IN: + text = "<= Recv SSL data"; + break; + } + dump(text, stderr, data, size, config->trace_ascii); + return 0; +} + +/** + * This function is burrowed from the libcurl documentation + * @param ptr + * @param size + * @return void* to memory region + */ +static void* myrealloc(void* ptr, size_t size) { +/* There might be a realloc() out there that doesn't like reallocing + * NULL pointers, so we take care of it here + * */ + if(ptr) + return realloc(ptr, size); + else + return malloc(size); +} + +/** + * This function is burrowed from the libcurl documentation + * @param ptr + * @param size + * @param nmemb + * @param data + * @return number of written bytes + */ +size_t WriteMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data) { + size_t realsize = size * nmemb; + struct MemoryStruct* mem = (struct MemoryStruct *)data; + mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1); + + if (mem->memory) { + memcpy(&(mem->memory[mem->size]), ptr, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + return realsize; +} + +/** + * This function is burrowed from the libcurl documentation + * @param ptr + * @param size + * @param nmemb + * @param data + * @return number of written bytes + */ +size_t WriteHeaderCallback(void* ptr, size_t size, size_t nmemb, void* data) { + size_t realsize = size * nmemb; + struct MemoryStruct* mem = (struct MemoryStruct *)data; + mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1); + + if (mem->memory) { + memcpy(&(mem->memory[mem->size]), ptr, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + return realsize; +} + +/* +size_t ReadMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data){ + struct MemoryStruct* mem = (struct MemoryStruct *)data; + + memcpy(ptr, mem->memory, mem->size); + return mem->size; +} +*/ + +/** + * Initialize caldav settings structure. + * @param settings @see caldav_settings + */ +void init_caldav_settings(caldav_settings* settings) { + settings->username = NULL; + settings->password = NULL; + settings->url = NULL; + settings->file = NULL; + settings->usehttps = FALSE; + settings->custom_cacert = NULL; + settings->verify_ssl_certificate = TRUE; + settings->debug = FALSE; + settings->trace_ascii = TRUE; + settings->ACTION = UNKNOWN; + settings->start = 0; + settings->end = 0; +} + +/** + * Free memory assigned to caldav settings structure. + * @param settings @see caldav_settings + */ +void free_caldav_settings(caldav_settings* settings) { + if (settings->username) { + g_free(settings->username); + settings->username = NULL; + } + if (settings->password) { + g_free(settings->password); + settings->password = NULL; + } + if (settings->url) { + g_free(settings->url); + settings->url = NULL; + } + if (settings->file) { + g_free(settings->file); + settings->file = NULL; + } + if (settings->custom_cacert) { + g_free(settings->custom_cacert); + settings->custom_cacert = NULL; + } + settings->verify_ssl_certificate = TRUE; + settings->usehttps = FALSE; + settings->debug = FALSE; + settings->trace_ascii = TRUE; + settings->ACTION = UNKNOWN; + settings->start = 0; + settings->end = 0; +} + +static gchar* place_after_hostname(const gchar* start, const gchar* stop) { + gchar* newpos = NULL; + gchar* pos = (gchar *) stop; + gboolean digit = TRUE; + + if (pos && stop && strcmp(start, pos) != 0) { + while (*pos != ':' && strcmp(start, pos) != 0) + --pos; + if (pos > start) { + gchar* tmp = (gchar *) pos + 1; + /* is pos++ a port number */ + while (*tmp != '/' && digit) { + if (isdigit(*tmp) != 0) { + digit = TRUE; + tmp++; + } + else + digit = FALSE; + } + if (digit) { + /* pos was a port number */ + while (*pos != '@' && strcmp(start, pos) != 0) + --pos; + if (strcmp(start, pos) != 0) + newpos = pos; + } + else { + while (*pos != '@' && pos != stop) + pos++; + if (pos != stop) + newpos = pos; + } + } + else { + /* is a username present */ + gchar* tmp = NULL; + while (*pos != '/' && pos != stop) { + if (*pos == '@') + tmp = pos; + pos++; + } + if (tmp && pos != stop) + newpos = tmp; + } + } + return newpos; +} + +/** + * Parse URL + * @param settings @see caldav_settings + * @param url String containing URL to collection + */ +void parse_url(caldav_settings* settings, const char* url) { + char* start; + char* pos; + char* end; + char* login; + + login = pos = end = start = NULL; + if (!url) + return; + if ((pos = strstr(url, "//")) != NULL) { + /* Does the URL use https ?*/ + if (!g_ascii_strncasecmp(url,"https",5) && settings->usehttps == FALSE) { + settings->usehttps=TRUE; + } + start = g_strdup(&(*(pos + 2))); + if ((pos = place_after_hostname(start, strrchr(start, '\0') - 1)) != NULL) { + /* username and/or password present */ + login = g_strndup(start, pos - start); + end = pos; + if ((pos = strrchr(login, ':')) != NULL) { + /* both username and password is present */ + settings->username = g_strndup(login, pos - login); + settings->password = g_strdup(++pos); + } + else { + /* only username present */ + settings->username = g_strdup(login); + settings->password = NULL; + } + g_free(login); + settings->url = g_strdup(++end); + } + else { + /* no username or password present */ + settings->url = g_strdup(start); + settings->username = NULL; + settings->password = NULL; + } + g_free(start); + } +} + +/** + * Find a specific HTTP header from last request + * @param header HTTP header to search for + * @param headers String of HTTP headers from last request + * @param lowcase Should string be returned in all lower case. + * @return The header found or NULL + */ +#define MAX_TOKENS 2 +gchar* get_response_header( + const char* header, gchar* headers, gboolean lowcase) { + gchar* line; + gchar* head = NULL; + gchar* oldhead = NULL; + gchar** buf; + gchar* header_list; + gchar* saveptr; + + header_list = g_strdup(headers); + line = strtok_r(header_list, "\r\n", &saveptr); + if (line != NULL) { + do { + buf = g_strsplit(line, ":", MAX_TOKENS); + if (buf[1] != NULL) { + if (g_ascii_strcasecmp(buf[0], header) == 0) { + if (head) { + if (strcmp(head, buf[1]) != 0) { + oldhead = head; + head = g_strconcat(head, ", ", buf[1], NULL); + g_free(oldhead); + } + } + else + head = g_strdup(buf[1]); + if (head) + g_strstrip(head); + } + } + g_strfreev(buf); + } while ((line = strtok_r(NULL, "\r\n", &saveptr)) != NULL); + } + g_free(header_list); + if (head) + return (lowcase) ? g_ascii_strdown(head, -1) : head; + else + return NULL; +} + +static const char* VCAL_HEAD = +"BEGIN:VCALENDAR\r\n" +"PRODID:-//CalDAV Calendar//NONSGML libcaldav//EN\r\n" +"VERSION:2.0\r\n"; +static const char* VCAL_FOOT = "END:VCALENDAR"; + +/** + * Parse response from CalDAV server. Internal function. + * @param report Response from server + * @param element XML element to find + * @param type VCalendar element to find + * @param wrap Is this the final parsing or just a part + * @param recursive Stop after first match or not + * @return the parsed result + */ +static gchar* parse_caldav_report_wrap( + char* report, const char* element, const char* type, + gboolean wrap, gboolean recursive) { + char* pos; + char* start; + char* object; + char* tmp_report; + char* tmp; + gchar* response; + gchar* begin_type; + gchar* end_type; + gboolean keep_going = TRUE; + + begin_type = g_strdup_printf("BEGIN:%s", type); + end_type = g_strdup_printf("END:%s", type); + pos = start = object = response = NULL; + tmp_report = g_strdup(report); + while ((pos = strstr(tmp_report, element)) != NULL && keep_going) { + pos = strchr(pos, '>'); + if (!pos) { + break; + } + pos = &(*(pos + 1)); + pos = strstr(pos, begin_type); + if (!pos) { + break; + } + object = &(*(pos + strlen(begin_type))); + object = g_strchug(object); + start = g_strdup(object); + if ((pos = strstr(start, end_type)) == NULL) { + g_free(start); + break; + } + char end_not_found = 1; + while (end_not_found == 1) { + if (strstr(pos+1, end_type) < strstr(pos+1, element)) { + if (strstr(pos+1, end_type) != NULL) { + pos = strstr(pos+1, end_type); + } + else { + end_not_found = 0; + } + } + else { + end_not_found = 0; + } + } + object = g_strndup(start, strlen(start) - strlen(pos)); + if (response) { + tmp = g_strdup(response); + g_free(response); + response = g_strdup_printf("%s%s\r\n%s%s\r\n", + tmp, begin_type, object, end_type); + g_free(tmp); + } + else { + if (wrap) + response = g_strdup_printf("%s%s\r\n%s%s\r\n", + VCAL_HEAD, begin_type, object, end_type); + else + response = g_strdup_printf("%s\r\n%s%s\r\n", + begin_type, object, end_type); + } + if (recursive) { + pos = strchr(pos, '>'); + g_free(tmp_report); + tmp_report = g_strdup(&(*(pos + 1))); + } + else { + keep_going = FALSE; + } + g_free(start); + g_free(object); + } + g_free(tmp_report); + g_free(begin_type); + g_free(end_type); + if (wrap) + if (response) { + object = g_strdup(response); + g_free(response); + response = g_strdup_printf("%s%s", object, VCAL_FOOT); + g_free(object); + } + return response; +} + +/** + * Parse response from CalDAV server + * @param report Response from server + * @param element XML element to find + * @param type VCalendar element to find + * @return the parsed result + */ +gchar* parse_caldav_report(char* report, const char* element, const char* type) { + gchar* response = NULL; + gchar* timezone = NULL; + gchar* temp = NULL; + + if (!report || !element || !type) + return NULL; + /* test for VTIMEZONE. + * Only the first found will be used and this will then + * be the time zone for the entire report + */ + timezone = parse_caldav_report_wrap( + report, element, "VTIMEZONE", FALSE, FALSE); + if (timezone) { + response = g_strdup_printf("%s%s", VCAL_HEAD, timezone); + g_free(timezone); + temp = parse_caldav_report_wrap(report, element, type, FALSE, TRUE); + if (temp) { + gchar* tmp = g_strdup(response); + g_free(response); + response = g_strdup_printf("%s%s%s", tmp, temp, VCAL_FOOT); + g_free(tmp); + g_free(temp); + } + else { + g_free(response); + return NULL; + } + } + else + response = parse_caldav_report_wrap(report, element, type, TRUE, TRUE); + return response; +} + +/** + * Convert a time_t variable to CalDAV DateTime + * @param time a specific date and time + * @return the CalDAV DateTime + */ +gchar* get_caldav_datetime(time_t* time) { + struct tm *current; + gchar* datetime; + + current = localtime(time); + datetime = g_strdup_printf("%d%.2d%.2dT%.2d%.2d%.2dZ", + current->tm_year + 1900, current->tm_mon + 1, current->tm_mday, + current->tm_hour, current->tm_min, current->tm_sec); + return datetime; +} + +/** + * Create a random text string, using MD5. @see caldav_md5_hex_digest() + * @param text some text to randomize + * @return MD5 hash of text + */ +gchar* random_file_name(gchar* text) { + unsigned char* name; + gchar md5sum[33]; + + name = (unsigned char *) g_strdup(text); + caldav_md5_hex_digest(md5sum, name); + g_free(name); + return g_strdup(md5sum); +} + +/** + * Does the event contain a UID element or not. If not add it. + * @param object A specific event + * @return event, eventually added UID + */ +gchar* verify_uid(gchar* object) { + gchar* uid; + gchar* newobj; + gchar* pos; + + newobj = g_strdup(object); + uid = get_response_header("uid", object, TRUE); + if (!uid) { + object = g_strdup(newobj); + g_free(newobj); + pos = strstr(object, "END:VEVENT"); + newobj = g_strndup(object, strlen(object) - strlen(pos)); + newobj = g_strchomp(newobj); + uid = random_file_name(object); + gchar*tmp = g_strdup(newobj); + g_free(newobj); + newobj = g_strdup_printf("%s\r\nUID:libcaldav-%s@tempuri.org\r\n%s", + tmp, uid, pos); + g_free(uid); + g_free(tmp); + g_free(object); + } + else + g_free(uid); + /*uid = g_strdup(newobj); + g_free(newobj);*/ + g_strchomp(newobj); + /*g_free(uid);*/ + return newobj; +} + +/** + * Fetch a URL from a XML element + * @param text String + * @return URL + */ +#define ELEM_HREF "href>" +gchar* get_url(gchar* text) { + gchar* pos; + gchar* url = NULL; + + if ((pos = strstr(text, ELEM_HREF)) == NULL) + return url; + pos = &(*(pos + strlen(ELEM_HREF))); + url = g_strndup(pos, strlen(pos) - strlen(strchr(pos, '<'))); + return url; +} + +/** + * Fetch any element from XML + * @param text String + * @param tag The element to look for + * @return element + */ +gchar* get_tag(const gchar* tag, gchar* text) { + gchar *pos; + gchar* res = NULL; + gchar* the_tag = NULL; + + /*printf("%s\n", text);*/ + the_tag = g_strdup_printf("<%s>", tag); + if ((pos = strstr(text, the_tag)) == NULL) { + g_free(the_tag); + return res; + } + pos = &(*(pos + strlen(the_tag))); + res = g_strndup(pos, strlen(pos) - strlen(strchr(pos, '<'))); + g_free(the_tag); + return res; +} + +/** + * Fetch the etag element from XML + * @param text String + * @return etag + */ +#define ELEM_ETAG "getetag" +gchar* get_etag(gchar* text) { + gchar* etag = NULL; + + etag = get_tag(ELEM_ETAG, text); + /* Maybe namespace prefixed */ + if (!etag) { + etag = get_tag("D:getetag", text); + } + return etag; +} + +/** + * Fetch host from URL + * @param url URL + * @return host + */ +gchar* get_host(gchar* url) { + gchar** buf; + gchar* result = NULL; + + buf = g_strsplit(url, "/", 2); + if (buf[0]) { + result = g_strdup(buf[0]); + } + g_strfreev(buf); + return result; +} + +/** + * rebuild a raw URL with https if needed from the settings + * @param settings caldav_settings + * @param uri URI to use instead of base + * @return URL + */ + +gchar* rebuild_url(caldav_settings* settings, gchar* uri){ + gchar* url = NULL; + gchar* mystr = NULL; + if (settings->usehttps) { + mystr = "https://"; + } else { + mystr = "http://"; + } + if (uri) + url = g_strdup_printf("%s%s", mystr, uri); + else + url = g_strdup_printf("%s%s", mystr,settings->url); + + return url; +} + +/** + * Prepare a curl connection + * @param settings caldav_settings + * @return CURL + */ +CURL* get_curl(caldav_settings* setting) { + CURL* curl; + gchar* userpwd = NULL; + gchar* url = NULL; + + curl = curl_easy_init(); + if (curl) { + if (setting->username) { + if (setting->password) + userpwd = g_strdup_printf("%s:%s", + setting->username, setting->password); + else + userpwd = g_strdup_printf("%s", setting->username); + curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd); + g_free(userpwd); + } + if (setting->verify_ssl_certificate) + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2); + else { + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + } + if (setting->custom_cacert) + curl_easy_setopt(curl, CURLOPT_CAINFO, setting->custom_cacert); + curl_easy_setopt(curl, CURLOPT_USERAGENT, __CALDAV_USERAGENT); + url = rebuild_url(setting, NULL); + curl_easy_setopt(curl, CURLOPT_URL, url); + g_free(url); + } + return (curl) ? curl : NULL; +} diff --git a/src/caldav-utils.h b/src/caldav-utils.h new file mode 100644 index 0000000..02c84d7 --- /dev/null +++ b/src/caldav-utils.h @@ -0,0 +1,221 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __CALDAV_UTILS_H__ +#define __CALDAV_UTILS_H__ + +#include <glib.h> +#include <stdlib.h> +#include <curl/curl.h> +#include "caldav.h" + +/** + * @typedef struct _CALDAV_SETTINGS caldav_settings + * A pointer to a struct _CALDAV_SETTINGS + */ +typedef struct _CALDAV_SETTINGS caldav_settings; + +/** + * @struct _CALDAV_SETTINGS + * A struct used to exchange all user input between various parts + * of the library + */ +struct _CALDAV_SETTINGS { + gchar* username; + gchar* password; + gchar* url; + gchar* file; + gboolean usehttps; + gboolean verify_ssl_certificate; + gchar* custom_cacert; + gboolean debug; + gboolean use_locking; + char trace_ascii; + CALDAV_ACTION ACTION; + time_t start; + time_t end; +}; + +/** + * @typedef struct MemoryStruct memory_ptr + * A pointer to a struct MemoryStruct + */ +typedef struct MemoryStruct memory_ptr; + +/** + * @struct MemoryStruct + * Used to hold messages between the CalDAV server and the library + */ +struct MemoryStruct { + char *memory; + size_t size; +}; + +/** @struct config_data + * Used to exchange user options to the library + */ +struct config_data { + char trace_ascii; +}; + +/** + * This function is burrowed from the libcurl documentation + * @param text + * @param stream + * @param ptr + * @param size + * @param nohex + */ +void dump(const char* text, FILE* stream, char* ptr, size_t size, char nohex); + +/** + * This function is burrowed from the libcurl documentation + * @param handle + * @param type + * @param data + * @param size + * @param userp + * @return + */ +int my_trace(CURL* handle, curl_infotype type, char* data, size_t size, void* userp); + +/** + * This function is burrowed from the libcurl documentation + * @param ptr + * @param size + * @return void* to memory region + */ +size_t WriteMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data); + +/** + * This function is burrowed from the libcurl documentation + * @param ptr + * @param size + * @param nmemb + * @param data + * @return number of written bytes + */ +size_t WriteHeaderCallback(void* ptr, size_t size, size_t nmemb, void* data); + +/*size_t ReadMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data);*/ + +/** + * Initialize caldav settings structure. + * @param settings @see caldav_settings + */ +void init_caldav_settings(caldav_settings* settings); + +/** + * Free momory assigned to caldav settings structure. + * @param settings @see caldav_settings + */ +void free_caldav_settings(caldav_settings* settings); + +/** + * Parse URL + * @param settings @see caldav_settings + * @param url String containing URL to collection + */ +void parse_url(caldav_settings* settings, const char* url); + +/** + * Find a specific HTTP header from last request + * @param header HTTP header to search for + * @param headers String of HTTP headers from last request + * @param lowcase Should string be returned in all lower case. + * @return The header found or NULL + */ +gchar* get_response_header( + const char* header, gchar* headers, gboolean lowcase); + +/** + * Parse response from CalDAV server + * @param report Response from server + * @param element XML element to find + * @param type VCalendar element to find + * @return the parsed result + */ +gchar* parse_caldav_report(char* report, const char* element, const char* type); + +/** + * Convert a time_t variable to CalDAV DateTime + * @param time a specific date and time + * @return the CalDAV DateTime + */ +gchar* get_caldav_datetime(time_t* time); + +/** + * Create a random text string, using MD5. @see caldav_md5_hex_digest() + * @param text some text to randomize + * @return MD5 hash of text + */ +gchar* random_file_name(gchar* text); + +/** + * Does the event contain a UID element or not. If not add it. + * @param object A specific event + * @return event, eventually added UID + */ +gchar* verify_uid(gchar* object); + +/** + * Fetch a URL from a XML element + * @param text String + * @return URL + */ +gchar* get_url(gchar* text); + +/** + * Fetch host from URL + * @param url URL + * @return host + */ +gchar* get_host(gchar* url); + +/** + * Fetch the etag element from XML + * @param text String + * @return etag + */ +gchar* get_etag(gchar* text); + +/** + * Fetch any element from XML + * @param text String + * @return element + */ +gchar* get_tag(const gchar* tag, gchar* text); + + + +/** + * rebuild a raw URL with https if needed from the settings + * @param settings caldav_settings + * @return URL + */ +gchar* rebuild_url(caldav_settings* setting, gchar* uri); + +/** + * Prepare a curl connection + * @param settings caldav_settings + * @return CURL + */ +CURL* get_curl(caldav_settings* setting); + +#endif diff --git a/src/caldav.c b/src/caldav.c new file mode 100644 index 0000000..9e2b0af --- /dev/null +++ b/src/caldav.c @@ -0,0 +1,995 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "caldav.h" +#include "caldav-utils.h" +#include "get-caldav-report.h" +#include "add-caldav-object.h" +#include "delete-caldav-object.h" +#include "modify-caldav-object.h" +#include "get-display-name.h" +#include "options-caldav-server.h" +#include "get-freebusy-report.h" +#include <curl/curl.h> +#include <glib.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static void init_runtime(runtime_info* info) { + if (! info) + return; + if (! info->error) + info->error = g_new0(caldav_error, 1); + if (! info->options) { + info->options = g_new0(debug_curl, 1); + info->options->trace_ascii = 1; + info->options->debug = 0; + info->options->verify_ssl_certificate = TRUE; + info->options->use_locking = TRUE; + info->options->custom_cacert = NULL; + } +} + +/** + * @param curl An instance of libcurl. + * @param settings Defines CalDAV resource. Receiver is responsible for freeing + * the memory. URL is part of the structure. [http://][username:password@]host[:port]/url-path. + * See (RFC1738). + * @return FALSE (zero) mens URL does not reference a CalDAV calendar + * resource. TRUE if the URL does reference a CalDAV calendar resource. + */ +static gboolean test_caldav_enabled(CURL* curl, + caldav_settings* settings, + caldav_error* error) { + return caldav_getoptions(curl, settings, NULL, error, TRUE); +} + +/* + * @param settings An instance of caldav_settings. @see caldav_settings + * @return TRUE if there was an error. Error can be in libcurl, in libcaldav, + * or an error related to the CalDAV protocol. + */ +static gboolean make_caldav_call(caldav_settings* settings, + runtime_info* info) { + CURL* curl; + gboolean result = FALSE; + + g_return_val_if_fail(info != NULL, TRUE); + + curl = get_curl(settings); + if (!curl) { + info->error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + if (!test_caldav_enabled(curl, settings, info->error)) { + g_free(settings->file); + settings->file = NULL; + curl_easy_cleanup(curl); + return TRUE; + } + curl_easy_cleanup(curl); + switch (settings->ACTION) { + case GETALL: result = caldav_getall(settings, info->error); break; + case GET: result = caldav_getrange(settings, info->error); break; + case GETALLTASKS: result = caldav_tasks_getall(settings, info->error); break; + case GETTASKS: result = caldav_tasks_getrange(settings, info->error); break; + case ADD: result = caldav_add(settings, info->error); break; + case DELETE: result = caldav_delete(settings, info->error); break; + case MODIFY: result = caldav_modify(settings, info->error); break; + case DELETETASKS: result = caldav_tasks_delete(settings, info->error); break; + case MODIFYTASKS: result = caldav_tasks_modify(settings, info->error); break; + case GETCALNAME: result = caldav_getname(settings, info->error); break; + case FREEBUSY: result = caldav_freebusy(settings, info->error); break; + default: break; + } + return result; +} + +/** + * Function for adding a new event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_add_object(const char* object, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + init_caldav_settings(&settings); + settings.file = g_strdup(object); + settings.ACTION = ADD; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for deleting an event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_delete_object(const char* object, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + init_caldav_settings(&settings); + settings.file = g_strdup(object); + settings.ACTION = DELETE; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for modifying an event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_modify_object(const char* object, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + init_caldav_settings(&settings); + settings.file = g_strdup(object); + settings.ACTION = MODIFY; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for getting a collection of events determined by time range. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param start time_t variable specifying start and end for range. Both + * are included in range. + * @param end time_t variable specifying start and end for range. Both + * are included in range. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_object(response *result, + time_t start, + time_t end, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + if (!result) { + result = malloc(sizeof(response *)); + memset(result, '\0', sizeof(response *)); + } + init_caldav_settings(&settings); + settings.ACTION = GET; + settings.start = start; + settings.end = end; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + result->msg = NULL; + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + result->msg = g_strdup(settings.file); + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for getting all events from the collection. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_getall_object(response* result, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + if (!result) { + result = malloc(sizeof(response *)); + memset(result, '\0', sizeof(response *)); + } + init_caldav_settings(&settings); + settings.ACTION = GETALL; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + result->msg = NULL; + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + result->msg = g_strdup(settings.file); + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for deleting a task. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_tasks_delete_object(const char* object, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + init_caldav_settings(&settings); + settings.file = g_strdup(object); + settings.ACTION = DELETETASKS; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for modifying a task. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_tasks_modify_object(const char* object, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + init_caldav_settings(&settings); + settings.file = g_strdup(object); + settings.ACTION = MODIFYTASKS; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for getting a collection of tasks determined by time range. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param start time_t variable specifying start and end for range. Both + * are included in range. + * @param end time_t variable specifying start and end for range. Both + * are included in range. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_tasks_get_object(response *result, + time_t start, + time_t end, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + if (!result) { + result = malloc(sizeof(response *)); + memset(result, '\0', sizeof(response *)); + } + init_caldav_settings(&settings); + settings.ACTION = GETTASKS; + settings.start = start; + settings.end = end; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + result->msg = NULL; + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + result->msg = g_strdup(settings.file); + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for getting all tasks from the collection. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_tasks_getall_object(response* result, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + if (!result) { + result = malloc(sizeof(response *)); + memset(result, '\0', sizeof(response *)); + } + init_caldav_settings(&settings); + settings.ACTION = GETALLTASKS; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + result->msg = NULL; + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + result->msg = g_strdup(settings.file); + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function for getting the stored display name for the collection. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_displayname(response* result, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + if (!result) { + result = malloc(sizeof(response *)); + memset(result, '\0', sizeof(response *)); + } + init_caldav_settings(&settings); + settings.ACTION = GETCALNAME; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + result->msg = NULL; + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + result->msg = g_strdup(settings.file); + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function to test wether a calendar resource is CalDAV enabled or not. + * @param URL Defines CalDAV resource. Receiver is responsible for + * freeing the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @result 0 (zero) means no CalDAV support, otherwise CalDAV support + * detechted. + */ +int caldav_enabled_resource(const char* URL, runtime_info* info) { + CURL* curl; + caldav_settings settings; + struct config_data data; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + init_caldav_settings(&settings); + + parse_url(&settings, URL); + curl = get_curl(&settings); + if (!curl) { + info->error->code = -1; + info->error->str = g_strdup("Could not initialize libcurl"); + settings.file = NULL; + return TRUE; + } + + if (info->options->trace_ascii) + data.trace_ascii = 1; + else + data.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + + if (info->options->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + gboolean res = test_caldav_enabled(curl, &settings, info->error); + free_caldav_settings(&settings); + curl_easy_cleanup(curl); + return (res && (info->error->code == 0 || info->error->code == 200)) ? 1 : 0; +} + +/** + * Function for getting free/busy information. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param start time_t variable specifying start and end for range. Both + * are included in range. + * @param end time_t variable specifying start and end for range. Both + * are included in range. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_freebusy(response *result, + time_t start, + time_t end, + const char* URL, + runtime_info* info) { + caldav_settings settings; + CALDAV_RESPONSE caldav_response; + + g_return_val_if_fail(info != NULL, TRUE); + + init_runtime(info); + if (!result) { + result = malloc(sizeof(response *)); + memset(result, '\0', sizeof(response *)); + } + init_caldav_settings(&settings); + settings.ACTION = FREEBUSY; + settings.start = start; + settings.end = end; + if (info->options->debug) + settings.debug = TRUE; + else + settings.debug = FALSE; + if (info->options->trace_ascii) + settings.trace_ascii = 1; + else + settings.trace_ascii = 0; + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + parse_url(&settings, URL); + gboolean res = make_caldav_call(&settings, info); + if (res) { + result->msg = NULL; + if (info->error->code > 0) { + switch (info->error->code) { + case 403: caldav_response = FORBIDDEN; break; + case 409: caldav_response = CONFLICT; break; + case 423: caldav_response = LOCKED; break; + case 501: caldav_response = NOTIMPLEMENTED; break; + default: caldav_response = CONFLICT; break; + } + } + else { + /* fall-back to conflicting state */ + caldav_response = CONFLICT; + } + } + else { + result->msg = g_strdup(settings.file); + caldav_response = OK; + } + free_caldav_settings(&settings); + return caldav_response; +} + +/** + * Function which supports sending various options inside the library. + * @param curl_options A struct debug_curl. See debug_curl. + */ +void caldav_set_options(debug_curl curl_options) { +} + +/** + * @deprecated Function to call in case of errors. + * Caller provides a pointer to a local caldav_error structure. + * Caldav_get_error will initialize pointer if NULL. + * Caller is responsible for freeing returned memory. + * After the first call the internal error buffer is reset. + * @param lib_error A pointer to a struct _caldav_error. @see _caldav_error + * @return An initialized caldav_error pointer to memory where error + * messages can be found from the last call to the library. + */ +caldav_error* caldav_get_error(caldav_error* lib_error) { + if (!lib_error) { + lib_error = g_new0(caldav_error, 1); + } + return lib_error; +} + +/** + * Function for freeing memory for a previous initialization of a + * caldav_error. @see caldav_get_error() + * Caller provides a pointer to a local caldav_error structure. + * @param lib_error A pointer to a struct _caldav_error. @see _caldav_error + */ +void caldav_free_error(caldav_error* lib_error) { + if (lib_error->str) + g_free(lib_error->str); + g_free(lib_error); + lib_error = NULL; +} + +/** + * Function to call to get a list of supported CalDAV options for a server + * @param URL Defines CalDAV resource. Receiver is responsible for + * freeing the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @result A list of available options or NULL in case of any error. + */ +char** caldav_get_server_options(const char* URL, runtime_info* info) { + CURL* curl; + caldav_settings settings; + response server_options; + gchar** option_list = NULL; + gchar** tmp; + gboolean res = FALSE; + + g_return_val_if_fail(info != NULL, NULL); + + init_runtime(info); + tmp = option_list = NULL; + init_caldav_settings(&settings); + + parse_url(&settings, URL); + curl = get_curl(&settings); + if (!curl) { + info->error->code = -1; + info->error->str = g_strdup("Could not initialize libcurl"); + settings.file = NULL; + return NULL; + } + if (info->options->use_locking) + settings.use_locking = 1; + else + settings.use_locking = 0; + + res = caldav_getoptions(curl, &settings, &server_options, info->error, FALSE); + if (res) { + if (server_options.msg) { + option_list = g_strsplit(server_options.msg, ", ", 0); + tmp = &(*(option_list)); + while (*tmp) { + g_strstrip(*tmp++); + } + } + } + free_caldav_settings(&settings); + curl_easy_cleanup(curl); + return (option_list) ? option_list : NULL; +} + +/** + * Function for getting an initialized runtime_info structure + * @return runtime_info. @see runtime_info + */ +runtime_info* caldav_get_runtime_info() { + runtime_info* rt_info; + + rt_info = g_new0(runtime_info, 1); + rt_info->error = g_new0(caldav_error, 1); + rt_info->options = g_new0(debug_curl, 1); + + return rt_info; +} + +/** + * Function for freeing memory for a previous initialization of an info + * structure + * @param info Address to a pointer to a runtime_info structure. @see + * runtime_info + */ +void caldav_free_runtime_info(runtime_info** info) { + runtime_info* ri; + + if (*info) { + ri = *info; + if (ri->error) { + if (ri->error->str) + g_free(ri->error->str); + g_free(ri->error); + ri->error = NULL; + } + if (ri->options) { + if (ri->options->custom_cacert) + g_free(ri->options->custom_cacert); + g_free(ri->options); + ri->options = NULL; + } + g_free(ri); + *info = ri = NULL; + } +} + +/** + * Function for getting an initialized response structure + * @return response. @see _response + */ +response* caldav_get_response() { + response* r; + + r = g_new0(response, 1); + + return r; +} + +/** + * Function for freeing memory for a previous initialization of an response + * structure + * @param info Address to a pointer to a response structure. @see + * _response + */ +void caldav_free_response(response** resp) { + response* r; + + if (*resp) { + r = *resp; + if (r->msg) + g_free(r->msg); + g_free(r); + *resp = r = NULL; + } +} diff --git a/src/caldav.h b/src/caldav.h new file mode 100644 index 0000000..5734404 --- /dev/null +++ b/src/caldav.h @@ -0,0 +1,407 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/** + * @file caldav.h + * @brief interface to the caldav library. + * The library conforms to RFC4791. For further information follow this + * link http://www.ietf.org/rfc/rfc4791.txt + */ + +/** + * @mainpage + * This document is the documentation for the public interface to libcaldav. + * If you want to study the implementation look for the developers API. + * + * The libray and documentation is Copyright (c) 2008 Michael Rasmussen + * (mir@datanom.net) + * + * License for the source code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * License for the documentation. + * + * Permission is granted to copy, distribute and/or modify this document + * under the terms of the GNU Free Documentation License, Version 1.2 + * or any later version published by the Free Software Foundation; + * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + * Texts. + */ + +#ifndef __CALDAV_H__ +#define __CALDAV_H__ + +#include <time.h> + +/* For debug purposes */ +/** + * @typedef struct debug_curl + * A struct used to set internal options in the library + */ +typedef struct { + int trace_ascii; /** @var int trace_ascii + * 0 or 1 + */ + int debug; /** @var int debug + * 0 or 1 + */ + int verify_ssl_certificate; + int use_locking; + char* custom_cacert; +} debug_curl; + +/** + * @typedef struct _caldav_error caldav_error + * Pointer to a caldav_error structure + */ +typedef struct _caldav_error caldav_error; + +/** + * @struct _caldav_error + * A struct for storing error codes and messages + */ +struct _caldav_error { + long code; /** + * @var long code + * if < 0 internal error > 0 CalDAV protocol error. + */ + char* str; /** @var char* str + * For storing human readable error message + */ +}; + +/** + * @typedef struct runtime_info + * Pointer to a runtime structure holding debug and error information + */ +typedef struct { + caldav_error* error; + debug_curl* options; +} runtime_info; + +/* CalDAV is defined in RFC4791 */ + +/* Buffer to hold response */ +/** + * @typedef struct _response response + * Pointer to a _response structure + */ +typedef struct _response response; + +/** + * @struct _response + * A struct used for returning messages from the library to users + */ +struct _response { + char* msg; /** @var char* msg + * String for storing response + */ +}; + +/** + * @enum CALDAV_ACTION specifies supported CalDAV actions. + * UNKNOWN. An unknown action. + * ADD. Add a CalDAV calendar object. + * DELETE. Delete a CalDAV calendar object. + * MODIFY. Modify a CalDAV calendar object. + * GET. Get one or more CalDAV calendar object(s). + * GETALL. Get all CalDAV calendar objects. + */ +typedef enum { + UNKNOWN, + ADD, + DELETE, + FREEBUSY, + MODIFY, + GET, + GETALL, + GETCALNAME, + ISCALDAV, + OPTIONS, + DELETETASKS, + MODIFYTASKS, + GETTASKS, + GETALLTASKS, +} CALDAV_ACTION; + +/** + * @enum CALDAV_RESPONSE specifies CalDAV error states. + * OK (HTTP 200). Request was satisfied. + * FORBIDDEN (HTTP 403). Access not allowed. Dont repeat request. + * CONFLICT (HTTP 409). Conflict between current state of CalDAV collection + * and request. Client must solve the conflict and then resend request. + * LOCKED (HTTP 423). Locking failed. + */ +typedef enum { + OK, + FORBIDDEN, + CONFLICT, + LOCKED, + NOTIMPLEMENTED +} CALDAV_RESPONSE; + + +#ifndef __CALDAV_USERAGENT +#define __CALDAV_USERAGENT "libcurl-agent/0.1" +#endif + + +/** + * Function for adding a new event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_add_object(const char* object, + const char* URL, + runtime_info* info); + +/** + * Function for deleting an event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_delete_object(const char* object, + const char* URL, + runtime_info* info); + +/** + * Function for modifying an event. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_modify_object(const char* object, + const char* URL, + runtime_info* info); + +/** + * Function for getting a collection of events determined by time range. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param start time_t variable specifying start for range. Included in search. + * @param end time_t variable specifying end for range. Included in search. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_object(response* result, + time_t start, + time_t end, + const char* URL, + runtime_info* info); + +/** + * Function for getting all events from the collection. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_getall_object(response* result, + const char* URL, + runtime_info* info); + +/** + * Function for deleting a task. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_tasks_delete_object(const char* object, + const char* URL, + runtime_info* info); + +/** + * Function for modifying a task. + * @param object Appointment following ICal format (RFC2445). Receiver is + * responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_tasks_modify_object(const char* object, + const char* URL, + runtime_info* info); + +/** + * Function for getting a collection of tasks determined by time range. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param start time_t variable specifying start for range. Included in search. + * @param end time_t variable specifying end for range. Included in search. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_tasks_get_object(response* result, + time_t start, + time_t end, + const char* URL, + runtime_info* info); + +/** + * Function for getting all tasks from the collection. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_tasks_getall_object(response* result, + const char* URL, + runtime_info* info); + +/** + * Function for getting the stored display name for the collection. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_displayname(response* result, + const char* URL, + runtime_info* info); + +/** + * Function to test wether a calendar resource is CalDAV enabled or not. + * @param URL Defines CalDAV resource. Receiver is responsible for + * freeing the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @result 0 (zero) means no CalDAV support, otherwise CalDAV support + * detechted. + */ +int caldav_enabled_resource(const char* URL, runtime_info* info); + +/** + * Function for getting free/busy information. + * @param result A pointer to struct _response where the result is to stored. + * @see response. Caller is responsible for freeing the memory. + * @param start time_t variable specifying start and end for range. Both + * are included in range. + * @param end time_t variable specifying start and end for range. Both + * are included in range. + * @param URL Defines CalDAV resource. Receiver is responsible for freeing + * the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @return Ok, FORBIDDEN, or CONFLICT. @see CALDAV_RESPONSE + */ +CALDAV_RESPONSE caldav_get_freebusy(response *result, + time_t start, + time_t end, + const char* URL, + runtime_info* info); + +/** + * @deprecated Always returns an initialized empty caldav_error + * Function to call in case of errors. + * Caller provides a pointer to a local caldav_error structure. + * Caldav_get_error will initialize pointer if NULL. + * Caller is responsible for freeing returned memory. + * After the first call the internal error buffer is reset. + * @param lib_error A pointer to a struct _caldav_error. @see _caldav_error + * @return An initialized caldav_error pointer to memory where error + * messages can be found from the last call to the library. + */ +caldav_error* caldav_get_error(caldav_error* lib_error); + +/** + * Function for freeing memory for a previous initialization of a + * caldav_error. @see caldav_get_error() + * Caller provides a pointer to a local caldav_error structure. + * @param lib_error A pointer to a struct _caldav_error. @see _caldav_error + */ +void caldav_free_error(caldav_error* lib_error); + +/* Setting various options in library */ + +/** + * @deprecated Does nothing + * Function which supports sending various options inside the library. + * @param curl_options A struct debug_curl. See debug_curl. + */ +void caldav_set_options(debug_curl curl_options); + +/** + * Function to call to get a list of supported CalDAV options for a server + * @param URL Defines CalDAV resource. Receiver is responsible for + * freeing the memory. [http://][username[:password]@]host[:port]/url-path. + * See (RFC1738). + * @param info Pointer to a runtime_info structure. @see runtime_info + * @result A list of available options or NULL in case of any error. + */ +char** caldav_get_server_options(const char* URL, runtime_info* info); + +/** + * Function for getting an initialized runtime_info structure + * @return runtime_info. @see runtime_info + */ +runtime_info* caldav_get_runtime_info(); + +/** + * Function for freeing memory for a previous initialization of an info + * structure + * @param info Address to a pointer to a runtime_info structure. @see + * runtime_info + */ +void caldav_free_runtime_info(runtime_info** info); + +/** + * Function for getting an initialized response structure + * @return response. @see _response + */ +response* caldav_get_response(); + +/** + * Function for freeing memory for a previous initialization of an response + * structure + * @param info Address to a pointer to a response structure. @see + * _response + */ +void caldav_free_response(response** info); + +#endif diff --git a/src/delete-caldav-object.c b/src/delete-caldav-object.c new file mode 100644 index 0000000..9a85168 --- /dev/null +++ b/src/delete-caldav-object.c @@ -0,0 +1,533 @@ +/* vim: set textwidth=80 tabstop=4: */ +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "delete-caldav-object.h" +#include "lock-caldav-object.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * A static literal string containing the first part of the calendar query. + * The actual UID to use for the query is added at runtime. + */ +static char* search_head = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>" +" <D:getetag/>" +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VEVENT\">" +" <C:prop-filter name=\"UID\">"; + +/** + * A static literal string containing the first part of the calendar query. + * The actual UID to use for the query is added at runtime. + */ +static char* search_tasks_head = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>" +" <D:getetag/>" +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VTODO\">" +" <C:prop-filter name=\"UID\">"; + +/** + * A static literal string containing the last part of the calendar query + */ +static char* search_tail = +"</C:prop-filter>" +" </C:comp-filter>" +" </C:comp-filter>" +" </C:filter>" +"</C:calendar-query>"; + +/** + * Function for deleting an event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_delete(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gchar* search; + gchar* uid; + gboolean LOCKSUPPORT = FALSE; + gchar* lock_token = NULL; + gboolean result = FALSE; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: infinity"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + gchar* file = g_strdup(settings->file); + if ((uid = get_response_header("uid", file, FALSE)) == NULL) { + g_free(file); + error->code = 1; + error->str = g_strdup("Error: Missing required UID for object"); + return TRUE; + } + g_free(file); + /* + * ICalendar server does not support collation + * <C:text-match collation=\"i;ascii-casemap\">%s</C:text-match> + */ + search = g_strdup_printf( + "%s\r\n<C:text-match>%s</C:text-match>\r\n%s", + search_head, uid, search_tail); + g_free(uid); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, search); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(search)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + g_free(search); + curl_slist_free_all(http_header); + http_header = NULL; + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 207) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + else { + /* enable uploading */ + gchar* url = NULL; + gchar* etag = NULL; + url = get_url(chunk.memory); + if (url) { + etag = get_etag(chunk.memory); + if (etag) { + gchar* host = get_host(settings->url); + if (host) { + file = g_strdup(url); + g_free(url); + url = g_strdup_printf("%s%s", host, file); + g_free(file); + g_free(host); + } + else { + g_free(etag); + g_free(url); + url = NULL; + } + } + else { + g_free(url); + url = NULL; + } + } + if (url) { + int lock = 0; + caldav_error lock_error; + + file = g_strdup(etag); + g_free(etag); + etag = g_strdup_printf("If-Match: %s", file); + g_free(file); + http_header = curl_slist_append(http_header, etag); + g_free(etag); + http_header = curl_slist_append(http_header, + "Content-Type: text/calendar; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append( + http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + if (settings->use_locking) + LOCKSUPPORT = caldav_lock_support(settings, &lock_error); + else + LOCKSUPPORT = FALSE; + if (LOCKSUPPORT) { + lock_token = caldav_lock_object(url, settings, &lock_error); + if (lock_token) { + http_header = curl_slist_append( + http_header, g_strdup_printf( + "If: (%s)", lock_token)); + } + /* + * If error code is 423 (Resource is LOCKED) bail out + */ + else if (lock_error.code == 423) { + lock = -1; + } + /* + * If error code is 501 (Not implemented) we continue + * hoping for the best. + */ + else if (lock_error.code == 501) { + lock_token = g_strdup(""); + } + else { + lock = -1; + } + } + if (! LOCKSUPPORT || (LOCKSUPPORT && lock_token && lock_error.code != 423)) { + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + curl_easy_setopt(curl, CURLOPT_URL, rebuild_url(settings, url)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, 0); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (LOCKSUPPORT && lock_token) { + caldav_unlock_object( + lock_token, url, settings, &lock_error); + } + } + g_free(url); + g_free(lock_token); + if (res != 0 || lock < 0) { + /* Is this a lock_error don't change error*/ + if (lock == 0 || lock_error.code == 423) { + error->code = code; + error->str = g_strdup(chunk.memory); + } + else { + error->code = lock_error.code; + error->str = g_strdup(lock_error.str); + } + result = TRUE; + g_free(settings->file); + settings->file = NULL; + } + else { + long code; + res = curl_easy_getinfo( + curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 204) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + } + curl_slist_free_all(http_header); + } + else { + error->code = code; + if (chunk.memory) + error->str = g_strdup(chunk.memory); + else + error->str = g_strdup("No object found"); + result = TRUE; + } + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_cleanup(curl); + return result; +} + +/** + * Function for deleting a task. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_tasks_delete(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gchar* search; + gchar* uid; + gboolean LOCKSUPPORT = FALSE; + gchar* lock_token = NULL; + gboolean result = FALSE; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: infinity"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + gchar* file = g_strdup(settings->file); + if ((uid = get_response_header("uid", file, FALSE)) == NULL) { + g_free(file); + error->code = 1; + error->str = g_strdup("Error: Missing required UID for object"); + return TRUE; + } + g_free(file); + /* + * ICalendar server does not support collation + * <C:text-match collation=\"i;ascii-casemap\">%s</C:text-match> + */ + search = g_strdup_printf( + "%s\r\n<C:text-match>%s</C:text-match>\r\n%s", + search_tasks_head, uid, search_tail); + g_free(uid); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, search); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(search)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + g_free(search); + curl_slist_free_all(http_header); + http_header = NULL; + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 207) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + else { + /* enable uploading */ + gchar* url = NULL; + gchar* etag = NULL; + url = get_url(chunk.memory); + if (url) { + etag = get_etag(chunk.memory); + if (etag) { + gchar* host = get_host(settings->url); + if (host) { + file = g_strdup(url); + g_free(url); + url = g_strdup_printf("%s%s", host, file); + g_free(file); + g_free(host); + } + else { + g_free(etag); + g_free(url); + url = NULL; + } + } + else { + g_free(url); + url = NULL; + } + } + if (url) { + int lock = 0; + caldav_error lock_error; + + file = g_strdup(etag); + g_free(etag); + etag = g_strdup_printf("If-Match: %s", file); + g_free(file); + http_header = curl_slist_append(http_header, etag); + g_free(etag); + http_header = curl_slist_append(http_header, + "Content-Type: text/calendar; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append( + http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + if (settings->use_locking) + LOCKSUPPORT = caldav_lock_support(settings, &lock_error); + else + LOCKSUPPORT = FALSE; + if (LOCKSUPPORT) { + lock_token = caldav_lock_object(url, settings, &lock_error); + if (lock_token) { + http_header = curl_slist_append( + http_header, g_strdup_printf( + "If: (%s)", lock_token)); + } + /* + * If error code is 423 (Resource is LOCKED) bail out + */ + else if (lock_error.code == 423) { + lock = -1; + } + /* + * If error code is 501 (Not implemented) we continue + * hoping for the best. + */ + else if (lock_error.code == 501) { + lock_token = g_strdup(""); + } + else { + lock = -1; + } + } + if (! LOCKSUPPORT || (LOCKSUPPORT && lock_token && lock_error.code != 423)) { + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + curl_easy_setopt(curl, CURLOPT_URL, rebuild_url(settings, url)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, 0); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (LOCKSUPPORT && lock_token) { + caldav_unlock_object( + lock_token, url, settings, &lock_error); + } + } + g_free(url); + g_free(lock_token); + if (res != 0 || lock < 0) { + /* Is this a lock_error don't change error*/ + if (lock == 0 || lock_error.code == 423) { + error->code = code; + error->str = g_strdup(chunk.memory); + } + else { + error->code = lock_error.code; + error->str = g_strdup(lock_error.str); + } + result = TRUE; + g_free(settings->file); + settings->file = NULL; + } + else { + long code; + res = curl_easy_getinfo( + curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 204) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + } + curl_slist_free_all(http_header); + } + else { + error->code = code; + if (chunk.memory) + error->str = g_strdup(chunk.memory); + else + error->str = g_strdup("No object found"); + result = TRUE; + } + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_cleanup(curl); + return result; +} diff --git a/src/delete-caldav-object.h b/src/delete-caldav-object.h new file mode 100644 index 0000000..cddf711 --- /dev/null +++ b/src/delete-caldav-object.h @@ -0,0 +1,43 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __DELETE_CALDAV_OBJECT_H__ +#define __DELETE_CALDAV_OBJECT_H__ + +#include "caldav-utils.h" +#include "caldav.h" + +/** + * Function for deleting an event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_delete(caldav_settings* settings, caldav_error* error); + +/** + * Function for deleting a task. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_tasks_delete(caldav_settings* settings, caldav_error* error); + +#endif + diff --git a/src/get-caldav-report.c b/src/get-caldav-report.c new file mode 100644 index 0000000..d916259 --- /dev/null +++ b/src/get-caldav-report.c @@ -0,0 +1,472 @@ +/* vim: set textwidth=80 tabstop=4 smarttab: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "get-caldav-report.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * A static literal string containing the calendar query for fetching + * all events from collection. + */ +static const char* getall_request = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>" +" <D:getetag/>" +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VEVENT\"/>" +" </C:comp-filter>" +" </C:filter>" +"</C:calendar-query>\r\n"; + +/** + * A static literal string containing the first part of the calendar query. + * The actual VEVENT to search for is added at runtime. + */ +static const char* getrange_request_head = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +/*"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>"*/ +"<C:calendar-query xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop xmlns:D=\"DAV:\">" +/*" <D:getetag/>"*/ +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VEVENT\">"; + +/** + * A static literal string containing the last part of the calendar query + */ +static const char* getrange_request_foot = +" </C:comp-filter>" +" </C:comp-filter>" +" </C:filter>" +"</C:calendar-query>\r\n"; + +/** + * A static literal string containing the calendar query for fetching + * all tasks from collection. + */ +static const char* getall_tasks_request = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>" +" <D:getetag/>" +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VTODO\"/>" +" </C:comp-filter>" +" </C:filter>" +"</C:calendar-query>\r\n"; + +/** + * A static literal string containing the first part of the calendar query. + * The actual VTODO to search for is added at runtime. + */ +static const char* getrange_tasks_request_head = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +/*"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>"*/ +"<C:calendar-query xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop xmlns:D=\"DAV:\">" +/*" <D:getetag/>"*/ +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VTODO\">"; + +/** + * Function for getting all events from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getall(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 1"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + data.trace_ascii = settings->trace_ascii; + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, getall_request); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(getall_request)); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 207) { + error->code = code; + error->str = g_strdup(headers.memory); + result = TRUE; + } + else { + gchar* report; + report = parse_caldav_report( + chunk.memory, "calendar-data", "VEVENT"); + settings->file = g_strdup(report); + g_free(report); + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_slist_free_all(http_header); + curl_easy_cleanup(curl); + return result; +} + +/** + * Function for getting all events within a time range from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getrange(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE + 1]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + gchar* request = NULL; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 1"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + request = g_strdup_printf( + "%s\r\n<C:time-range start=\"%s\"\r\n end=\"%s\"/>\r\n%s", + getrange_request_head, get_caldav_datetime(&settings->start), + get_caldav_datetime(&settings->end), getrange_request_foot); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(request)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + gchar* report; + report = parse_caldav_report(chunk.memory, "calendar-data", "VEVENT"); + settings->file = g_strdup(report); + g_free(report); + } + g_free(request); + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_slist_free_all(http_header); + curl_easy_cleanup(curl); + return result; +} + +/** + * Function for getting all tasks from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_tasks_getall(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 1"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + data.trace_ascii = settings->trace_ascii; + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, getall_tasks_request); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(getall_tasks_request)); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 207) { + error->code = code; + error->str = g_strdup(headers.memory); + result = TRUE; + } + else { + gchar* report; + report = parse_caldav_report( + chunk.memory, "calendar-data", "VTODO"); + settings->file = g_strdup(report); + g_free(report); + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_slist_free_all(http_header); + curl_easy_cleanup(curl); + return result; +} + +/** + * Function for getting all events within a time range from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_tasks_getrange(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE + 1]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + gchar* request = NULL; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 1"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + request = g_strdup_printf( + "%s\r\n<C:time-range start=\"%s\"\r\n end=\"%s\"/>\r\n%s", + getrange_tasks_request_head, get_caldav_datetime(&settings->start), + get_caldav_datetime(&settings->end), getrange_request_foot); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(request)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + gchar* report; + report = parse_caldav_report(chunk.memory, "calendar-data", "VTODO"); + settings->file = g_strdup(report); + g_free(report); + } + g_free(request); + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_slist_free_all(http_header); + curl_easy_cleanup(curl); + return result; +} diff --git a/src/get-caldav-report.h b/src/get-caldav-report.h new file mode 100644 index 0000000..f769685 --- /dev/null +++ b/src/get-caldav-report.h @@ -0,0 +1,59 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __GET_CALDAV_REPORT_H__ +#define __GET_CALDAV_REPORT_H__ + +#include "caldav-utils.h" +#include "caldav.h" +#include <glib.h> + +/** + * Function for getting all events from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getall(caldav_settings* settings, caldav_error* error); + +/** + * Function for getting all events within a time range from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getrange(caldav_settings* settings, caldav_error* error); + +/** + * Function for getting all tasks from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_tasks_getall(caldav_settings* settings, caldav_error* error); + +/** + * Function for getting all tasks within a time range from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_tasks_getrange(caldav_settings* settings, caldav_error* error); + +#endif diff --git a/src/get-display-name.c b/src/get-display-name.c new file mode 100644 index 0000000..8fc0ff8 --- /dev/null +++ b/src/get-display-name.c @@ -0,0 +1,139 @@ +/* vim: set textwidth=80 tabstop=4 smarttab: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "get-caldav-report.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * A static literal string containing the calendar query for fetching + * the stored display name for the collection. + */ +static const char* getname_request = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<D:propfind xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>" +" <D:displayname/>" +" </D:prop>" +"</D:propfind>\r\n"; + +/** + * Function for getting the display name from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getname(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 0"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + data.trace_ascii = settings->trace_ascii; + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, getname_request); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(getname_request)); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PROPFIND"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 207) { + error->code = code; + error->str = g_strdup(headers.memory); + result = TRUE; + } + else { + gchar* displayname; + displayname = get_tag("displayname", chunk.memory); + /* Maybe namespace prefixed */ + if (!displayname) { + displayname = get_tag("D:displayname", chunk.memory); + } + settings->file = (displayname) ? + g_strdup(displayname) : g_strdup(""); + g_free(displayname); + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_slist_free_all(http_header); + curl_easy_cleanup(curl); + return result; +} + diff --git a/src/get-display-name.h b/src/get-display-name.h new file mode 100644 index 0000000..5c80272 --- /dev/null +++ b/src/get-display-name.h @@ -0,0 +1,34 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __GET_DISPLAY_NAME_H__ +#define __GET_DISPLAY_NAME_H__ + +#include "caldav-utils.h" + +/** + * Function for getting the display name from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_getname(caldav_settings* settings, caldav_error* error); + +#endif + diff --git a/src/get-freebusy-report.c b/src/get-freebusy-report.c new file mode 100644 index 0000000..f75ab92 --- /dev/null +++ b/src/get-freebusy-report.c @@ -0,0 +1,140 @@ +/* vim: set textwidth=80 tabstop=4 smarttab: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "get-freebusy-report.h" +#include <glib.h> +#include <curl/curl.h> +#include <string.h> + +/** + * A static literal string containing the first part of the calendar query. + * The actual VEVENT to search for is added at runtime. + */ +static const char* getrange_request_head = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<C:free-busy-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">"; + +/** + * A static literal string containing the last part of the calendar query + */ +static const char* getrange_request_foot = +"</C:free-busy-query>\r\n"; + +/** + * Function for getting freebusy within a time range from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_freebusy(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE + 1]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + gchar* request = NULL; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 1"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + request = g_strdup_printf( + "%s\r\n<C:time-range start=\"%s\"\r\n end=\"%s\"/>\r\n%s", + getrange_request_head, get_caldav_datetime(&settings->start), + get_caldav_datetime(&settings->end), getrange_request_foot); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(request)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 200) { + error->code = code; + error->str = g_strdup(headers.memory); + result = TRUE; + } + else { + /*gchar* report; + report = parse_caldav_report(chunk.memory, "calendar-data", "VFREEBUSY"); + settings->file = g_strdup(report);*/ + settings->file = g_strdup(chunk.memory); + /*g_free(report);*/ + } + } + g_free(request); + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_slist_free_all(http_header); + curl_easy_cleanup(curl); + return result; +} diff --git a/src/get-freebusy-report.h b/src/get-freebusy-report.h new file mode 100644 index 0000000..6c52c51 --- /dev/null +++ b/src/get-freebusy-report.h @@ -0,0 +1,35 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __GET_FREEBUSY_REPORT_H__ +#define __GET_FREEBUSY_REPORT_H__ + +#include "caldav-utils.h" +#include "caldav.h" +#include <glib.h> + +/** + * Function for getting freebusy within a time range from collection. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_freebusy(caldav_settings* settings, caldav_error* error); + +#endif diff --git a/src/lock-caldav-object.c b/src/lock-caldav-object.c new file mode 100644 index 0000000..1ddf926 --- /dev/null +++ b/src/lock-caldav-object.c @@ -0,0 +1,292 @@ +/* vim: set textwidth=80 tabstop=4: */ +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "lock-caldav-object.h" +#include "options-caldav-server.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * A static literal string containing the lock query. + */ +static char* lock_query = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<D:lockinfo xmlns:D=\"DAV:\">" +" <D:lockscope><D:exclusive/></D:lockscope>" +" <D:locktype><D:write/></D:locktype>" +"</D:lockinfo>"; + +/** + * Function which requests a lock on a calendar resource + * @param URI The resource to request lock on. + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return The Lock-Token or NULL in case of error + */ +gchar* caldav_lock_object( + gchar* URI, caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gchar* lock_token = NULL; + gchar* url; + + if (! caldav_lock_support(settings, error)) + return lock_token; + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return lock_token; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Timeout: Second-300"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + if (settings->usehttps) { + url = g_strdup_printf("https://%s", URI); + } else { + url = g_strdup_printf("http://%s", URI); + } + curl_easy_setopt(curl, CURLOPT_URL, url); + g_free(url); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, lock_query); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(lock_query)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "LOCK"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + curl_slist_free_all(http_header); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 200) { + gchar* status = get_tag("status", chunk.memory); + if (status && strstr(status, "423") != NULL) { + error->code = 423; + error->str = g_strdup(status); + } + else { + error->code = code; + error->str = g_strdup(chunk.memory); + } + g_free(status); + } + else { + lock_token = get_response_header( + "Lock-Token", headers.memory, FALSE); + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_cleanup(curl); + return lock_token; +} + +/** + * Function which requests to have a lock removed from a calendar resource + * @param lock_token A privious aquired Lock-Token + * @param URI The resource to request unlock on. + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return False in case the lock could not be removed. True otherwise. + */ +gboolean caldav_unlock_object(gchar* lock_token, gchar* URI, + caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gboolean result = FALSE; + gchar* url; + + if (! caldav_lock_support(settings, error)) + return result; + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + g_strdup_printf("Lock-Token: %s", lock_token)); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + if (settings->usehttps) { + url = g_strdup_printf("https://%s", URI); + } else { + url = g_strdup_printf("http://%s", URI); + } + curl_easy_setopt(curl, CURLOPT_URL, url); + g_free(url); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "UNLOCK"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + curl_slist_free_all(http_header); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 204) { + error->code = code; + error->str = g_strdup(chunk.memory); + } + else { + result = TRUE; + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_cleanup(curl); + return result; +} + +/** + * Function to test whether the server supports locking or not. Searching + * for PROP LOCK. If LOCK is present then according to RFC4791 PROP UNLOCK + * must also be present. + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return True if locking is supported by the server. False otherwise + */ +gboolean caldav_lock_support(caldav_settings* settings, caldav_error* error) { + gboolean found = FALSE; + gchar* url = NULL; + gchar* mystr = NULL; + runtime_info* info; + + info = caldav_get_runtime_info(); + info->options->verify_ssl_certificate = settings->verify_ssl_certificate; + info->options->custom_cacert = g_strdup(settings->custom_cacert); + if (settings->usehttps) { + mystr = g_strdup("https://"); + } else { + mystr = g_strdup("http://"); + } + + + if (settings->username && settings->password) { + url = g_strdup_printf("%s%s:%s@%s", + mystr, settings->username, settings->password, settings->url); + } + else if (settings->username) { + url = g_strdup_printf("%s%s@%s", + mystr, settings->username, settings->url); + } + else { + url = g_strdup_printf("%s%s", mystr, settings->url); + } + gchar** options = caldav_get_server_options(url, info); + g_free(url); + gchar** tmp = options; + caldav_free_runtime_info(&info); + while (*options) { + if (strcmp(*options++, "LOCK") == 0) { + found = TRUE; + break; + } + } + g_strfreev(tmp); + g_free(mystr); + return found; +} + + diff --git a/src/lock-caldav-object.h b/src/lock-caldav-object.h new file mode 100644 index 0000000..6dd884f --- /dev/null +++ b/src/lock-caldav-object.h @@ -0,0 +1,55 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __LOCK_CALDAV_OBJECT_H__ +#define __LOCK_CALDAV_OBJECT_H__ + +#include "caldav-utils.h" +#include "caldav.h" + +/** + * Function which requests a lock on a calendar resource + * @param URI The resource to request lock on. + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return The Lock-Token or NULL in case of error + */ +gchar* caldav_lock_object( + gchar* URI, caldav_settings* settings, caldav_error* error); + +/** + * Function which requests to have a lock removed from a calendar resource + * @param lock_token A privious aquired Lock-Token + * @param URI The resource to request unlock on. + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return False in case the lock could not be removed. True otherwise. + */ +gboolean caldav_unlock_object(gchar* lock_token, gchar* URI, + caldav_settings* settings, caldav_error* error); + +/** + * Function to test whether the server supports locking or not + * @param settings @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return True if locking is supported by the server. False otherwise + */ +gboolean caldav_lock_support(caldav_settings* settings, caldav_error* error); + +#endif diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..62d4b2d --- /dev/null +++ b/src/md5.c @@ -0,0 +1,442 @@ +/** md5.c - MD5 Message-Digest Algorithm + * Copyright (C) 1995, 1996, 1998, 1999 Free Software Foundation, Inc. + * + * according to the definition of MD5 in RFC 1321 from April 1992. + * NOTE: This is *not* the same file as the one from glibc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 3, or (at your option) any + * later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. + * heavily modified for GnuPG by <werner.koch@guug.de>. + * modified again for Sylpheed by <wk@gnupg.org> 2001-02-11. + */ + + +/* Test values: + * "" D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E + * "a" 0C C1 75 B9 C0 F1 B6 A8 31 C3 99 E2 69 77 26 61 + * "abc 90 01 50 98 3C D2 4F B0 D6 96 3F 7D 28 E1 7F 72 + * "message digest" F9 6B 69 7D 7C B7 93 8D 52 5A 2F 31 AA F1 61 D0 + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "md5.h" + +/**************** + * Rotate a 32 bit integer by n bytes + */ +#if defined(__GNUC__) && defined(__i386__) +static inline u32 +rol( u32 x, int n) +{ + __asm__("roll %%cl,%0" + :"=r" (x) + :"0" (x),"c" (n)); + return x; +} +#else +#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) ) +#endif + + +static void +md5_init(MD5_CONTEXT *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->nblocks = 0; + ctx->count = 0; + ctx->finalized = 0; +} + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + + +/**************** + * transform n*64 bytes + */ +static void +transform(MD5_CONTEXT *ctx, const unsigned char *data) +{ + u32 correct_words[16]; + u32 A = ctx->A; + u32 B = ctx->B; + u32 C = ctx->C; + u32 D = ctx->D; + u32 *cwp = correct_words; + +#ifdef BIG_ENDIAN_HOST + { + int i; + unsigned char *p2; + const unsigned char *p1; + + for (i = 0, p1 = data, p2 = (unsigned char*)correct_words; + i < 16; i++, p2 += 4) { + p2[3] = *p1++; + p2[2] = *p1++; + p2[1] = *p1++; + p2[0] = *p1++; + } + } +#else + memcpy(correct_words, data, 64); +#endif + + +#define OP(a, b, c, d, s, T) \ + do { \ + a += FF (b, c, d) + (*cwp++) + T; \ + a = rol(a, s); \ + a += b; \ + } while (0) + + /* Before we start, one word about the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do { \ + a += f (b, c, d) + correct_words[k] + T; \ + a = rol(a, s); \ + a += b; \ + } while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Put checksum in context given as argument. */ + ctx->A += A; + ctx->B += B; + ctx->C += C; + ctx->D += D; +} + + + +/* The routine updates the message-digest context to + * account for the presence of each of the characters inBuf[0..inLen-1] + * in the message whose digest is being computed. + */ +static void +md5_update(MD5_CONTEXT *hd, const unsigned char *inbuf, size_t inlen) +{ + if (hd->count == 64) { /* flush the buffer */ + transform( hd, hd->buf ); + hd->count = 0; + hd->nblocks++; + } + if (!inbuf) + return; + if (hd->count) { + for (; inlen && hd->count < 64; inlen--) + hd->buf[hd->count++] = *inbuf++; + md5_update(hd, NULL, 0); + if (!inlen) + return; + } + + while (inlen >= 64) { + transform(hd, inbuf); + hd->count = 0; + hd->nblocks++; + inlen -= 64; + inbuf += 64; + } + + for (; inlen && hd->count < 64; inlen--) + hd->buf[hd->count++] = *inbuf++; +} + + + +/* The routine final terminates the message-digest computation and + * ends with the desired message digest in mdContext->digest[0...15]. + * The handle is prepared for a new MD5 cycle. + * Returns 16 bytes representing the digest. + */ + +static void +do_final(MD5_CONTEXT *hd) +{ + u32 t, msb, lsb; + unsigned char *p; + + md5_update(hd, NULL, 0); /* flush */ + + msb = 0; + t = hd->nblocks; + if ((lsb = t << 6) < t) /* multiply by 64 to make a byte count */ + msb++; + msb += t >> 26; + t = lsb; + if ((lsb = t + hd->count) < t) /* add the count */ + msb++; + t = lsb; + if ((lsb = t << 3) < t) /* multiply by 8 to make a bit count */ + msb++; + msb += t >> 29; + + if (hd->count < 56) { /* enough room */ + hd->buf[hd->count++] = 0x80; /* pad */ + while(hd->count < 56) + hd->buf[hd->count++] = 0; /* pad */ + } else { /* need one extra block */ + hd->buf[hd->count++] = 0x80; /* pad character */ + while (hd->count < 64) + hd->buf[hd->count++] = 0; + md5_update(hd, NULL, 0); /* flush */ + memset(hd->buf, 0, 56); /* fill next block with zeroes */ + } + + /* append the 64 bit count */ + hd->buf[56] = lsb ; + hd->buf[57] = lsb >> 8; + hd->buf[58] = lsb >> 16; + hd->buf[59] = lsb >> 24; + hd->buf[60] = msb ; + hd->buf[61] = msb >> 8; + hd->buf[62] = msb >> 16; + hd->buf[63] = msb >> 24; + transform(hd, hd->buf); + + p = hd->buf; +#ifdef BIG_ENDIAN_HOST +#define X(a) do { *p++ = hd->a ; *p++ = hd->a >> 8; \ + *p++ = hd->a >> 16; *p++ = hd->a >> 24; } while(0) +#else /* little endian */ + /*#define X(a) do { *(u32*)p = hd->##a ; p += 4; } while(0)*/ + /* Unixware's cpp doesn't like the above construct so we do it his way: + * (reported by Allan Clark) */ +#define X(a) do { *(u32*)p = (*hd).a ; p += 4; } while(0) +#endif + X(A); + X(B); + X(C); + X(D); +#undef X + hd->finalized = 1; +} + +static void +md5_final(unsigned char *digest, MD5_CONTEXT *ctx) +{ + if (!ctx->finalized) + do_final(ctx); + memcpy(digest, ctx->buf, 16); +} + +/* + * Creates a MD5 digest in hex fomrat (lowercase letters) from the + * string S. hextdigest but be buffer of at lease 33 bytes! + */ +static void +md5_hex_digest(char *hexdigest, const unsigned char *s) +{ + int i; + MD5_CONTEXT context; + unsigned char digest[16]; + + md5_init(&context); + md5_update(&context, s, strlen((gchar *) s)); + md5_final(digest, &context); + + for (i = 0; i < 16; i++) + sprintf(hexdigest + 2 * i, "%02x", digest[i]); +} + + +/* +** Function: md5_hmac +** taken from the file rfc2104.txt +** written by Martin Schaaf <mascha@ma-scha.de> +*/ +static void +md5_hmac(unsigned char *digest, + const unsigned char* text, int text_len, + const unsigned char* key, int key_len) +{ + MD5_CONTEXT context; + unsigned char k_ipad[64]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[64]; /* outer padding - + * key XORd with opad + */ + /* unsigned char tk[16]; */ + int i; + + /* start out by storing key in pads */ + memset(k_ipad, 0, sizeof k_ipad); + memset(k_opad, 0, sizeof k_opad); + if (key_len > 64) { + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + MD5_CONTEXT tctx; + + md5_init(&tctx); + md5_update(&tctx, key, key_len); + md5_final(k_ipad, &tctx); + md5_final(k_opad, &tctx); + } else { + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + + /* XOR key with ipad and opad values */ + for (i = 0; i < 64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* + * perform inner MD5 + */ + md5_init(&context); /* init context for 1st + * pass */ + md5_update(&context, k_ipad, 64); /* start with inner pad */ + md5_update(&context, text, text_len); /* then text of datagram */ + md5_final(digest, &context); /* finish up 1st pass */ + /* + * perform outer MD5 + */ + md5_init(&context); /* init context for 2nd + * pass */ + md5_update(&context, k_opad, 64); /* start with outer pad */ + md5_update(&context, digest, 16); /* then results of 1st + * hash */ + md5_final(digest, &context); /* finish up 2nd pass */ +} + + +static void +md5_hex_hmac(char *hexdigest, + const unsigned char* text, int text_len, + const unsigned char* key, int key_len) +{ + unsigned char digest[16]; + int i; + + md5_hmac(digest, text, text_len, key, key_len); + for (i = 0; i < 16; i++) + sprintf(hexdigest + 2 * i, "%02x", digest[i]); +} + +void caldav_md5_hex_digest(char *hexdigest, const unsigned char *s) { + md5_hex_digest(hexdigest, s); +} + +void caldav_md5_hex_hmac(char *hexdigest, + const unsigned char* text, int text_len, + const unsigned char* key, int key_len) { + md5_hex_hmac(hexdigest, text, text_len, key, key_len); +} diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..7605214 --- /dev/null +++ b/src/md5.h @@ -0,0 +1,52 @@ +/** + * md5.h - MD5 Message-Digest Algorithm + * Copyright (C) 1995, 1996, 1998, 1999 Free Software Foundation, Inc. + * + * according to the definition of MD5 in RFC 1321 from April 1992. + * NOTE: This is *not* the same file as the one from glibc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 3, or (at your option) any + * later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _MD5_HDR_ +#define _MD5_HDR_ + +#include <glib.h> + +/** + * @typedef u32 + * Kept this typedef for compatibility reasons + */ +#ifndef HAVE_U32_TYPEDEF + #undef u32 + typedef guint32 u32; + #define HAVE_U32_TYPEDEF +#endif + +typedef struct { /* Hmm, should be private */ + u32 A,B,C,D; + u32 nblocks; + unsigned char buf[64]; + int count; + int finalized; +} MD5_CONTEXT; + +void caldav_md5_hex_digest(char *hexdigest, const unsigned char *s); + +void caldav_md5_hex_hmac(char *hexdigest, + const unsigned char* text, int text_len, + const unsigned char* key, int key_len); + +#endif /* _MD5_HDR_ */ + diff --git a/src/modify-caldav-object.c b/src/modify-caldav-object.c new file mode 100644 index 0000000..fb3a4d0 --- /dev/null +++ b/src/modify-caldav-object.c @@ -0,0 +1,555 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "modify-caldav-object.h" +#include "lock-caldav-object.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * A static literal string containing the first part of the calendar query. + * The actual UID to use for the query is added at runtime. + */ +static char* search_head = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>" +" <D:getetag/>" +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VEVENT\">" +" <C:prop-filter name=\"UID\">"; + +/** + * A static literal string containing the first part of the calendar query. + * The actual UID to use for the query is added at runtime. + */ +static char* search_tasks_head = +"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +"<C:calendar-query xmlns:D=\"DAV:\"" +" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +" <D:prop>" +" <D:getetag/>" +" <C:calendar-data/>" +" </D:prop>" +" <C:filter>" +" <C:comp-filter name=\"VCALENDAR\">" +" <C:comp-filter name=\"VTODO\">" +" <C:prop-filter name=\"UID\">"; + +/** + * A static literal string containing the last part of the calendar query + */ +static char* search_tail = +"</C:prop-filter>" +" </C:comp-filter>" +" </C:comp-filter>" +" </C:filter>" +"</C:calendar-query>"; + +/** + * Function for modifying an event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_modify(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gchar* search; + gchar* uid; + gboolean result = FALSE; + gboolean LOCKSUPPORT = FALSE; + gchar* lock_token = NULL; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 1"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + gchar* file = g_strdup(settings->file); + if ((uid = get_response_header("uid", file, FALSE)) == NULL) { + g_free(file); + error->code = 1; + error->str = g_strdup("Error: Missing required UID for object"); + return TRUE; + } + g_free(file); + /* + * collation is not supported by ICalendar. + * <C:text-match collation=\"i;ascii-casemap\">%s</C:text-match> + */ + search = g_strdup_printf( + "%s\r\n<C:text-match>%s</C:text-match>\r\n%s", + search_head, uid, search_tail); + g_free(uid); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, search); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(search)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + curl_slist_free_all(http_header); + http_header = NULL; + g_free(search); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 207) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + else { + /* enable uploading */ + gchar* url = NULL; + gchar* etag = NULL; + url = get_url(chunk.memory); + if (url) { + etag = get_etag(chunk.memory); + if (etag) { + gchar* host = get_host(settings->url); + if (host) { + file = g_strdup(url); + g_free(url); + url = g_strdup_printf("%s%s", host, file); + g_free(file); + g_free(host); + } + else { + g_free(etag); + g_free(url); + url = NULL; + } + } + else { + g_free(url); + url = NULL; + } + if (url) { + int lock = 0; + caldav_error lock_error; + + file = g_strdup(etag); + g_free(etag); + etag = g_strdup_printf("If-Match: %s", file); + g_free(file); + http_header = curl_slist_append(http_header, etag); + g_free(etag); + http_header = curl_slist_append(http_header, + "Content-Type: text/calendar; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append( + http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + if (settings->use_locking) + LOCKSUPPORT = caldav_lock_support(settings, &lock_error); + else + LOCKSUPPORT = FALSE; + if (LOCKSUPPORT) { + lock_token = caldav_lock_object(url, settings, &lock_error); + if (lock_token) { + http_header = curl_slist_append( + http_header, g_strdup_printf( + "If: (%s)", lock_token)); + } + /* + * If error code is 423 (Resource is LOCKED) bail out + */ + else if (lock_error.code == 423) { + lock = -1; + } + /* + * If error code is 501 (Not implemented) we continue + * hoping for the best. + */ + else if (lock_error.code == 501) { + lock_token = g_strdup(""); + } + else { + lock = -1; + } + } + if (! LOCKSUPPORT || (LOCKSUPPORT && lock_token && lock_error.code != 423)) { + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + curl_easy_setopt(curl, CURLOPT_URL, rebuild_url(settings, url)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, settings->file); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, + strlen(settings->file)); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + res = curl_easy_perform(curl); + if (LOCKSUPPORT && lock_token) { + caldav_unlock_object( + lock_token, url, settings, &lock_error); + } + } + g_free(url); + g_free(lock_token); + if (res != 0 || lock < 0) { + /* Is this a lock_error don't change error*/ + if (lock == 0 || lock_error.code == 423) { + error->code = code; + error->str = g_strdup(chunk.memory); + } + else { + error->code = lock_error.code; + error->str = g_strdup(lock_error.str); + } + result = TRUE; + g_free(settings->file); + settings->file = NULL; + } + else { + long code; + res = curl_easy_getinfo( + curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 204) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + } + curl_slist_free_all(http_header); + } + else { + error->code = code; + if (chunk.memory) + error->str = g_strdup(chunk.memory); + else + error->str = g_strdup("No object found"); + result = TRUE; + } + } + else { + /* + * No object found on server. Posible synchronization + * problem or a server side race condition + */ + error->code = 409; + error->str = g_strdup("No object found"); + result = TRUE; + } + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_cleanup(curl); + return result; +} + +/** + * Function for modifying a task. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_tasks_modify(caldav_settings* settings, caldav_error* error) { + CURL* curl; + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct config_data data; + struct MemoryStruct chunk; + struct MemoryStruct headers; + struct curl_slist *http_header = NULL; + gchar* search; + gchar* uid; + gboolean result = FALSE; + gboolean LOCKSUPPORT = FALSE; + gchar* lock_token = NULL; + + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + curl = get_curl(settings); + if (!curl) { + error->code = -1; + error->str = g_strdup("Could not initialize libcurl"); + g_free(settings->file); + settings->file = NULL; + return TRUE; + } + + http_header = curl_slist_append(http_header, + "Content-Type: application/xml; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Depth: 1"); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append(http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + data.trace_ascii = settings->trace_ascii; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'headers' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + if (settings->debug) { + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &data); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + } + gchar* file = g_strdup(settings->file); + if ((uid = get_response_header("uid", file, FALSE)) == NULL) { + g_free(file); + error->code = 1; + error->str = g_strdup("Error: Missing required UID for object"); + return TRUE; + } + g_free(file); + /* + * collation is not supported by ICalendar. + * <C:text-match collation=\"i;ascii-casemap\">%s</C:text-match> + */ + search = g_strdup_printf( + "%s\r\n<C:text-match>%s</C:text-match>\r\n%s", + search_tasks_head, uid, search_tail); + g_free(uid); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, search); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, strlen(search)); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + curl_slist_free_all(http_header); + http_header = NULL; + g_free(search); + if (res != 0) { + error->code = -1; + error->str = g_strdup_printf("%s", error_buf); + g_free(settings->file); + settings->file = NULL; + result = TRUE; + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 207) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + else { + /* enable uploading */ + gchar* url = NULL; + gchar* etag = NULL; + url = get_url(chunk.memory); + if (url) { + etag = get_etag(chunk.memory); + if (etag) { + gchar* host = get_host(settings->url); + if (host) { + file = g_strdup(url); + g_free(url); + url = g_strdup_printf("%s%s", host, file); + g_free(file); + g_free(host); + } + else { + g_free(etag); + g_free(url); + url = NULL; + } + } + else { + g_free(url); + url = NULL; + } + if (url) { + int lock = 0; + caldav_error lock_error; + + file = g_strdup(etag); + g_free(etag); + etag = g_strdup_printf("If-Match: %s", file); + g_free(file); + http_header = curl_slist_append(http_header, etag); + g_free(etag); + http_header = curl_slist_append(http_header, + "Content-Type: text/calendar; charset=\"utf-8\""); + http_header = curl_slist_append(http_header, "Expect:"); + http_header = curl_slist_append( + http_header, "Transfer-Encoding:"); + http_header = curl_slist_append(http_header, "Connection: close"); + if (settings->use_locking) + LOCKSUPPORT = caldav_lock_support(settings, &lock_error); + else + LOCKSUPPORT = FALSE; + if (LOCKSUPPORT) { + lock_token = caldav_lock_object(url, settings, &lock_error); + if (lock_token) { + http_header = curl_slist_append( + http_header, g_strdup_printf( + "If: (%s)", lock_token)); + } + /* + * If error code is 423 (Resource is LOCKED) bail out + */ + else if (lock_error.code == 423) { + lock = -1; + } + /* + * If error code is 501 (Not implemented) we continue + * hoping for the best. + */ + else if (lock_error.code == 501) { + lock_token = g_strdup(""); + } + else { + lock = -1; + } + } + if (! LOCKSUPPORT || (LOCKSUPPORT && lock_token && lock_error.code != 423)) { + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header); + curl_easy_setopt(curl, CURLOPT_URL, rebuild_url(settings, url)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, settings->file); + curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, + strlen(settings->file)); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + res = curl_easy_perform(curl); + if (LOCKSUPPORT && lock_token) { + caldav_unlock_object( + lock_token, url, settings, &lock_error); + } + } + g_free(url); + g_free(lock_token); + if (res != 0 || lock < 0) { + /* Is this a lock_error don't change error*/ + if (lock == 0 || lock_error.code == 423) { + error->code = code; + error->str = g_strdup(chunk.memory); + } + else { + error->code = lock_error.code; + error->str = g_strdup(lock_error.str); + } + result = TRUE; + g_free(settings->file); + settings->file = NULL; + } + else { + long code; + res = curl_easy_getinfo( + curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 204) { + error->code = code; + error->str = g_strdup(chunk.memory); + result = TRUE; + } + } + curl_slist_free_all(http_header); + } + else { + error->code = code; + if (chunk.memory) + error->str = g_strdup(chunk.memory); + else + error->str = g_strdup("No object found"); + result = TRUE; + } + } + else { + /* + * No object found on server. Posible synchronization + * problem or a server side race condition + */ + error->code = 409; + error->str = g_strdup("No object found"); + result = TRUE; + } + } + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_cleanup(curl); + return result; +} + diff --git a/src/modify-caldav-object.h b/src/modify-caldav-object.h new file mode 100644 index 0000000..4171f91 --- /dev/null +++ b/src/modify-caldav-object.h @@ -0,0 +1,42 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MODIFY_CALDAV_OBJECT_H__ +#define __MODIFY_CALDAV_OBJECT_H__ + +#include "caldav-utils.h" +#include "caldav.h" + +/** + * Function for modifying an event. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_modify(caldav_settings* settings, caldav_error* error); + +/** + * Function for modifying a task. + * @param settings A pointer to caldav_settings. @see caldav_settings + * @param error A pointer to caldav_error. @see caldav_error + * @return TRUE in case of error, FALSE otherwise. + */ +gboolean caldav_tasks_modify(caldav_settings* settings, caldav_error* error); + +#endif diff --git a/src/options-caldav-server.c b/src/options-caldav-server.c new file mode 100644 index 0000000..1d97d04 --- /dev/null +++ b/src/options-caldav-server.c @@ -0,0 +1,135 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "options-caldav-server.h" +#include <glib.h> +#include <curl/curl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * Function for getting supported options from a server. + * @param curl A pointer to an initialized CURL instance + * @param settings struct containing the URL to the server. If authentication + * is required prior to making the call the credentials must be available + * via CURLOPT_USERPWD before calling. + * @param result A pointer to a struct _response. If test is true + * this variable can be NULL. Caller is responsible for freeing associated + * memory. + * @param error A pointer to caldav_error. @see caldav_error + * @param test if this is true response will be whether the server + * represented by the URL is a CalDAV collection or not. + * @return FALSE in case of error, TRUE otherwise. + */ +gboolean caldav_getoptions(CURL* curl, caldav_settings* settings, response* result, + caldav_error* error, gboolean test) { + CURLcode res = 0; + char error_buf[CURL_ERROR_SIZE]; + struct MemoryStruct chunk; + struct MemoryStruct headers; + gboolean enabled = FALSE; + + if (! curl) + return FALSE; + + if (!error) { + error = (caldav_error *) malloc(sizeof(struct _caldav_error)); + memset(error, '\0', sizeof(struct _caldav_error)); + } + chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + headers.memory = NULL; + headers.size = 0; + + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "OPTIONS"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + res = curl_easy_perform(curl); + if (res == 0) { + gchar* head; + head = get_response_header("DAV", headers.memory, TRUE); + if (head && strstr(head, "calendar-access") != NULL) { + enabled = TRUE; + if (! test) { + result->msg = g_strdup( + get_response_header("Allow", headers.memory, FALSE)); + } + } + else { + long code; + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code == 200) { + error->code = -1; + error->str = g_strdup("URL is not a CalDAV resource"); + } + else { + error->code = -1 * code; + error->str = g_strdup(headers.memory); + } + } + g_free(head); + } + else if ( + (res == CURLE_SSL_CONNECT_ERROR || + CURLE_PEER_FAILED_VERIFICATION || + CURLE_SSL_ENGINE_NOTFOUND || + CURLE_SSL_ENGINE_SETFAILED || + CURLE_SSL_CERTPROBLEM || + CURLE_SSL_CIPHER || + CURLE_SSL_CACERT || + CURLE_SSL_CACERT_BADFILE || + CURLE_SSL_CRL_BADFILE || + CURLE_SSL_ISSUER_ERROR) && settings->usehttps) { + error->code = -2; + error->str = g_strdup(error_buf); + } + else if (res == CURLE_COULDNT_RESOLVE_HOST) { + error->code = -3; + error->str = g_strdup("Could not resolve host"); + } + else if (res == CURLE_COULDNT_CONNECT) { + error->code = -4; + error->str = g_strdup("Unable to connect"); + } + else { + error->code = -1; + error->str = g_strdup("URL is not a CalDAV resource"); + } + if (chunk.memory) + free(chunk.memory); + if (headers.memory) + free(headers.memory); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + return enabled; +} diff --git a/src/options-caldav-server.h b/src/options-caldav-server.h new file mode 100644 index 0000000..d17cba2 --- /dev/null +++ b/src/options-caldav-server.h @@ -0,0 +1,43 @@ +/* vim: set textwidth=80 tabstop=4: */ + +/* Copyright (c) 2008 Michael Rasmussen (mir@datanom.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __OPTIONS_CALDAV_SERVER_H__ +#define __OPTIONS_CALDAV_SERVER_H__ + +#include "caldav-utils.h" +#include "caldav.h" + +/** + * Function for getting supported options from a server. + * @param curl A pointer to an initialized CURL instance + * @param settings struct containing the URL to the server. If authentication + * is required prior to making the call the credentials must be available + * via CURLOPT_USERPWD before calling. + * @param result A pointer to a struct _response. If test is true + * this variable can be NULL. Caller is responsible for freeing associated + * memory. + * @param error A pointer to caldav_error. @see caldav_error + * @param test if this is true response will be whether the server + * represented by the URL is a CalDAV collection or not. + * @return FALSE in case of error, TRUE otherwise. + */ +gboolean caldav_getoptions(CURL* curl, caldav_settings* settings, response* result, + caldav_error* error, gboolean test); + +#endif |