evolution-mapi r2 - in trunk: . po src src/account-setup-eplugin src/addressbook src/calendar src/camel src/libexchangemapi
- From: jjohnny svn gnome org
- To: svn-commits-list gnome org
- Subject: evolution-mapi r2 - in trunk: . po src src/account-setup-eplugin src/addressbook src/calendar src/camel src/libexchangemapi
- Date: Wed, 19 Nov 2008 04:28:20 +0000 (UTC)
Author: jjohnny
Date: Wed Nov 19 04:28:20 2008
New Revision: 2
URL: http://svn.gnome.org/viewvc/evolution-mapi?rev=2&view=rev
Log:
Initial checkin from evolution and evolution-data-server's EXCHANGE_MAPI_BRANCH
Added:
trunk/AUTHORS
trunk/COPYING
trunk/COPYING.LGPL3
trunk/ChangeLog
trunk/INSTALL
trunk/MAINTAINERS
trunk/Makefile.am
trunk/NEWS
trunk/README
trunk/autogen.sh (contents, props changed)
trunk/configure.in
trunk/eplugin-rule.mk
trunk/po/
trunk/po/ChangeLog
trunk/po/Makefile.in.in
trunk/po/POTFILES.in
trunk/src/
trunk/src/Makefile.am
trunk/src/account-setup-eplugin/
trunk/src/account-setup-eplugin/ChangeLog
trunk/src/account-setup-eplugin/Makefile.am
trunk/src/account-setup-eplugin/exchange-mapi-account-listener.c
trunk/src/account-setup-eplugin/exchange-mapi-account-listener.h
trunk/src/account-setup-eplugin/exchange-mapi-account-setup.c
trunk/src/account-setup-eplugin/exchange-mapi-account-setup.h
trunk/src/account-setup-eplugin/org-gnome-exchange-mapi.eplug.xml
trunk/src/addressbook/
trunk/src/addressbook/ChangeLog
trunk/src/addressbook/Makefile.am
trunk/src/addressbook/e-book-backend-mapi-factory.c
trunk/src/addressbook/e-book-backend-mapi.c
trunk/src/addressbook/e-book-backend-mapi.h
trunk/src/calendar/
trunk/src/calendar/ChangeLog
trunk/src/calendar/Makefile.am
trunk/src/calendar/e-cal-backend-mapi-factory.c
trunk/src/calendar/e-cal-backend-mapi-factory.h
trunk/src/calendar/e-cal-backend-mapi.c
trunk/src/calendar/e-cal-backend-mapi.h
trunk/src/camel/
trunk/src/camel/ChangeLog
trunk/src/camel/Makefile.am
trunk/src/camel/camel-mapi-folder.c
trunk/src/camel/camel-mapi-folder.h
trunk/src/camel/camel-mapi-private.h
trunk/src/camel/camel-mapi-provider.c
trunk/src/camel/camel-mapi-store-summary.c
trunk/src/camel/camel-mapi-store-summary.h
trunk/src/camel/camel-mapi-store.c
trunk/src/camel/camel-mapi-store.h
trunk/src/camel/camel-mapi-summary.c
trunk/src/camel/camel-mapi-summary.h
trunk/src/camel/camel-mapi-transport.c
trunk/src/camel/camel-mapi-transport.h
trunk/src/camel/camel-private.h
trunk/src/camel/libcamelmapi.urls
trunk/src/libexchangemapi/
trunk/src/libexchangemapi/ChangeLog
trunk/src/libexchangemapi/Makefile.am
trunk/src/libexchangemapi/exchange-mapi-cal-recur-utils.c
trunk/src/libexchangemapi/exchange-mapi-cal-recur-utils.h
trunk/src/libexchangemapi/exchange-mapi-cal-tz-utils.c
trunk/src/libexchangemapi/exchange-mapi-cal-tz-utils.h
trunk/src/libexchangemapi/exchange-mapi-cal-utils.c
trunk/src/libexchangemapi/exchange-mapi-cal-utils.h
trunk/src/libexchangemapi/exchange-mapi-connection.c
trunk/src/libexchangemapi/exchange-mapi-connection.h
trunk/src/libexchangemapi/exchange-mapi-defs.h
trunk/src/libexchangemapi/exchange-mapi-folder.c
trunk/src/libexchangemapi/exchange-mapi-folder.h
trunk/src/libexchangemapi/exchange-mapi-utils.c
trunk/src/libexchangemapi/exchange-mapi-utils.h
trunk/src/libexchangemapi/libexchangemapi.pc.in
trunk/src/libexchangemapi/tz-ical-to-mapi
trunk/src/libexchangemapi/tz-mapi-to-ical
Added: trunk/AUTHORS
==============================================================================
--- (empty file)
+++ trunk/AUTHORS Wed Nov 19 04:28:20 2008
@@ -0,0 +1,5 @@
+AUTHORS
+-------
+Johnny Jacob <jjohnny novell com>
+Srinivasa Ragavan <sragavan novell com>
+Suman Manjunath <msuman novell com>
\ No newline at end of file
Added: trunk/COPYING
==============================================================================
--- (empty file)
+++ trunk/COPYING Wed Nov 19 04:28:20 2008
@@ -0,0 +1 @@
+placeholder
\ No newline at end of file
Added: trunk/COPYING.LGPL3
==============================================================================
--- (empty file)
+++ trunk/COPYING.LGPL3 Wed Nov 19 04:28:20 2008
@@ -0,0 +1,166 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
Added: trunk/INSTALL
==============================================================================
--- (empty file)
+++ trunk/INSTALL Wed Nov 19 04:28:20 2008
@@ -0,0 +1 @@
+http://www.go-evolution.org/MAPIProvider
Added: trunk/MAINTAINERS
==============================================================================
--- (empty file)
+++ trunk/MAINTAINERS Wed Nov 19 04:28:20 2008
@@ -0,0 +1,4 @@
+
+Johnny Jacob
+E-mail: jjohnny novell com
+Userid: jjohnny
Added: trunk/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/Makefile.am Wed Nov 19 04:28:20 2008
@@ -0,0 +1,18 @@
+AUTOMAKE_OPTIONS = 1.6
+
+SUBDIRS = src po
+DIST_SUBDIRS= src po
+
+changelogs = \
+ ChangeLog
+
+EXTRA_DIST = \
+ $(changelog) \
+ eplugin-rule.mk \
+ intltool-merge.in \
+ intltool-update.in \
+ intltool-extract.in
+
+CLEANFILES = intltool-merge \
+ intltool-update \
+ intltool-extract
Added: trunk/NEWS
==============================================================================
Added: trunk/README
==============================================================================
--- (empty file)
+++ trunk/README Wed Nov 19 04:28:20 2008
@@ -0,0 +1 @@
+http://www.go-evolution.org/MAPIProvider
Added: trunk/autogen.sh
==============================================================================
--- (empty file)
+++ trunk/autogen.sh Wed Nov 19 04:28:20 2008
@@ -0,0 +1,23 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+PKG_NAME="evolution-mapi"
+REQUIRED_AUTOMAKE_VERSION=1.6
+
+(test -f $srcdir/configure.in \
+ && test -f $srcdir/ChangeLog \
+ && test -d $srcdir/src/account-setup-eplugin) || {
+ echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+ echo " top-level $PKG_NAME directory"
+ exit 1
+}
+
+which gnome-autogen.sh || {
+ echo "You need to install gnome-common from the GNOME SVN"
+ exit 1
+}
+USE_GNOME2_MACROS=1 . gnome-autogen.sh
+
Added: trunk/configure.in
==============================================================================
--- (empty file)
+++ trunk/configure.in Wed Nov 19 04:28:20 2008
@@ -0,0 +1,147 @@
+# Simple configuration script for openchange evolution plugin
+# Written by Julien Kerihuel <j kerihuel openchange org>
+# Modified for GNOME Evolution MAPI Provider by Johnny Jacob <jjohnny novell com>
+
+AC_PREREQ(2.57)
+AC_INIT(evolution-mapi, 0.1, http://go-evolution.org/MAPIProvider, evolution-mapi)
+AC_CONFIG_SRCDIR(README)
+
+AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
+
+AC_CONFIG_HEADER([config.h])
+AC_DEFINE(_GNU_SOURCE, 1, [Use GNU extensions])
+AC_PROG_CC
+
+AC_PROG_INTLTOOL([0.35.5])
+
+dnl I18N stuff
+AM_GLIB_GNU_GETTEXT
+
+GETTEXT_PACKAGE=evolution-mapi
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [Package name for gettext])
+
+localedir='$(prefix)/$(DATADIRNAME)/locale'
+AC_SUBST(localedir)
+
+dnl Initialize libtool
+AC_PROG_LIBTOOL
+
+PKG_PROG_PKG_CONFIG
+
+dnl ****************************
+dnl Check for evolution plugins
+dnl ****************************
+
+AC_ARG_WITH(evolution, [ --evolution=[version] Use Evolution version.],
+ evolution_version="$withval", evolution_version="2.12")
+
+AC_ARG_WITH(evolution-camelprovider, [ --evolution-camel=[version] Use Evolution Camel Provider version.],
+ evolution_camel_version="$withval", evolution_camel_version="1.2" )
+
+PKG_CHECK_MODULES(EVOLUTION_DATA_SERVER, evolution-data-server-${evolution_camel_version})
+PKG_CHECK_MODULES(EVOLUTION_PLUGIN, evolution-plugin)
+
+PKG_CHECK_MODULES(LIBEDATASERVER, libedataserver-${evolution_camel_version})
+PKG_CHECK_MODULES(LIBEBACKEND, libebackend-${evolution_camel_version})
+
+PKG_CHECK_MODULES(LIBECAL, libecal-${evolution_camel_version})
+PKG_CHECK_MODULES(LIBEDATACAL, libedata-cal-${evolution_camel_version})
+
+PKG_CHECK_MODULES(LIBBOOK, libebook-${evolution_camel_version})
+PKG_CHECK_MODULES(LIBEDATABOOK, libedata-book-${evolution_camel_version})
+
+PKG_CHECK_MODULES(CAMEL, camel-provider-${evolution_camel_version})
+
+dnl TODO : Version check.
+PKG_CHECK_MODULES(LIBMAPI, libmapi)
+PKG_CHECK_MODULES(GCONF2, gconf-2.0)
+
+AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
+AM_GCONF_SOURCE_2
+
+API_VERSION=${evolution_camel_version}
+AC_SUBST(API_VERSION)
+
+BASE_VERSION=`pkg-config --variable=execversion evolution-shell`
+AC_SUBST(BASE_VERSION)
+
+GETTEXT_PACKAGE=evolution
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Package Name for Gettext])
+
+dnl Add evolution plugin rules here
+EVO_PLUGIN_RULE=$srcdir/eplugin-rule.mk
+AC_SUBST_FILE(EVO_PLUGIN_RULE)
+
+EVOLUTION_PLUGIN="evolution-plugin"
+AC_SUBST(EVOLUTION_PLUGIN)
+
+EVOLUTION_CAMEL="evolution-camel"
+AC_SUBST(EVOLUTION_CAMEL)
+
+EVOLUTION_CALENDAR="evolution-calendar"
+AC_SUBST(EVOLUTION_CALENDAR)
+
+EVOLUTION_EBOOK="evolution-ebook"
+AC_SUBST(EVOLUTION_EBOOK)
+
+EVOLUTION_PLUGIN_INSTALL="evolution-plugin-install"
+AC_SUBST(EVOLUTION_PLUGIN_INSTALL)
+
+EVOLUTION_CAMEL_INSTALL="evolution-camel-install"
+AC_SUBST(EVOLUTION_CAMEL_INSTALL)
+
+EVOLUTION_CALENDAR_INSTALL="evolution-calendar-install"
+AC_SUBST(EVOLUTION_CALENDAR_INSTALL)
+
+EVOLUTION_EBOOK_INSTALL="evolution-ebook-install"
+AC_SUBST(EVOLUTION_EBOOK_INSTALL)
+
+EVOLUTION_PLUGIN_UNINSTALL="evolution-plugin-uninstall"
+AC_SUBST(EVOLUTION_PLUGIN_UNINSTALL)
+
+EVOLUTION_CAMEL_UNINSTALL="evolution-camel-uninstall"
+AC_SUBST(EVOLUTION_CAMEL_UNINSTALL)
+
+EVOLUTION_CALENDAR_UNINSTALL="evolution-calendar-uninstall"
+AC_SUBST(EVOLUTION_CALENDAR_UNINSTALL)
+
+EVOLUTION_EBOOK_UNINSTALL="evolution-ebook-uninstall"
+AC_SUBST(EVOLUTION_EBOOK_UNINSTALL)
+
+plugindir=`$PKG_CONFIG --variable=plugindir evolution-plugin`
+AC_SUBST(plugindir)
+
+camel_providerdir=`$PKG_CONFIG --variable=camel_providerdir camel-provider-${evolution_camel_version}`
+AC_SUBST(camel_providerdir)
+
+extensiondir=`$PKG_CONFIG --variable=extensiondir evolution-data-server-${evolution_camel_version}`
+AC_SUBST(extensiondir)
+
+libmapi_ldif_dir=`$PKG_CONFIG --variable=libdir libmapi`/openchange/setup
+AC_SUBST(libmapi_ldif_dir)
+
+eds_privdatadir=`$PKG_CONFIG --variable=privdatadir evolution-data-server-${evolution_camel_version}`
+AC_SUBST(eds_privdatadir)
+
+edataserver_privincludedir=`$PKG_CONFIG --variable=privincludedir libedataserver-${evolution_camel_version}`
+AC_SUBST(edataserver_privincludedir)
+
+mapidatadir="$eds_privdatadir/mapi"
+AC_SUBST(mapidatadir)
+
+dnl ***********************
+dnl Makefiles
+dnl ***********************
+
+AC_CONFIG_FILES([Makefile
+src/Makefile
+src/libexchangemapi/Makefile
+src/calendar/Makefile
+src/addressbook/Makefile
+src/camel/Makefile
+src/account-setup-eplugin/Makefile
+po/Makefile.in
+])
+AC_OUTPUT
\ No newline at end of file
Added: trunk/eplugin-rule.mk
==============================================================================
--- (empty file)
+++ trunk/eplugin-rule.mk Wed Nov 19 04:28:20 2008
@@ -0,0 +1,11 @@
+%.eplug: %.eplug.in
+ sed -e 's|\ PLUGINDIR\@|$(plugindir)|' \
+ -e 's|\ SOEXT\@|$(SOEXT)|' \
+ -e 's|\ GETTEXT_PACKAGE\@|$(GETTEXT_PACKAGE)|' \
+ -e 's|\ LOCALEDIR\@|$(localedir)|' $< > $@
+
+%.eplug.in: %.eplug.xml
+ LC_ALL=C $(INTLTOOL_MERGE) -x -u /tmp $< $@
+
+%.error: %.error.xml
+ LC_ALL=C $(INTLTOOL_MERGE) -x -u /tmp $< $@
Added: trunk/po/Makefile.in.in
==============================================================================
--- (empty file)
+++ trunk/po/Makefile.in.in Wed Nov 19 04:28:20 2008
@@ -0,0 +1,217 @@
+# Makefile for program source directory in GNU NLS utilities package.
+# Copyright (C) 1995, 1996, 1997 by Ulrich Drepper <drepper gnu ai mit edu>
+# Copyright (C) 2004-2008 Rodney Dawes <dobey pwns gmail com>
+#
+# This file may be copied and used freely without restrictions. It may
+# be used in projects which are not available under a GNU Public License,
+# but which still want to provide support for the GNU gettext functionality.
+#
+# - Modified by Owen Taylor <otaylor redhat com> to use GETTEXT_PACKAGE
+# instead of PACKAGE and to look for po2tbl in ./ not in intl/
+#
+# - Modified by jacob berkman <jacob ximian com> to install
+# Makefile.in.in and po2tbl.sed.in for use with glib-gettextize
+#
+# - Modified by Rodney Dawes <dobey pwns gmail com> for use with intltool
+#
+# We have the following line for use by intltoolize:
+# INTLTOOL_MAKEFILE
+
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datadir = @datadir@
+datarootdir = @datarootdir@
+libdir = @libdir@
+DATADIRNAME = @DATADIRNAME@
+itlocaledir = $(prefix)/$(DATADIRNAME)/locale
+subdir = po
+install_sh = @install_sh@
+# Automake >= 1.8 provides @mkdir_p
+# Until it can be supposed, use the safe fallback:
+mkdir_p = $(install_sh) -d
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+
+GMSGFMT = @GMSGFMT@
+MSGFMT = @MSGFMT@
+XGETTEXT = @XGETTEXT@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+MSGMERGE = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) srcdir=$(srcdir) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --dist
+GENPOT = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) srcdir=$(srcdir) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --pot
+
+ALL_LINGUAS = @ALL_LINGUAS@
+
+PO_LINGUAS=$(shell if test -r $(srcdir)/LINGUAS; then grep -v "^\#" $(srcdir)/LINGUAS; else echo "$(ALL_LINGUAS)"; fi)
+
+USER_LINGUAS=$(shell if test -n "$(LINGUAS)"; then LLINGUAS="$(LINGUAS)"; ALINGUAS="$(ALL_LINGUAS)"; for lang in $$LLINGUAS; do if test -n "`grep ^$$lang$$ $(srcdir)/LINGUAS 2>/dev/null`" -o -n "`echo $$ALINGUAS|tr ' ' '\n'|grep ^$$lang$$`"; then printf "$$lang "; fi; done; fi)
+
+USE_LINGUAS=$(shell if test -n "$(USER_LINGUAS)" -o -n "$(LINGUAS)"; then LLINGUAS="$(USER_LINGUAS)"; else if test -n "$(PO_LINGUAS)"; then LLINGUAS="$(PO_LINGUAS)"; else LLINGUAS="$(ALL_LINGUAS)"; fi; fi; for lang in $$LLINGUAS; do printf "$$lang "; done)
+
+POFILES=$(shell LINGUAS="$(PO_LINGUAS)"; for lang in $$LINGUAS; do printf "$$lang.po "; done)
+
+DISTFILES = Makefile.in.in POTFILES.in $(POFILES)
+EXTRA_DISTFILES = ChangeLog POTFILES.skip Makevars LINGUAS
+
+POTFILES = \
+# This comment gets stripped out
+
+CATALOGS=$(shell LINGUAS="$(USE_LINGUAS)"; for lang in $$LINGUAS; do printf "$$lang.gmo "; done)
+
+.SUFFIXES:
+.SUFFIXES: .po .pox .gmo .mo .msg .cat
+
+.po.pox:
+ $(MAKE) $(GETTEXT_PACKAGE).pot
+ $(MSGMERGE) $< $(GETTEXT_PACKAGE).pot -o $*.pox
+
+.po.mo:
+ $(MSGFMT) -o $@ $<
+
+.po.gmo:
+ file=`echo $* | sed 's,.*/,,'`.gmo \
+ && rm -f $$file && $(GMSGFMT) -o $$file $<
+
+.po.cat:
+ sed -f ../intl/po2msg.sed < $< > $*.msg \
+ && rm -f $@ && gencat $@ $*.msg
+
+
+all: all- USE_NLS@
+
+all-yes: $(CATALOGS)
+all-no:
+
+$(GETTEXT_PACKAGE).pot: $(POTFILES)
+ $(GENPOT)
+
+install: install-data
+install-data: install-data- USE_NLS@
+install-data-no: all
+install-data-yes: all
+ linguas="$(USE_LINGUAS)"; \
+ for lang in $$linguas; do \
+ dir=$(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES; \
+ $(mkdir_p) $$dir; \
+ if test -r $$lang.gmo; then \
+ $(INSTALL_DATA) $$lang.gmo $$dir/$(GETTEXT_PACKAGE).mo; \
+ echo "installing $$lang.gmo as $$dir/$(GETTEXT_PACKAGE).mo"; \
+ else \
+ $(INSTALL_DATA) $(srcdir)/$$lang.gmo $$dir/$(GETTEXT_PACKAGE).mo; \
+ echo "installing $(srcdir)/$$lang.gmo as" \
+ "$$dir/$(GETTEXT_PACKAGE).mo"; \
+ fi; \
+ if test -r $$lang.gmo.m; then \
+ $(INSTALL_DATA) $$lang.gmo.m $$dir/$(GETTEXT_PACKAGE).mo.m; \
+ echo "installing $$lang.gmo.m as $$dir/$(GETTEXT_PACKAGE).mo.m"; \
+ else \
+ if test -r $(srcdir)/$$lang.gmo.m ; then \
+ $(INSTALL_DATA) $(srcdir)/$$lang.gmo.m \
+ $$dir/$(GETTEXT_PACKAGE).mo.m; \
+ echo "installing $(srcdir)/$$lang.gmo.m as" \
+ "$$dir/$(GETTEXT_PACKAGE).mo.m"; \
+ else \
+ true; \
+ fi; \
+ fi; \
+ done
+
+# Empty stubs to satisfy archaic automake needs
+dvi info tags TAGS ID:
+
+# Define this as empty until I found a useful application.
+install-exec installcheck:
+
+uninstall:
+ linguas="$(USE_LINGUAS)"; \
+ for lang in $$linguas; do \
+ rm -f $(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo; \
+ rm -f $(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo.m; \
+ done
+
+check: all $(GETTEXT_PACKAGE).pot
+ rm -f missing notexist
+ srcdir=$(srcdir) $(INTLTOOL_UPDATE) -m
+ if [ -r missing -o -r notexist ]; then \
+ exit 1; \
+ fi
+
+mostlyclean:
+ rm -f *.pox $(GETTEXT_PACKAGE).pot *.old.po cat-id-tbl.tmp
+ rm -f .intltool-merge-cache
+
+clean: mostlyclean
+
+distclean: clean
+ rm -f Makefile Makefile.in POTFILES stamp-it
+ rm -f *.mo *.msg *.cat *.cat.m *.gmo
+
+maintainer-clean: distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+ rm -f Makefile.in.in
+
+distdir = ../$(PACKAGE)-$(VERSION)/$(subdir)
+dist distdir: $(DISTFILES)
+ dists="$(DISTFILES)"; \
+ extra_dists="$(EXTRA_DISTFILES)"; \
+ for file in $$extra_dists; do \
+ test -f $(srcdir)/$$file && dists="$$dists $(srcdir)/$$file"; \
+ done; \
+ for file in $$dists; do \
+ test -f $$file || file="$(srcdir)/$$file"; \
+ ln $$file $(distdir) 2> /dev/null \
+ || cp -p $$file $(distdir); \
+ done
+
+update-po: Makefile
+ $(MAKE) $(GETTEXT_PACKAGE).pot
+ tmpdir=`pwd`; \
+ linguas="$(USE_LINGUAS)"; \
+ for lang in $$linguas; do \
+ echo "$$lang:"; \
+ result="`$(MSGMERGE) -o $$tmpdir/$$lang.new.po $$lang`"; \
+ if $$result; then \
+ if cmp $(srcdir)/$$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
+ rm -f $$tmpdir/$$lang.new.po; \
+ else \
+ if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \
+ :; \
+ else \
+ echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \
+ rm -f $$tmpdir/$$lang.new.po; \
+ exit 1; \
+ fi; \
+ fi; \
+ else \
+ echo "msgmerge for $$lang.gmo failed!"; \
+ rm -f $$tmpdir/$$lang.new.po; \
+ fi; \
+ done
+
+Makefile POTFILES: stamp-it
+ @if test ! -f $@; then \
+ rm -f stamp-it; \
+ $(MAKE) stamp-it; \
+ fi
+
+stamp-it: Makefile.in.in $(top_builddir)/config.status POTFILES.in
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/Makefile.in CONFIG_HEADERS= CONFIG_LINKS= \
+ $(SHELL) ./config.status
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
Added: trunk/po/POTFILES.in
==============================================================================
--- (empty file)
+++ trunk/po/POTFILES.in Wed Nov 19 04:28:20 2008
@@ -0,0 +1,7 @@
+src/account-setup-eplugin/exchange-account-listener.c
+src/account-setup-eplugin/exchange-mapi-account-setup.c
+src/backends/calendar/e-cal-backend-mapi.c
+src/camel/camel-mapi-folder.c
+src/camel/camel-mapi-provider.c
+src/camel/camel-mapi-store.c
+src/camel/camel-mapi-transport.c
Added: trunk/src/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/Makefile.am Wed Nov 19 04:28:20 2008
@@ -0,0 +1,2 @@
+SUBDIRS=libexchangemapi calendar addressbook camel account-setup-eplugin
+DIST_SUBDIRS=libexchangemapi calendar addressbook camel account-setup-eplugin
\ No newline at end of file
Added: trunk/src/account-setup-eplugin/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/account-setup-eplugin/Makefile.am Wed Nov 19 04:28:20 2008
@@ -0,0 +1,40 @@
+INCLUDES = -I . \
+ -DEVOLUTION_GLADEDIR=\""$(gladedir)"\" \
+ -DCONNECTOR_GLADEDIR=\""$(gladedir)"\" \
+ -DLIBMAPI_LDIF_DIR=\""$(libmapi_ldif_dir)"\" \
+ -I$(top_srcdir)/src/libexchangemapi/ \
+ $(CAMEL_CFLAGS) \
+ $(EVOLUTION_PLUGIN_CFLAGS) \
+ $(EVOLUTION_CALENDAR_CFLAGS) \
+ $(EVOLUTION_ADDRESSBOOK_CFLAGS) \
+ $(LIBMAPI_CFLAGS)
+
+
+ EVO_PLUGIN_RULE@
+
+plugin_DATA = org-gnome-exchange-mapi.eplug
+
+plugin_LTLIBRARIES = liborg-gnome-exchange-mapi.la
+
+liborg_gnome_exchange_mapi_la_SOURCES = \
+ exchange-mapi-account-setup.c \
+ exchange-mapi-account-setup.h \
+ exchange-mapi-account-listener.c \
+ exchange-mapi-account-listener.h
+
+liborg_gnome_exchange_mapi_la_LIBADD = \
+ $(EVOLUTION_CALENDAR_LIBS) \
+ $(EVOLUTION_ADDRESSBOOK_LIBS) \
+ $(EVOLUTION_PLUGIN_LIBS) \
+ $(CAMEL_LIBS) \
+ $(LIBMAPI_LIBS)
+
+liborg_gnome_exchange_mapi_la_LDFLAGS = -module -avoid-version -lmapi $(NO_UNDEFINED)
+liborg_gnome_exchange_mapi_la_CFLAGS = -I/usr/local/samba/include/
+
+EXTRA_DIST = \
+ org-gnome-exchange-mapi.eplug.xml
+
+BUILT_SOURCES = org-gnome-exchange-mapi.eplug
+
+CLEANFILES = $(BUILT_SOURCES)
Added: trunk/src/account-setup-eplugin/exchange-mapi-account-listener.c
==============================================================================
--- (empty file)
+++ trunk/src/account-setup-eplugin/exchange-mapi-account-listener.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,783 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan novell com>
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "exchange-mapi-account-listener.h"
+#include "exchange-mapi-account-setup.h"
+#include <string.h>
+#include <camel/camel-i18n.h>
+#include <libedataserverui/e-passwords.h>
+#include "e-util/e-error.h"
+#include <libedataserver/e-account.h>
+#include <libecal/e-cal.h>
+#include <libedataserver/e-account-list.h>
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-list.h>
+#include <camel/camel-url.h>
+
+#include <libmapi/libmapi.h>
+
+
+/* FIXME: The mapi should not be needed in the include statement.
+LIMBAPI_CFLAGS or something is going wrong */
+
+#include <mapi/exchange-mapi-folder.h>
+#include <mapi/exchange-mapi-connection.h>
+#include <mapi/exchange-mapi-utils.h>
+
+#define d(x) x
+
+struct _ExchangeMAPIAccountListenerPrivate {
+ GConfClient *gconf_client;
+ /* we get notification about mail account changes from this object */
+ EAccountList *account_list;
+};
+
+typedef struct _ExchangeMAPIAccountInfo ExchangeMAPIAccountInfo;
+
+/* stores some info about all currently existing mapi accounts */
+struct _ExchangeMAPIAccountInfo {
+ char *uid;
+ char *name;
+ char *source_url;
+ gboolean enabled;
+};
+
+/* list of ExchangeMAPIAccountInfo structures */
+static GList *mapi_accounts = NULL;
+
+#define PARENT_TYPE G_TYPE_OBJECT
+
+static GObjectClass *parent_class = NULL;
+
+static void
+dispose (GObject *object)
+{
+ ExchangeMAPIAccountListener *config_listener = EXCHANGE_MAPI_ACCOUNT_LISTENER (object);
+
+ g_object_unref (config_listener->priv->gconf_client);
+ g_object_unref (config_listener->priv->account_list);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ ExchangeMAPIAccountListener *config_listener = EXCHANGE_MAPI_ACCOUNT_LISTENER (object);
+ GList *list;
+
+ if (config_listener->priv) {
+ g_free (config_listener->priv);
+ }
+
+ for (list = g_list_first (mapi_accounts); list ; list = g_list_next (list)) {
+ ExchangeMAPIAccountInfo *info = (ExchangeMAPIAccountInfo *)(list->data);
+ if (info) {
+ g_free (info->uid);
+ g_free (info->name);
+ g_free (info->source_url);
+ g_free (info);
+ }
+ }
+
+ g_list_free (mapi_accounts);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+exchange_mapi_account_listener_class_init (ExchangeMAPIAccountListenerClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_ref (PARENT_TYPE);
+ object_class = G_OBJECT_CLASS (class);
+
+ /* virtual method override */
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+}
+
+static void
+exchange_mapi_account_listener_init (ExchangeMAPIAccountListener *config_listener, ExchangeMAPIAccountListenerClass *class)
+{
+ config_listener->priv = g_new0 (ExchangeMAPIAccountListenerPrivate, 1);
+}
+
+
+/* This is a list of folders returned by e-d-s. */
+static GSList *folders_list = NULL;
+
+GSList *
+exchange_mapi_account_listener_peek_folder_list (void)
+{
+ if (!folders_list)
+ folders_list = exchange_mapi_peek_folder_list ();
+
+ return folders_list;
+}
+
+void
+exchange_mapi_account_listener_get_folder_list (void)
+{
+ if (folders_list)
+ return;
+
+ folders_list = exchange_mapi_peek_folder_list ();
+}
+
+void
+exchange_mapi_account_listener_free_folder_list (void)
+{
+ exchange_mapi_folder_list_free ();
+ folders_list = NULL;
+}
+
+/*determines whehter the passed in account is exchange or not by looking at source url */
+
+static gboolean
+is_mapi_account (EAccount *account)
+{
+ return (account->source->url && (g_ascii_strncasecmp (account->source->url, MAPI_URI_PREFIX, MAPI_PREFIX_LENGTH) == 0));
+}
+
+/* looks up for an existing exchange account info in the mapi_accounts list based on uid */
+
+static ExchangeMAPIAccountInfo*
+lookup_account_info (const char *key)
+{
+ GList *list;
+
+ g_return_val_if_fail (key != NULL, NULL);
+
+ for (list = g_list_first (mapi_accounts); list; list = g_list_next (list)) {
+ ExchangeMAPIAccountInfo *info = (ExchangeMAPIAccountInfo *)(list->data);
+ if (g_ascii_strcasecmp (info->uid, key) == 0)
+ return info;
+ }
+
+ return NULL;
+}
+
+#define CALENDAR_SOURCES "/apps/evolution/calendar/sources"
+#define TASK_SOURCES "/apps/evolution/tasks/sources"
+#define JOURNAL_SOURCES "/apps/evolution/memos/sources"
+#define SELECTED_CALENDARS "/apps/evolution/calendar/display/selected_calendars"
+#define SELECTED_TASKS "/apps/evolution/calendar/tasks/selected_tasks"
+#define SELECTED_JOURNALS "/apps/evolution/calendar/memos/selected_memos"
+
+#define ITIP_MESSAGE_HANDLING "/apps/evolution/itip/delete_processed"
+
+static void
+add_cal_esource (EAccount *account, GSList *folders, ExchangeMAPIFolderType folder_type, CamelURL *url)
+{
+ ESourceList *source_list = NULL;
+ ESourceGroup *group = NULL;
+ const gchar *conf_key = NULL, *source_selection_key = NULL;
+ GSList *temp_list = NULL;
+ GConfClient* client;
+ GSList *ids, *temp ;
+ gchar *base_uri = NULL;
+
+ if (folder_type == MAPI_FOLDER_TYPE_APPOINTMENT) {
+ conf_key = CALENDAR_SOURCES;
+ source_selection_key = SELECTED_CALENDARS;
+ } else if (folder_type == MAPI_FOLDER_TYPE_TASK) {
+ conf_key = TASK_SOURCES;
+ source_selection_key = SELECTED_TASKS;
+ } else if (folder_type == MAPI_FOLDER_TYPE_MEMO) {
+ conf_key = JOURNAL_SOURCES;
+ source_selection_key = SELECTED_JOURNALS;
+ } else {
+ g_warning ("%s(%d): %s: Unknown ExchangeMAPIFolderType\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ return;
+ }
+
+ client = gconf_client_get_default ();
+ gconf_client_set_bool (client, ITIP_MESSAGE_HANDLING, TRUE, NULL);
+ source_list = e_source_list_new_for_gconf (client, conf_key);
+ base_uri = g_strdup_printf ("%s%s %s/", MAPI_URI_PREFIX, url->user, url->host);
+ group = e_source_group_new (account->name, base_uri);
+ g_free (base_uri);
+ e_source_group_set_property (group, "create_source", "yes");
+ e_source_group_set_property (group, "username", url->user);
+ e_source_group_set_property (group, "host", url->host);
+ e_source_group_set_property (group, "profile", camel_url_get_param (url, "profile"));
+ e_source_group_set_property (group, "domain", camel_url_get_param (url, "domain"));
+
+ /* We set these because on new folder creation - these are required. */
+ e_source_group_set_property (group, "acl-user-name", account->id->name);
+ e_source_group_set_property (group, "acl-user-email", account->id->address);
+ e_source_group_set_property (group, "acl-owner-name", account->id->name);
+ e_source_group_set_property (group, "acl-owner-email", account->id->address);
+
+ for (temp_list = folders; temp_list != NULL; temp_list = g_slist_next (temp_list)) {
+ ExchangeMAPIFolder *folder = temp_list->data;
+ ESource *source = NULL;
+ gchar *relative_uri = NULL, *fid = NULL;
+
+ if (folder->container_class != folder_type)
+ continue;
+
+ fid = exchange_mapi_util_mapi_id_to_string (folder->folder_id);
+ relative_uri = g_strconcat (";", fid, NULL);
+ source = e_source_new (folder->folder_name, relative_uri);
+ e_source_set_property (source, "auth", "1");
+ e_source_set_property (source, "auth-domain", EXCHANGE_MAPI_PASSWORD_COMPONENT);
+ e_source_set_property (source, "auth-type", "plain/password");
+ e_source_set_property (source, "username", url->user);
+ e_source_set_property (source, "host", url->host);
+ e_source_set_property (source, "profile", camel_url_get_param (url, "profile"));
+ e_source_set_property (source, "domain", camel_url_get_param (url, "domain"));
+ e_source_set_property (source, "folder-id", fid);
+ e_source_set_property (source, "offline_sync",
+ camel_url_get_param (url, "offline_sync") ? "1" : "0");
+
+ if (folder->is_default)
+ e_source_set_property (source, "delete", "no");
+
+ if (folder->parent_folder_id) {
+ gchar *tmp = exchange_mapi_util_mapi_id_to_string (folder->parent_folder_id);
+ e_source_set_property (source, "parent-fid", tmp);
+ g_free (tmp);
+ }
+
+ e_source_set_property (source, "acl-user-name", account->id->name);
+ e_source_set_property (source, "acl-user-email", account->id->address);
+ /* FIXME: this would change after foreign folders/delegation is implemented */
+ e_source_set_property (source, "acl-owner-name", account->id->name);
+ e_source_set_property (source, "acl-owner-email", account->id->address);
+
+ e_source_group_add_source (group, source, -1);
+
+ if (source_selection_key && folder->is_default) {
+ ids = gconf_client_get_list (client, source_selection_key , GCONF_VALUE_STRING, NULL);
+ ids = g_slist_append (ids, g_strdup (e_source_peek_uid (source)));
+ gconf_client_set_list (client, source_selection_key, GCONF_VALUE_STRING, ids, NULL);
+
+ for (temp = ids; temp != NULL; temp = g_slist_next (temp))
+ g_free (temp->data);
+
+ g_slist_free (ids);
+ }
+
+ g_object_unref (source);
+ g_free (relative_uri);
+ g_free (fid);
+ }
+
+ if (!e_source_list_add_group (source_list, group, -1))
+ return;
+
+ if (!e_source_list_sync (source_list, NULL))
+ return;
+
+ g_object_unref (group);
+ g_object_unref (source_list);
+ g_object_unref (client);
+}
+
+static void
+remove_cal_esource (EAccount *existing_account_info, ExchangeMAPIFolderType folder_type, CamelURL *url)
+{
+ ESourceList *list;
+ const gchar *conf_key = NULL, *source_selection_key = NULL;
+ GSList *groups;
+ gboolean found_group;
+ GConfClient* client;
+ GSList *ids;
+ GSList *node_tobe_deleted;
+ gchar *base_uri;
+
+ if (folder_type == MAPI_FOLDER_TYPE_APPOINTMENT) {
+ conf_key = CALENDAR_SOURCES;
+ source_selection_key = SELECTED_CALENDARS;
+ } else if (folder_type == MAPI_FOLDER_TYPE_TASK) {
+ conf_key = TASK_SOURCES;
+ source_selection_key = SELECTED_TASKS;
+ } else if (folder_type == MAPI_FOLDER_TYPE_MEMO) {
+ conf_key = JOURNAL_SOURCES;
+ source_selection_key = SELECTED_JOURNALS;
+ } else {
+ g_warning ("%s(%d): %s: Unknown ExchangeMAPIFolderType\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ return;
+ }
+
+ client = gconf_client_get_default();
+ gconf_client_set_bool (client, ITIP_MESSAGE_HANDLING, FALSE, NULL);
+ list = e_source_list_new_for_gconf (client, conf_key);
+ groups = e_source_list_peek_groups (list);
+
+ base_uri = g_strdup_printf ("mapi://%s %s/", url->user, url->host);
+
+ found_group = FALSE;
+
+ for ( ; groups != NULL && !found_group; groups = g_slist_next (groups)) {
+ ESourceGroup *group = E_SOURCE_GROUP (groups->data);
+
+ if (strcmp (e_source_group_peek_name (group), existing_account_info->name) == 0 &&
+ strcmp (e_source_group_peek_base_uri (group), base_uri) == 0) {
+ GSList *sources = e_source_group_peek_sources (group);
+
+ for( ; sources != NULL; sources = g_slist_next (sources)) {
+ ESource *source = E_SOURCE (sources->data);
+
+ if (source_selection_key) {
+ ids = gconf_client_get_list (client, source_selection_key ,
+ GCONF_VALUE_STRING, NULL);
+ node_tobe_deleted = g_slist_find_custom (ids, e_source_peek_uid (source), (GCompareFunc) strcmp);
+ if (node_tobe_deleted) {
+ g_free (node_tobe_deleted->data);
+ ids = g_slist_delete_link (ids, node_tobe_deleted);
+ }
+ gconf_client_set_list (client, source_selection_key,
+ GCONF_VALUE_STRING, ids, NULL);
+ }
+ }
+ e_source_list_remove_group (list, group);
+ e_source_list_sync (list, NULL);
+ found_group = TRUE;
+ break;
+ }
+ }
+
+ g_free (base_uri);
+ g_object_unref (list);
+ g_object_unref (client);
+}
+
+/* add sources for calendar and tasks if the account added is exchange account
+ adds the new account info to mapi_accounts list */
+
+static void
+add_calendar_sources (EAccount *account, GSList *folders)
+{
+ CamelURL *url;
+
+ url = camel_url_new (account->source->url, NULL);
+
+ if (url) {
+ add_cal_esource (account, folders, MAPI_FOLDER_TYPE_APPOINTMENT, url);
+ add_cal_esource (account, folders, MAPI_FOLDER_TYPE_TASK, url);
+ add_cal_esource (account, folders, MAPI_FOLDER_TYPE_MEMO, url);
+ }
+
+ camel_url_free (url);
+}
+
+/* removes calendar and tasks sources if the account removed is exchange account
+ removes the the account info from mapi_account list */
+
+static void
+remove_calendar_sources (EAccount *account)
+{
+ CamelURL *url;
+
+ url = camel_url_new (account->source->url, NULL);
+
+ if (url) {
+ remove_cal_esource (account, MAPI_FOLDER_TYPE_APPOINTMENT, url);
+ remove_cal_esource (account, MAPI_FOLDER_TYPE_TASK, url);
+ remove_cal_esource (account, MAPI_FOLDER_TYPE_MEMO, url);
+ }
+
+ camel_url_free (url);
+}
+
+static gboolean
+add_addressbook_sources (EAccount *account, GSList *folders)
+{
+ CamelURL *url;
+ ESourceList *list;
+ ESourceGroup *group;
+ ESource *source;
+ char *base_uri;
+ GSList *temp_list;
+ GConfClient* client;
+
+ url = camel_url_new (account->source->url, NULL);
+ if (url == NULL) {
+ return FALSE;
+ }
+
+ base_uri = g_strdup_printf ("mapi://%s %s/", url->user, url->host);
+ client = gconf_client_get_default ();
+ list = e_source_list_new_for_gconf (client, "/apps/evolution/addressbook/sources" );
+ group = e_source_group_new (account->name, base_uri);
+ e_source_group_set_property (group, "user", url->user);
+ e_source_group_set_property (group, "host", url->host);
+ e_source_group_set_property (group, "profile", camel_url_get_param (url, "profile"));
+ e_source_group_set_property (group, "domain", camel_url_get_param (url, "domain"));
+
+ for (temp_list = folders; temp_list != NULL; temp_list = g_slist_next (temp_list)) {
+ ExchangeMAPIFolder *folder = temp_list->data;
+ char *tmp = NULL;
+ if (folder->container_class != MAPI_FOLDER_TYPE_CONTACT)
+ continue;
+
+ source = e_source_new (folder->folder_name, g_strconcat (";",folder->folder_name, NULL));
+ e_source_set_property (source, "auth", "plain/password");
+ e_source_set_property (source, "auth-domain", EXCHANGE_MAPI_PASSWORD_COMPONENT);
+ e_source_set_property(source, "user", url->user);
+ e_source_set_property(source, "host", url->host);
+ e_source_set_property(source, "profile", camel_url_get_param (url, "profile"));
+ e_source_set_property(source, "domain", camel_url_get_param (url, "domain"));
+ tmp = exchange_mapi_util_mapi_id_to_string (folder->folder_id);
+ e_source_set_property(source, "folder-id", tmp);
+ g_free (tmp);
+ e_source_set_property (source, "offline_sync",
+ camel_url_get_param (url, "offline_sync") ? "1" : "0");
+ e_source_set_property (source, "completion", "true");
+ e_source_group_add_source (group, source, -1);
+ g_object_unref (source);
+ }
+
+ //Add GAL
+ {
+ char *uri;
+ uri = g_strdup_printf("galldap://%s %s/;Global Address List", url->user, url->host);
+ source = e_source_new_with_absolute_uri ("Global Address List", uri);
+// source = e_source_new ("Global Address List", g_strconcat (";","Global Address List" , NULL));
+ e_source_set_property (source, "auth", "plain/password");
+ e_source_set_property (source, "auth-domain", "GALLDAP");
+ e_source_set_property(source, "user", url->user);
+ e_source_set_property(source, "host", camel_url_get_param (url, "ad_server"));
+ e_source_set_property(source, "view-limit", camel_url_get_param (url, "ad_limit"));
+ e_source_set_property(source, "profile", camel_url_get_param (url, "profile"));
+ e_source_set_property(source, "domain", camel_url_get_param (url, "domain"));
+// e_source_set_property (source, "offline_sync",
+// camel_url_get_param (url, "offline_sync") ? "1" : "0");
+ e_source_set_property(source, "offline_sync", "1");
+ e_source_set_property (source, "completion", "true");
+ e_source_group_add_source (group, source, -1);
+ g_object_unref (source);
+ }
+ e_source_list_add_group (list, group, -1);
+ e_source_list_sync (list, NULL);
+ g_object_unref (group);
+ g_object_unref (list);
+ g_object_unref (client);
+ g_free (base_uri);
+
+ return TRUE;
+}
+
+static void
+remove_addressbook_sources (ExchangeMAPIAccountInfo *existing_account_info)
+{
+ ESourceList *list;
+ ESourceGroup *group;
+ GSList *groups;
+ gboolean found_group;
+ CamelURL *url;
+ char *base_uri;
+ GConfClient *client;
+
+ url = camel_url_new (existing_account_info->source_url, NULL);
+ if (url == NULL) {
+ return;
+ }
+
+ base_uri = g_strdup_printf ("mapi://%s %s/", url->user, url->host);
+ client = gconf_client_get_default ();
+ list = e_source_list_new_for_gconf (client, "/apps/evolution/addressbook/sources" );
+ groups = e_source_list_peek_groups (list);
+
+ found_group = FALSE;
+
+ for ( ; groups != NULL && !found_group; groups = g_slist_next (groups)) {
+
+ group = E_SOURCE_GROUP (groups->data);
+ if ( strcmp ( e_source_group_peek_base_uri (group), base_uri) == 0 && strcmp (e_source_group_peek_name (group), existing_account_info->name) == 0) {
+
+ e_source_list_remove_group (list, group);
+ e_source_list_sync (list, NULL);
+ found_group = TRUE;
+ }
+ }
+
+ g_object_unref (list);
+ g_object_unref (client);
+ g_free (base_uri);
+ camel_url_free (url);
+}
+
+static void
+mapi_account_added (EAccountList *account_listener, EAccount *account)
+{
+ ExchangeMAPIAccountInfo *info = NULL;
+
+ if (!is_mapi_account (account))
+ return;
+
+ info = g_new0 (ExchangeMAPIAccountInfo, 1);
+ info->uid = g_strdup (account->uid);
+ info->name = g_strdup (account->name);
+ info->source_url = g_strdup (account->source->url);
+ info->enabled = account->enabled;
+
+ mapi_accounts = g_list_append (mapi_accounts, info);
+
+ if (account->enabled) {
+ /* Fetch the folders into a global list for future use.*/
+ exchange_mapi_account_listener_get_folder_list ();
+
+ add_addressbook_sources (account, folders_list);
+ add_calendar_sources (account, folders_list);
+ /*FIXME: Maybe the folders_list above should be freed */
+ }
+}
+
+static void
+mapi_account_removed (EAccountList *account_listener, EAccount *account)
+{
+ ExchangeMAPIAccountInfo *info = NULL;
+ CamelURL *url = NULL;
+
+ if (!is_mapi_account (account))
+ return;
+
+ /* We store a complete list of MAPI accounts - both enabled and disabled */
+ info = lookup_account_info (account->uid);
+ g_return_if_fail (info != NULL);
+
+ /* Remove from the local MAPI accounts list */
+ mapi_accounts = g_list_remove (mapi_accounts, info);
+
+ /* If the account was disabled, then the corresponding ESource should have been removed
+ * when the account was disabled. We should only clean up the MAPI profile database etc.
+ */
+ if (info->enabled) {
+ remove_addressbook_sources (info);
+ remove_calendar_sources (account);
+ }
+
+ /* Now, clean up the profile database etc */
+ url = camel_url_new (info->source_url, NULL);
+ if (url != NULL) {
+ const char *profile = camel_url_get_param (url, "profile");
+ gchar *key = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD | CAMEL_URL_HIDE_PARAMS);
+ exchange_mapi_delete_profile (profile);
+ e_passwords_forget_password (EXCHANGE_MAPI_PASSWORD_COMPONENT, key);
+ g_free (key);
+ camel_url_free (url);
+ }
+
+ /* Free up the structure */
+ g_free (info->uid);
+ g_free (info->name);
+ g_free (info->source_url);
+ g_free (info);
+}
+
+static gboolean
+create_profile_entry (CamelURL *url)
+{
+ gboolean status = FALSE;
+ guint8 attempts = 0;
+
+ while (!status && attempts <= 3) {
+ gchar *password = NULL, *key = NULL;
+
+ key = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD | CAMEL_URL_HIDE_PARAMS);
+ password = e_passwords_get_password (EXCHANGE_MAPI_PASSWORD_COMPONENT, key);
+ if (!password) {
+ gboolean remember = FALSE;
+ gchar *title;
+
+ title = g_strdup_printf (_("Enter Password for %s %s"), url->user, url->host);
+ password = e_passwords_ask_password (title, EXCHANGE_MAPI_PASSWORD_COMPONENT, key, title,
+ E_PASSWORDS_REMEMBER_FOREVER|E_PASSWORDS_SECRET,
+ &remember, NULL);
+ g_free (title);
+ }
+ g_free (key);
+
+ if (password)
+ status = exchange_mapi_create_profile (url->user, password, camel_url_get_param (url, "domain"), url->host);
+
+ ++attempts;
+ }
+
+ return status;
+}
+
+static gboolean
+mapi_camel_url_equal (CamelURL *a, CamelURL *b)
+{
+ const char *params[] = { "profile", "domain", "ad_limit", "ad_server" };
+ guint n_params = G_N_ELEMENTS (params), i;
+ gboolean retval = TRUE;
+
+ retval &= camel_url_equal (a, b);
+
+ for (i = 0; i < n_params; ++i)
+ retval &= (g_ascii_strcasecmp (camel_url_get_param (a, params[i]), camel_url_get_param (b, params[i])) == 0);
+
+ return retval;
+}
+
+static void
+mapi_account_changed (EAccountList *account_listener, EAccount *account)
+{
+ CamelURL *new_url = NULL, *old_url = NULL;
+ gboolean isa_mapi_account = FALSE;
+ ExchangeMAPIAccountInfo *existing_account_info = NULL;
+
+ isa_mapi_account = is_mapi_account (account);
+
+ if (isa_mapi_account)
+ existing_account_info = lookup_account_info (account->uid);
+
+ if (existing_account_info)
+ old_url = camel_url_new (existing_account_info->source_url, NULL);
+
+ new_url = camel_url_new (account->source->url, NULL);
+
+ if (existing_account_info == NULL && isa_mapi_account) {
+ /* some account of other type is changed to MAPI */
+ if (create_profile_entry (new_url)) {
+ /* Things are successful */
+ gchar *profname = NULL, *uri = NULL;
+ ExchangeMAPIAccountListener *config_listener = exchange_mapi_accounts_peek_config_listener();
+
+ profname = g_strdup_printf("%s %s", new_url->user, camel_url_get_param (new_url, "domain"));
+ camel_url_set_param(new_url, "profile", profname);
+ g_free (profname);
+
+ uri = camel_url_to_string(new_url, 0);
+ /* FIXME: Find a better way to append to the Account source URL. The current
+ * method uses e_account_set_string() which initiates another signal emmission
+ * which we have to block for now. */
+ g_signal_handlers_block_by_func (config_listener->priv->account_list, G_CALLBACK (mapi_account_changed), NULL);
+ e_account_set_string(account, E_ACCOUNT_SOURCE_URL, uri);
+ g_signal_handlers_unblock_by_func (config_listener->priv->account_list, G_CALLBACK (mapi_account_changed), NULL);
+ g_free (uri);
+
+ mapi_account_added (account_listener, account);
+ }
+ } else if (existing_account_info != NULL && !isa_mapi_account) {
+ /* MAPI account is changed to some other type */
+ mapi_account_removed (account_listener, account);
+ } else if (existing_account_info != NULL && isa_mapi_account) {
+ /* Just disabling the account requires no further action */
+ if (!account->enabled) {
+ remove_addressbook_sources (existing_account_info);
+ remove_calendar_sources (account);
+ existing_account_info->enabled = FALSE;
+ } else if (!mapi_camel_url_equal (old_url, new_url) || (existing_account_info->enabled != account->enabled)) {
+ /* Some or all of the account info changed OR the account has been moved from a disabled state to enabled state */
+ mapi_account_removed (account_listener, account);
+ if (create_profile_entry (new_url)) {
+ /* Things are successful */
+ gchar *profname = NULL, *uri = NULL;
+ ExchangeMAPIAccountListener *config_listener = exchange_mapi_accounts_peek_config_listener();
+
+ profname = g_strdup_printf("%s %s", new_url->user, camel_url_get_param (new_url, "domain"));
+ camel_url_set_param(new_url, "profile", profname);
+ g_free (profname);
+
+ uri = camel_url_to_string(new_url, 0);
+ /* FIXME: Find a better way to append to the Account source URL. The current
+ * method uses e_account_set_string() which initiates another signal emmission
+ * which we have to block for now. */
+ g_signal_handlers_block_by_func (config_listener->priv->account_list, G_CALLBACK (mapi_account_changed), NULL);
+ e_account_set_string(account, E_ACCOUNT_SOURCE_URL, uri);
+ g_signal_handlers_unblock_by_func (config_listener->priv->account_list, G_CALLBACK (mapi_account_changed), NULL);
+ g_free (uri);
+
+ mapi_account_added (account_listener, account);
+ }
+ }
+ }
+
+ if (old_url)
+ camel_url_free (old_url);
+
+ camel_url_free (new_url);
+}
+
+static void
+exchange_mapi_account_listener_construct (ExchangeMAPIAccountListener *config_listener)
+{
+ EIterator *iter;
+
+ config_listener->priv->account_list = e_account_list_new (config_listener->priv->gconf_client);
+
+ for (iter = e_list_get_iterator (E_LIST(config_listener->priv->account_list)); e_iterator_is_valid (iter); e_iterator_next (iter)) {
+ EAccount *account = E_ACCOUNT (e_iterator_get (iter));
+ if (is_mapi_account (account)) {
+ ExchangeMAPIAccountInfo *info = g_new0 (ExchangeMAPIAccountInfo, 1);
+ info->uid = g_strdup (account->uid);
+ info->name = g_strdup (account->name);
+ info->source_url = g_strdup (account->source->url);
+ info->enabled = account->enabled;
+ mapi_accounts = g_list_append (mapi_accounts, info);
+ }
+ }
+
+ d(g_debug ("MAPI listener is constructed with %d listed MAPI accounts ", g_list_length (mapi_accounts)));
+
+ g_signal_connect (config_listener->priv->account_list, "account_added", G_CALLBACK (mapi_account_added), NULL);
+ g_signal_connect (config_listener->priv->account_list, "account_changed", G_CALLBACK (mapi_account_changed), NULL);
+ g_signal_connect (config_listener->priv->account_list, "account_removed", G_CALLBACK (mapi_account_removed), NULL);
+}
+
+GType
+exchange_mapi_account_listener_get_type (void)
+{
+ static GType exchange_mapi_account_listener_type = 0;
+
+ if (!exchange_mapi_account_listener_type) {
+ static GTypeInfo info = {
+ sizeof (ExchangeMAPIAccountListenerClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) exchange_mapi_account_listener_class_init,
+ NULL, NULL,
+ sizeof (ExchangeMAPIAccountListener),
+ 0,
+ (GInstanceInitFunc) exchange_mapi_account_listener_init
+ };
+ exchange_mapi_account_listener_type = g_type_register_static (PARENT_TYPE, "ExchangeMAPIAccountListener", &info, 0);
+ }
+
+ return exchange_mapi_account_listener_type;
+}
+
+ExchangeMAPIAccountListener *
+exchange_mapi_account_listener_new ()
+{
+ ExchangeMAPIAccountListener *config_listener;
+
+ config_listener = g_object_new (EXCHANGE_MAPI_ACCOUNT_LISTENER_TYPE, NULL);
+ config_listener->priv->gconf_client = gconf_client_get_default();
+
+ exchange_mapi_account_listener_construct (config_listener);
+
+ return config_listener;
+}
Added: trunk/src/account-setup-eplugin/exchange-mapi-account-listener.h
==============================================================================
--- (empty file)
+++ trunk/src/account-setup-eplugin/exchange-mapi-account-listener.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,58 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EXCHANGE_MAPI_ACCOUNT_LISTENER_H
+#define EXCHANGE_MAPI_ACCOUNT_LISTENER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EXCHANGE_MAPI_ACCOUNT_LISTENER_TYPE (exchange_mapi_account_listener_get_type ())
+#define EXCHANGE_MAPI_ACCOUNT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXCHANGE_MAPI_ACCOUNT_LISTENER_TYPE, ExchangeMAPIAccountListener))
+#define EXCHANGE_MAPI_ACCOUNT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EXCHANGE_MAPI_ACCOUNT_LISTENER_TYPE, ExchangeMAPIAccountListenerClass))
+#define EXCHANGE_MAPI_IS_ACCOUNT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXCHANGE_MAPI_ACCOUNT_LISTENER_TYPE))
+#define EXCHANGE_MAPI_IS_ACCOUNT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EXCHANGE_MAPI_ACCOUNT_LISTENER_TYPE))
+
+typedef struct _ExchangeMAPIAccountListener ExchangeMAPIAccountListener;
+typedef struct _ExchangeMAPIAccountListenerClass ExchangeMAPIAccountListenerClass;
+typedef struct _ExchangeMAPIAccountListenerPrivate ExchangeMAPIAccountListenerPrivate;
+
+struct _ExchangeMAPIAccountListener {
+ GObject parent;
+ ExchangeMAPIAccountListenerPrivate *priv;
+};
+
+struct _ExchangeMAPIAccountListenerClass {
+ GObjectClass parent_class;
+};
+
+GType exchange_mapi_account_listener_get_type (void);
+ExchangeMAPIAccountListener * exchange_mapi_account_listener_new (void);
+GSList * exchange_mapi_account_listener_peek_folder_list (void);
+void exchange_mapi_account_listener_get_folder_list (void);
+void exchange_mapi_account_listener_free_folder_list (void);
+
+G_END_DECLS
+
+#endif /* EXCHANGE_MAPI_ACCOUNT_LISTENER_H */
Added: trunk/src/account-setup-eplugin/exchange-mapi-account-setup.c
==============================================================================
--- (empty file)
+++ trunk/src/account-setup-eplugin/exchange-mapi-account-setup.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,699 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan novell com>
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+#include <camel/camel-provider.h>
+#include <camel/camel-url.h>
+#include <camel/camel-service.h>
+#include <camel/camel-folder.h>
+#include <libedataserver/e-xml-hash-utils.h>
+#include <libedataserverui/e-passwords.h>
+#include <libedataserver/e-account.h>
+#include <e-util/e-dialog-utils.h>
+#include <libmapi/libmapi.h>
+#include "mail/em-config.h"
+#include "exchange-mapi-account-setup.h"
+#include <addressbook/gui/widgets/eab-config.h>
+#include <calendar/gui/e-cal-config.h>
+
+#include <exchange-mapi-folder.h>
+#include <exchange-mapi-connection.h>
+#include <exchange-mapi-utils.h>
+
+#define d(x) x
+
+int e_plugin_lib_enable (EPluginLib *ep, int enable);
+
+/* Account Setup */
+GtkWidget *org_gnome_exchange_mapi_account_setup (EPlugin *epl, EConfigHookItemFactoryData *data);
+gboolean org_gnome_exchange_mapi_check_options(EPlugin *epl, EConfigHookPageCheckData *data);
+
+/* New Addressbook/CAL */
+GtkWidget *exchange_mapi_create (EPlugin *epl, EConfigHookItemFactoryData *data);
+
+/* New Addressbook */
+gboolean exchange_mapi_book_check (EPlugin *epl, EConfigHookPageCheckData *data);
+void exchange_mapi_book_commit (EPlugin *epl, EConfigTarget *target);
+
+/* New calendar/task list/memo list */
+gboolean exchange_mapi_cal_check (EPlugin *epl, EConfigHookPageCheckData *data);
+void exchange_mapi_cal_commit (EPlugin *epl, EConfigTarget *target);
+
+
+static ExchangeMAPIAccountListener *config_listener = NULL;
+
+static void
+free_mapi_listener ( void )
+{
+ g_object_unref (config_listener);
+}
+
+int
+e_plugin_lib_enable (EPluginLib *ep, int enable)
+{
+ g_debug ("Loading Exchange MAPI Plugin \n");
+
+ if (!config_listener) {
+ config_listener = exchange_mapi_account_listener_new ();
+ g_atexit ( free_mapi_listener );
+ }
+
+ return 0;
+}
+
+ExchangeMAPIAccountListener *
+exchange_mapi_accounts_peek_config_listener ()
+{
+ return config_listener;
+}
+
+gboolean
+exchange_mapi_delete_profile (const char *profile)
+{
+ enum MAPISTATUS retval;
+ gboolean result = FALSE;
+ gchar *profpath = NULL;
+
+ profpath = g_build_filename (g_get_home_dir(), DEFAULT_PROF_PATH, NULL);
+ if (!g_file_test (profpath, G_FILE_TEST_EXISTS)) {
+ g_warning ("No need to delete profile. DB itself is missing \n");
+ result = TRUE;
+ goto cleanup;
+ }
+
+ retval = MAPIInitialize(profpath);
+ if (retval == MAPI_E_SESSION_LIMIT)
+ /* do nothing, the profile store is already initialized */
+ ;
+ else if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("MAPIInitialize", GetLastError());
+ goto cleanup;
+ }
+
+ g_debug ("Deleting profile %s ", profile);
+ retval = DeleteProfile(profile);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("DeleteProfile", GetLastError());
+ goto cleanup;
+ }
+
+ exchange_mapi_connection_close ();
+ result = TRUE;
+
+cleanup:
+ g_free(profpath);
+
+ return result;
+}
+
+gboolean
+exchange_mapi_create_profile(const char *username, const char *password, const char *domain, const char *server)
+{
+ enum MAPISTATUS retval;
+ gboolean result = FALSE;
+ const gchar *workstation = "localhost";
+ gchar *profname = NULL, *profpath = NULL;
+ struct mapi_session *session = NULL;
+
+ d(g_print ("Create profile with %s %s (****) %s %s\n", username, password, domain, server));
+
+ profpath = g_build_filename (g_get_home_dir(), DEFAULT_PROF_PATH, NULL);
+ profname = g_strdup_printf("%s %s", username, domain);
+
+ if (!g_file_test (profpath, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
+ /* Create a ProfileStore */
+ retval = CreateProfileStore (profpath, LIBMAPI_LDIF_DIR);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("CreateProfileStore", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ retval = MAPIInitialize(profpath);
+ if (retval == MAPI_E_SESSION_LIMIT)
+ /* do nothing, the profile store is already initialized */
+ mapi_errstr("MAPIInitialize", GetLastError());
+ else if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("MAPIInitialize", GetLastError());
+ goto cleanup;
+ }
+
+ /* Delete any existing profiles with the same profilename */
+ retval = DeleteProfile(profname);
+ /* don't bother to check error - it would be valid if we got an error */
+
+ retval = CreateProfile(profname, username, password, 0);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("CreateProfile", GetLastError());
+ goto cleanup;
+ }
+
+ mapi_profile_add_string_attr(profname, "binding", server);
+ mapi_profile_add_string_attr(profname, "workstation", workstation);
+ mapi_profile_add_string_attr(profname, "domain", domain);
+
+ /* This is only convenient here and should be replaced at some point */
+ mapi_profile_add_string_attr(profname, "codepage", "0x4e4");
+ mapi_profile_add_string_attr(profname, "language", "0x40c");
+ mapi_profile_add_string_attr(profname, "method", "0x409");
+
+ /* Login now */
+ d(g_print("Logging into the server... "));
+ retval = MapiLogonProvider(&session, profname, NULL, PROVIDER_ID_NSPI);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("MapiLogonProvider", GetLastError());
+ g_debug ("Deleting profile %s ", profname);
+ retval = DeleteProfile(profname);
+ if (retval != MAPI_E_SUCCESS)
+ mapi_errstr("DeleteProfile", GetLastError());
+ goto cleanup;
+ }
+ d(g_print("succeeded \n"));
+
+ retval = ProcessNetworkProfile(session, username, NULL, NULL);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("ProcessNetworkProfile", GetLastError());
+ goto cleanup;
+ }
+
+ /* Set it as the default profile. Is this needed? */
+ retval = SetDefaultProfile(profname);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetDefaultProfile", GetLastError());
+ goto cleanup;
+ }
+
+ /* Close the connection, so that we can login with what we created */
+ exchange_mapi_connection_close ();
+
+ /* Initialize a global connection */
+ if (exchange_mapi_connection_new (profname, password)) {
+ result = TRUE;
+ exchange_mapi_account_listener_get_folder_list ();
+ }
+
+cleanup:
+ if (!result)
+ MAPIUninitialize ();
+
+ g_free (profname);
+ g_free (profpath);
+
+ return result;
+}
+
+
+static void
+validate_credentials (GtkWidget *widget, EConfig *config)
+{
+ EMConfigTargetAccount *target_account = (EMConfigTargetAccount *)(config->target);
+ CamelURL *url = NULL;
+ gchar *key = NULL, *password = NULL;
+
+ url = camel_url_new (e_account_get_string (target_account->account, E_ACCOUNT_SOURCE_URL), NULL);
+ key = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD | CAMEL_URL_HIDE_PARAMS);
+ password = e_passwords_get_password (EXCHANGE_MAPI_PASSWORD_COMPONENT, key);
+ if (!password) {
+ gboolean remember = FALSE;
+ gchar *title;
+
+ title = g_strdup_printf (_("Enter Password for %s %s"), url->user, url->host);
+ password = e_passwords_ask_password (title, EXCHANGE_MAPI_PASSWORD_COMPONENT, key, title,
+ E_PASSWORDS_REMEMBER_FOREVER|E_PASSWORDS_SECRET,
+ &remember, NULL);
+ g_free (title);
+ }
+
+ if (password) {
+ const gchar *domain_name = camel_url_get_param (url, "domain");
+ gboolean status = exchange_mapi_create_profile (url->user, password, domain_name, url->host);
+ if (status) {
+ /* Things are successful */
+ gchar *profname = NULL, *uri = NULL;
+
+ profname = g_strdup_printf("%s %s", url->user, domain_name);
+ camel_url_set_param(url, "profile", profname);
+ g_free (profname);
+
+ uri = camel_url_to_string(url, 0);
+ e_account_set_string(target_account->account, E_ACCOUNT_SOURCE_URL, uri);
+ g_free (uri);
+ } else {
+ e_passwords_forget_password (EXCHANGE_MAPI_PASSWORD_COMPONENT, key);
+ /* FIXME: Run an error dialog here */
+ }
+ }
+
+ g_free (password);
+ g_free (key);
+ camel_url_free (url);
+}
+
+static void
+domain_entry_changed(GtkWidget *entry, EConfig *config)
+{
+ EMConfigTargetAccount *target = (EMConfigTargetAccount *)(config->target);
+ CamelURL *url = NULL;
+ const char *domain = NULL;
+ char *url_string = NULL;
+
+ url = camel_url_new (e_account_get_string(target->account, E_ACCOUNT_SOURCE_URL), NULL);
+ domain = gtk_entry_get_text (GTK_ENTRY(entry));
+
+ if (domain && domain[0])
+ camel_url_set_param (url, "domain", domain);
+ else
+ camel_url_set_param (url, "domain", NULL);
+
+ url_string = camel_url_to_string (url, 0);
+ e_account_set_string (target->account, E_ACCOUNT_SOURCE_URL, url_string);
+ g_free (url_string);
+
+ camel_url_free (url);
+}
+
+GtkWidget *
+org_gnome_exchange_mapi_account_setup (EPlugin *epl, EConfigHookItemFactoryData *data)
+{
+ EMConfigTargetAccount *target_account;
+ CamelURL *url;
+ GtkWidget *hbox = NULL;
+
+ target_account = (EMConfigTargetAccount *)data->config->target;
+ url = camel_url_new(e_account_get_string(target_account->account, E_ACCOUNT_SOURCE_URL), NULL);
+
+ g_return_val_if_fail (url != NULL, NULL);
+
+ if (!g_ascii_strcasecmp (url->protocol, "mapi")) {
+ GtkWidget *label;
+ GtkWidget *domain_name;
+ GtkWidget *auth_button;
+ int row = ((GtkTable *)data->parent)->nrows;
+
+ /* Domain name & Authenticate Button */
+ hbox = gtk_hbox_new (FALSE, 6);
+ label = gtk_label_new_with_mnemonic (_("_Domain name:"));
+ gtk_widget_show (label);
+
+ domain_name = gtk_entry_new ();
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), domain_name);
+ gtk_box_pack_start (GTK_BOX (hbox), domain_name, FALSE, FALSE, 0);
+ g_signal_connect (domain_name, "changed", G_CALLBACK(domain_entry_changed), data->config);
+
+ auth_button = gtk_button_new_with_mnemonic (_("_Authenticate"));
+ gtk_box_pack_start (GTK_BOX (hbox), auth_button, FALSE, FALSE, 0);
+ g_signal_connect(GTK_OBJECT(auth_button), "clicked", G_CALLBACK(validate_credentials), data->config);
+
+ gtk_table_attach (GTK_TABLE (data->parent), label, 0, 1, row, row+1, 0, 0, 0, 0);
+ gtk_widget_show_all (GTK_WIDGET (hbox));
+ gtk_table_attach (GTK_TABLE (data->parent), GTK_WIDGET (hbox), 1, 2, row, row+1, GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 0);
+ }
+
+ camel_url_free (url);
+ return GTK_WIDGET (hbox);
+}
+
+gboolean
+org_gnome_exchange_mapi_check_options(EPlugin *epl, EConfigHookPageCheckData *data)
+{
+ EMConfigTargetAccount *target = (EMConfigTargetAccount *)(data->config->target);
+ gboolean status = FALSE;
+
+ if (data->pageid != NULL && g_ascii_strcasecmp (data->pageid, "10.receive") == 0) {
+ CamelURL *url = camel_url_new (e_account_get_string(target->account, E_ACCOUNT_SOURCE_URL), NULL);
+ if (url && url->protocol && g_ascii_strcasecmp (url->protocol, "mapi") == 0) {
+ const gchar *prof = NULL;
+
+ /* We assume that if the profile is set, then the setting is valid. */
+ prof = camel_url_get_param (url, "profile");
+
+ if (prof && *prof)
+ status = TRUE;
+ }
+ if (url)
+ camel_url_free(url);
+ }
+
+ /* FIXME: don't know why we should always return TRUE */
+ return TRUE;
+
+ return status;
+}
+
+enum {
+ CONTACTSNAME_COL,
+ CONTACTSFID_COL,
+ CONTACTSFOLDER_COL,
+ NUM_COLS
+};
+
+
+static gboolean
+check_node (GtkTreeStore *ts, ExchangeMAPIFolder *folder, GtkTreeIter *iter)
+{
+ mapi_id_t fid;
+ gboolean status = FALSE;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (ts), iter, 1, &fid, -1);
+ if (fid && folder->parent_folder_id == fid) {
+ /* Do something */
+ GtkTreeIter node;
+ gtk_tree_store_append (ts, &node, iter);
+ gtk_tree_store_set (ts, &node, 0, folder->folder_name, 1, folder->folder_id, 2, folder,-1);
+ return TRUE;
+ }
+
+ if (gtk_tree_model_iter_has_child (ts, iter)) {
+ GtkTreeIter child;
+ gtk_tree_model_iter_children (ts, &child, iter);
+ status = check_node (ts, folder, &child);
+ }
+
+ while (gtk_tree_model_iter_next (ts, iter) && !status) {
+ status = check_node (ts, folder, iter);
+ }
+
+ return status;
+}
+
+static void
+add_to_store (GtkTreeStore *ts, ExchangeMAPIFolder *folder)
+{
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter_first (ts, &iter);
+ if (!check_node (ts, folder, &iter)) {
+ GtkTreeIter node;
+ gtk_tree_store_append (ts, &node, &iter);
+ gtk_tree_store_set (ts, &node, 0, folder->folder_name, 1, folder->folder_id, -1);
+
+ }
+}
+
+static void
+add_folders (GSList *folders, GtkTreeStore *ts)
+{
+ GSList *tmp = folders;
+ GtkTreeIter iter;
+ char *node = _("Personal Folders");
+
+ gtk_tree_store_append (ts, &iter, NULL);
+ gtk_tree_store_set (ts, &iter, 0, node, -1);
+ while (tmp) {
+ ExchangeMAPIFolder *folder = tmp->data;
+ g_print("%s\n", folder->folder_name);
+ add_to_store (ts, folder);
+ tmp = tmp->next;
+ }
+}
+
+static void
+exchange_mapi_cursor_change (GtkTreeView *treeview, ESource *source)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ mapi_id_t pfid;
+ gchar *sfid=NULL;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+ gtk_tree_selection_get_selected(selection, &model, &iter);
+
+ gtk_tree_model_get (model, &iter, CONTACTSFID_COL, &pfid, -1);
+ sfid = exchange_mapi_util_mapi_id_to_string (pfid);
+ e_source_set_property (source, "parent-fid", sfid);
+ g_free (sfid);
+}
+
+GtkWidget *
+exchange_mapi_create (EPlugin *epl, EConfigHookItemFactoryData *data)
+{
+ GtkWidget *vbox, *label, *scroll, *tv;
+ EABConfigTargetSource *t = (EABConfigTargetSource *) data->target;
+ ESource *source = t->source;
+ char *uri_text;
+ GtkCellRenderer *rcell;
+ GtkTreeStore *ts;
+ GtkTreeViewColumn *tvc;
+ const char *acc;
+ GSList *folders = exchange_mapi_account_listener_peek_folder_list ();
+
+ uri_text = e_source_get_uri (source);
+ if (uri_text && g_ascii_strncasecmp (uri_text, MAPI_URI_PREFIX, MAPI_PREFIX_LENGTH)) {
+ return NULL;
+ }
+
+ acc = e_source_group_peek_name (e_source_peek_group (source));
+ ts = gtk_tree_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_INT64, G_TYPE_POINTER);
+
+ add_folders (folders, ts);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+
+ if (!strcmp (data->config->id, "org.gnome.evolution.calendar.calendarProperties")) {
+ int row = ((GtkTable*) data->parent)->nrows;
+ gtk_table_attach (GTK_TABLE (data->parent), vbox, 0, 2, row+1, row+2, GTK_FILL|GTK_EXPAND, 0, 0, 0);
+ } else if (!strcmp (data->config->id, "com.novell.evolution.addressbook.config.accountEditor")) {
+ gtk_container_add (GTK_CONTAINER (data->parent), vbox);
+ }
+
+ label = gtk_label_new_with_mnemonic (_("_Location:"));
+ gtk_widget_show (label);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ rcell = gtk_cell_renderer_text_new ();
+ tvc = gtk_tree_view_column_new_with_attributes (acc, rcell, "text", CONTACTSNAME_COL, NULL);
+ tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (ts));
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tv), tvc);
+ g_object_set (tv,"expander-column", tvc, "headers-visible", TRUE, NULL);
+ gtk_tree_view_expand_all (GTK_TREE_VIEW (tv));
+
+ scroll = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
+ g_object_set (scroll, "height-request", 150, NULL);
+ gtk_container_add (GTK_CONTAINER (scroll), tv);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), tv);
+ g_signal_connect (G_OBJECT (tv), "cursor-changed", G_CALLBACK (exchange_mapi_cursor_change), t->source);
+ gtk_widget_show_all (scroll);
+
+ gtk_box_pack_start (GTK_BOX (vbox), scroll, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (vbox);
+ return vbox;
+}
+
+gboolean
+exchange_mapi_book_check (EPlugin *epl, EConfigHookPageCheckData *data)
+{
+ EABConfigTargetSource *t = (EABConfigTargetSource *) data->target;
+ ESource *source = t->source;
+ char *uri_text = e_source_get_uri (source);
+
+ if (!uri_text)
+ return TRUE;
+
+ /* FIXME: Offline handling */
+
+ /* not a MAPI account */
+ if (g_ascii_strncasecmp (uri_text, MAPI_URI_PREFIX, MAPI_PREFIX_LENGTH)) {
+ g_free (uri_text);
+ return TRUE;
+ }
+
+ /* does not have a parent-fid which is needed for folder creation on server */
+ if (!e_source_get_property (source, "parent-fid")) {
+ g_free (uri_text);
+ return FALSE;
+ }
+
+ g_free (uri_text);
+ return TRUE;
+}
+
+void
+exchange_mapi_book_commit (EPlugin *epl, EConfigTarget *target)
+{
+ EABConfigTargetSource *t = (EABConfigTargetSource *) target;
+ ESource *source = t->source;
+ char *uri_text, *tmp;
+ const char *sfid;
+ mapi_id_t fid, pfid;
+ ESourceGroup *grp;
+
+ uri_text = e_source_get_uri (source);
+ if (uri_text && g_ascii_strncasecmp (uri_text, MAPI_URI_PREFIX, MAPI_PREFIX_LENGTH))
+ return;
+
+ //FIXME: Offline handling
+ sfid = e_source_get_property (source, "parent-fid");
+ exchange_mapi_util_mapi_id_from_string (sfid, &pfid);
+
+ fid = exchange_mapi_create_folder (olFolderContacts, pfid, e_source_peek_name (source));
+ g_print("Created %016llX\n", fid);
+ grp = e_source_peek_group (source);
+ e_source_set_property (source, "auth", "plain/password");
+ e_source_set_property (source, "auth-domain", EXCHANGE_MAPI_PASSWORD_COMPONENT);
+ e_source_set_property(source, "user", e_source_group_get_property (grp, "user"));
+ e_source_set_property(source, "host", e_source_group_get_property (grp, "host"));
+ e_source_set_property(source, "profile", e_source_group_get_property (grp, "profile"));
+ e_source_set_property(source, "domain", e_source_group_get_property (grp, "domain"));
+ e_source_set_relative_uri (source, g_strconcat (";",e_source_peek_name (source), NULL));
+
+ tmp = exchange_mapi_util_mapi_id_to_string (fid);
+ e_source_set_property(source, "folder-id", tmp);
+ g_free (tmp);
+ e_source_set_property (source, "completion", "true");
+ // Update the folder list in the plugin and ExchangeMAPIFolder
+
+ return;
+}
+
+
+/* New calendar/task list/memo list */
+gboolean
+exchange_mapi_cal_check (EPlugin *epl, EConfigHookPageCheckData *data)
+{
+ ECalConfigTargetSource *t = (ECalConfigTargetSource *)(data->target);
+ ESource *source = t->source;
+ char *uri_text = e_source_get_uri (source);
+
+ if (!uri_text)
+ return TRUE;
+
+ /* FIXME: Offline handling */
+
+ /* not a MAPI account */
+ if (g_ascii_strncasecmp (uri_text, MAPI_URI_PREFIX, MAPI_PREFIX_LENGTH)) {
+ g_free (uri_text);
+ return TRUE;
+ }
+
+ g_free (uri_text);
+
+ /* FIXME: Offline handling */
+
+ /* does not have a parent-fid which is needed for folder creation on server */
+ if (!e_source_get_property (source, "parent-fid"))
+ return FALSE;
+
+ return TRUE;
+}
+
+void
+exchange_mapi_cal_commit (EPlugin *epl, EConfigTarget *target)
+{
+ ECalConfigTargetSource *t = (ECalConfigTargetSource *) target;
+ ESourceGroup *group;
+ ESource *source = t->source;
+ gchar *tmp, *sfid;
+ mapi_id_t fid, pfid;
+ uint32_t type;
+ char *uri_text = e_source_get_uri (source);
+
+ if (!uri_text || g_ascii_strncasecmp (uri_text, MAPI_URI_PREFIX, MAPI_PREFIX_LENGTH))
+ return;
+ g_free (uri_text);
+
+ switch (t->source_type) {
+ case E_CAL_SOURCE_TYPE_EVENT:
+ type = olFolderCalendar;
+ break;
+ case E_CAL_SOURCE_TYPE_TODO:
+ type = olFolderTasks;
+ break;
+ case E_CAL_SOURCE_TYPE_JOURNAL:
+ type = olFolderNotes;
+ break;
+ default:
+ g_warning ("%s(%d): %s: Unknown ExchangeMAPIFolderType\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ return;
+ }
+
+ /* FIXME: Offline handling */
+
+ exchange_mapi_util_mapi_id_from_string (e_source_get_property (source, "parent-fid"), &pfid);
+
+ fid = exchange_mapi_create_folder (type, pfid, e_source_peek_name (source));
+
+ sfid = exchange_mapi_util_mapi_id_to_string (fid);
+ tmp = g_strconcat (";", sfid, NULL);
+ e_source_set_relative_uri (source, tmp);
+ g_free (tmp);
+ g_free (sfid);
+
+ e_source_set_property (source, "auth", "1");
+ e_source_set_property (source, "auth-domain", EXCHANGE_MAPI_PASSWORD_COMPONENT);
+ e_source_set_property (source, "auth-type", "plain/password");
+
+ group = e_source_peek_group (source);
+
+ tmp = e_source_group_get_property (group, "username");
+ e_source_set_property (source, "username", tmp);
+ g_free (tmp);
+
+ tmp = e_source_group_get_property (group, "host");
+ e_source_set_property (source, "host", tmp);
+ g_free (tmp);
+
+ tmp = e_source_group_get_property (group, "profile");
+ e_source_set_property (source, "profile", tmp);
+ g_free (tmp);
+
+ tmp = e_source_group_get_property (group, "domain");
+ e_source_set_property (source, "domain", tmp);
+ g_free (tmp);
+
+ tmp = exchange_mapi_util_mapi_id_to_string (fid);
+ e_source_set_property (source, "folder-id", tmp);
+ g_free (tmp);
+
+ e_source_set_property (source, "offline_sync", "0");
+
+ /* Delegatees can never create folders for delegators. So we can copy safely. */
+ tmp = e_source_group_get_property (group, "acl-user-name");
+ e_source_set_property (source, "acl-user-name", tmp);
+ g_free (tmp);
+ tmp = e_source_group_get_property (group, "acl-user-email");
+ e_source_set_property (source, "acl-user-email", tmp);
+ g_free (tmp);
+ tmp = e_source_group_get_property (group, "acl-owner-name");
+ e_source_set_property (source, "acl-owner-name", tmp);
+ g_free (tmp);
+ tmp = e_source_group_get_property (group, "acl-owner-email");
+ e_source_set_property (source, "acl-owner-email", tmp);
+ g_free (tmp);
+
+ // Update the folder list in the plugin and ExchangeMAPIFolder
+ return;
+}
+
Added: trunk/src/account-setup-eplugin/exchange-mapi-account-setup.h
==============================================================================
--- (empty file)
+++ trunk/src/account-setup-eplugin/exchange-mapi-account-setup.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,47 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan novell com>
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+
+#ifndef EXCHANGE_MAPI_ACCOUNT_SETUP_H
+#define EXCHANGE_MAPI_ACCOUNT_SETUP_H
+
+#include "exchange-mapi-account-listener.h"
+
+/* This definition should be in-sync with the definition in camel-mapi-store.c */
+#define EXCHANGE_MAPI_PASSWORD_COMPONENT "ExchangeMAPI"
+
+#define DEFAULT_PROF_PATH ".evolution/mapi-profiles.ldb"
+
+#define MAPI_URI_PREFIX "mapi://"
+#define MAPI_PREFIX_LENGTH 7
+
+ExchangeMAPIAccountListener *
+exchange_mapi_accounts_peek_config_listener (void);
+
+gboolean
+exchange_mapi_create_profile(const char *username, const char *password, const char *domain, const char *server);
+
+gboolean
+exchange_mapi_delete_profile (const char *profile);
+
+#endif /* EXCHANGE_MAPI_ACCOUNT_SETUP_H */
Added: trunk/src/account-setup-eplugin/org-gnome-exchange-mapi.eplug.xml
==============================================================================
--- (empty file)
+++ trunk/src/account-setup-eplugin/org-gnome-exchange-mapi.eplug.xml Wed Nov 19 04:28:20 2008
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<e-plugin-list>
+ <e-plugin type="shlib" location="@PLUGINDIR@/liborg-gnome-exchange-mapi.so" load-on-startup="true" id="org.gnome.evolution.plugin.exchange-mapi" name="Exchange MAPI">
+
+ <author name="Srinivasa Ragavan" email="sragavan novell com"/>
+ <author name="Johnny Jacob" email="jjohnny novell com"/>
+ <author name="Suman Manjunath" email="msuman novell com"/>
+
+ <description>Exchange MAPI Plugin</description>
+
+ <hook class="org.gnome.evolution.mail.config:1.0">
+ <group
+ target="account"
+ id="org.gnome.evolution.mail.config.accountWizard"
+ check="org_gnome_exchange_mapi_check_options">
+ <item
+ type="item_table"
+ path="10.receive/20.config/30.mapi"
+ factory="org_gnome_exchange_mapi_account_setup"/>
+ </group>
+ </hook>
+ <hook class="org.gnome.evolution.mail.config:1.0">
+ <group
+ target="account"
+ id="org.gnome.evolution.mail.config.accountDruid"
+ check="org_gnome_exchange_mapi_check_options">
+ <item
+ type="item_table"
+ path="10.receive/20.config/30.mapi"
+ factory="org_gnome_exchange_mapi_account_setup"/>
+ </group>
+ </hook>
+ <hook class="org.gnome.evolution.mail.config:1.0">
+ <group
+ target="account"
+ id="org.gnome.evolution.mail.config.accountEditor"
+ check="org_gnome_exchange_mapi_check_options">
+ <item
+ type="item_table"
+ path="10.receive/20.config/30.mapi"
+ factory="org_gnome_exchange_mapi_account_setup"/>
+ </group>
+ </hook>
+ <hook class="org.gnome.evolution.addressbook.config:1.0">
+ <group
+ target="source"
+ id="com.novell.evolution.addressbook.config.accountEditor"
+ check="exchange_mapi_book_check"
+ commit="exchange_mapi_book_commit">
+ <item
+ type="item"
+ path="00.general/10.display/50.createcontacts"
+ factory="exchange_mapi_create"/>
+ </group>
+ </hook>
+ <hook class="org.gnome.evolution.calendar.config:1.0">
+ <group
+ target="source"
+ id="org.gnome.evolution.calendar.calendarProperties"
+ check="exchange_mapi_cal_check"
+ commit="exchange_mapi_cal_commit">
+ <item
+ type="item_table"
+ path="00.general/10.source/40.pcalendar"
+ factory="exchange_mapi_create"/>
+ </group>
+ </hook>
+
+ </e-plugin>
+</e-plugin-list>
Added: trunk/src/addressbook/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/addressbook/Makefile.am Wed Nov 19 04:28:20 2008
@@ -0,0 +1,38 @@
+INCLUDES = \
+ -DG_LOG_DOMAIN=\"libebookbackend\" \
+ -I$(top_srcdir)/src/addressbook \
+ -I$(top_builddir)/src/addressbook \
+ -I$(top_srcdir)/src/libexchangemapi \
+ $(LIBMAPI_CFLAGS) \
+ $(LIBEDATABOOK_CFLAGS) \
+ $(LIBEDATASERVER_CFLAGS) \
+ $(LIBEBACKEND_CFLAGS) \
+ $(EVOLUTION_DATA_SERVER_CFLAGS) \
+ $(EVOLUTION_ADDRESSBOOK_CFLAGS)
+
+extension_LTLIBRARIES = libebookbackendmapi.la
+
+libebookbackendmapi_la_SOURCES = \
+ e-book-backend-mapi.c \
+ e-book-backend-mapi.h \
+ e-book-backend-mapi-factory.c
+
+libebookbackendmapi_la_LIBADD = \
+ $(top_builddir)/src/libexchangemapi/libexchangemapi-1.0.la \
+ $(LIBEDATABOOK_LIBS) \
+ $(LIBEBACKEND_CFLAGS) \
+ $(LIBEDATASERVER_LIBS) \
+ $(EVOLUTION_DATA_SERVER_LIBS) \
+ $(EVOLUTION_ADDRESSBOOK_LIBS) \
+ $(LIBMAPI_LIBS)
+
+libebookbackendmapi_la_LDFLAGS = \
+ -module -avoid-version $(NO_UNDEFINED)
+
+#LDAP_SCHEMA = \
+# evolutionperson.schema
+
+#ldapschemadir = $(privdatadir)
+#ldapschema_DATA= $(LDAP_SCHEMA)
+
+#EXTRA_DIST = $(LDAP_SCHEMA) openldap-extract.h
Added: trunk/src/addressbook/e-book-backend-mapi-factory.c
==============================================================================
--- (empty file)
+++ trunk/src/addressbook/e-book-backend-mapi-factory.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors:
+ * Srinivasa Ragavan <sragavan novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to:
+ * Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libebackend/e-data-server-module.h>
+#include <libedata-book/e-book-backend-factory.h>
+#include "e-book-backend-mapi.h"
+
+E_BOOK_BACKEND_FACTORY_SIMPLE (mapi,
+ MAPI,
+ e_book_backend_mapi_new)
+
+static GType mapi_type;
+
+void eds_module_initialize (GTypeModule *module)
+{
+ mapi_type = _mapi_factory_get_type (module);
+}
+
+void eds_module_shutdown (void)
+{
+}
+
+void eds_module_list_types (const GType **types, int *num_types)
+{
+ *types = &mapi_type;
+ *num_types = 1;
+}
Added: trunk/src/addressbook/e-book-backend-mapi.c
==============================================================================
--- (empty file)
+++ trunk/src/addressbook/e-book-backend-mapi.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,1770 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <glib.h>
+
+#include <sys/time.h>
+/*
+** #include <glib/gi18n-lib.h>
+*/
+
+#include <libedataserver/e-sexp.h>
+#include "libedataserver/e-flag.h"
+#include <libebook/e-contact.h>
+
+#include <libedata-book/e-book-backend-sexp.h>
+#include <libedata-book/e-data-book.h>
+#include <libedata-book/e-data-book-view.h>
+#include <libedata-book/e-book-backend-cache.h>
+#include <libedata-book/e-book-backend-summary.h>
+#include "e-book-backend-mapi.h"
+
+
+static EBookBackendClass *e_book_backend_mapi_parent_class;
+static gboolean enable_debug = TRUE;
+
+struct _EBookBackendMAPIPrivate
+{
+ char *profile;
+ mapi_id_t fid;
+ int mode;
+ gboolean marked_for_offline;
+ gboolean is_cache_ready;
+ gboolean is_summary_ready;
+ gboolean is_writable;
+ char *uri;
+ char *book_name;
+
+ GMutex *lock;
+ char *summary_file_name;
+ EBookBackendSummary *summary;
+ EBookBackendCache *cache;
+
+};
+
+#define LOCK() g_mutex_lock (priv->lock)
+#define UNLOCK() g_mutex_unlock (priv->lock)
+
+#define ELEMENT_TYPE_SIMPLE 0x01
+#define ELEMENT_TYPE_COMPLEX 0x02 /* fields which require explicit functions to set values into EContact and EGwItem */
+
+#define SUMMARY_FLUSH_TIMEOUT 5000
+#define ELEMENT_TYPE_SIMPLE 0x01
+#define ELEMENT_TYPE_COMPLEX 0x02
+
+static EContact * emapidump_contact(struct mapi_SPropValue_array *properties);
+
+static const struct field_element_mapping {
+ EContactField field_id;
+ int element_type;
+ int mapi_id;
+ int contact_type;
+// char *element_name;
+// void (*populate_contact_func)(EContact *contact, gpointer data);
+// void (*set_value_in_gw_item) (EGwItem *item, gpointer data);
+// void (*set_changes) (EGwItem *new_item, EGwItem *old_item);
+
+ } mappings [] = {
+
+ { E_CONTACT_UID, PT_STRING8, 0, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_REV, PT_SYSTIME, PR_LAST_MODIFICATION_TIME, ELEMENT_TYPE_SIMPLE},
+
+ { E_CONTACT_FILE_AS, PT_STRING8, PR_EMS_AB_MANAGER_T, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_FULL_NAME, PT_STRING8, PR_DISPLAY_NAME, ELEMENT_TYPE_SIMPLE },
+ { E_CONTACT_GIVEN_NAME, PT_STRING8, PR_GIVEN_NAME, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_FAMILY_NAME, PT_STRING8, PR_SURNAME , ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_NICKNAME, PT_STRING8, PR_NICKNAME, ELEMENT_TYPE_SIMPLE },
+
+ { E_CONTACT_EMAIL_1, PT_STRING8, 0x8084001e, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_EMAIL_2, PT_STRING8, 0x8094001e, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_EMAIL_3, PT_STRING8, 0x80a4001e, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_IM_AIM, PT_STRING8, 0x8062001e, ELEMENT_TYPE_COMPLEX},
+
+ { E_CONTACT_PHONE_BUSINESS, PT_STRING8, PR_OFFICE_TELEPHONE_NUMBER, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_PHONE_HOME, PT_STRING8, PR_HOME_TELEPHONE_NUMBER, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_PHONE_MOBILE, PT_STRING8, PR_MOBILE_TELEPHONE_NUMBER, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_PHONE_HOME_FAX, PT_STRING8, PR_HOME_FAX_NUMBER ,ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_PHONE_BUSINESS_FAX, PT_STRING8, PR_BUSINESS_FAX_NUMBER,ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_PHONE_PAGER, PT_STRING8, PR_PAGER_TELEPHONE_NUMBER,ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_PHONE_ASSISTANT, PT_STRING8, PR_ASSISTANT_TELEPHONE_NUMBER ,ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_PHONE_COMPANY, PT_STRING8, PR_COMPANY_MAIN_PHONE_NUMBER ,ELEMENT_TYPE_SIMPLE},
+
+ { E_CONTACT_HOMEPAGE_URL, PT_STRING8, 0x802b001e, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_FREEBUSY_URL, PT_STRING8, 0x80d8001e, ELEMENT_TYPE_SIMPLE},
+
+ { E_CONTACT_ROLE, PT_STRING8, PR_PROFESSION, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_TITLE, PT_STRING8, PR_TITLE, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_ORG, PT_STRING8, PR_COMPANY_NAME, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_ORG_UNIT, PT_STRING8, PR_DEPARTMENT_NAME,ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_MANAGER, PT_STRING8, PR_MANAGER_NAME, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_ASSISTANT, PT_STRING8, PR_ASSISTANT, ELEMENT_TYPE_SIMPLE},
+
+ { E_CONTACT_OFFICE, PT_STRING8, PR_OFFICE_LOCATION, ELEMENT_TYPE_SIMPLE},
+ { E_CONTACT_SPOUSE, PT_STRING8, PR_SPOUSE_NAME, ELEMENT_TYPE_SIMPLE},
+
+ { E_CONTACT_BIRTH_DATE, PT_SYSTIME, PR_BIRTHDAY, ELEMENT_TYPE_COMPLEX},
+ { E_CONTACT_ANNIVERSARY, PT_SYSTIME, PR_WEDDING_ANNIVERSARY, ELEMENT_TYPE_COMPLEX},
+
+ { E_CONTACT_NOTE, PT_STRING8, PR_BODY, ELEMENT_TYPE_SIMPLE},
+
+
+ { E_CONTACT_ADDRESS_HOME, PT_STRING8, 0x801a001e, ELEMENT_TYPE_COMPLEX},
+ { E_CONTACT_ADDRESS_WORK, PT_STRING8, 0x801c001e, ELEMENT_TYPE_COMPLEX},
+// { E_CONTACT_BOOK_URI, ELEMENT_TYPE_SIMPLE, "book_uri"}
+// { E_CONTACT_EMAIL, PT_STRING8, 0x8084001e},
+// { E_CONTACT_CATEGORIES, },
+ };
+
+static maplen = G_N_ELEMENTS(mappings);
+
+static EDataBookView *
+find_book_view (EBookBackendMAPI *ebmapi)
+{
+ EList *views = e_book_backend_get_book_views (E_BOOK_BACKEND (ebmapi));
+ EIterator *iter;
+ EDataBookView *rv = NULL;
+
+ if (!views)
+ return NULL;
+
+ iter = e_list_get_iterator (views);
+
+ if (!iter) {
+ g_object_unref (views);
+ return NULL;
+ }
+
+ if (e_iterator_is_valid (iter)) {
+ /* just always use the first book view */
+ EDataBookView *v = (EDataBookView*)e_iterator_get(iter);
+ if (v)
+ rv = v;
+ }
+
+ g_object_unref (iter);
+ g_object_unref (views);
+
+ return rv;
+}
+
+static gboolean
+build_restriction_emails_contains (struct mapi_SRestriction *res,
+ char *query)
+{
+ char *email=NULL, *tmp, *tmp1;
+ int status;
+
+ /* This currently supports "email foo bar soo" */
+ tmp = strdup (query);
+
+ tmp = strstr (tmp, "email");
+ if (tmp ) {
+ tmp = strchr (tmp, '\"');
+ if (tmp && ++tmp) {
+ tmp = strchr (tmp, '\"');
+ if (tmp && ++tmp) {
+ tmp1 = tmp;
+ tmp1 = strchr (tmp1, '\"');
+ if (tmp1) {
+ *tmp1 = 0;
+ email = tmp;
+ }
+ }
+ }
+ }
+
+
+ if (email==NULL || !strchr (email, '@'))
+ return FALSE;
+
+ res->rt = RES_PROPERTY;
+ res->res.resProperty.relop = RES_PROPERTY;
+ res->res.resProperty.ulPropTag = 0x801f001e; /* EMAIL */
+ res->res.resProperty.lpProp.ulPropTag = 0x801f001e; /* EMAIL*/
+ res->res.resProperty.lpProp.value.lpszA = email;
+
+ return TRUE;
+}
+
+static char *
+get_filename_from_uri (const char *uri, const char *file)
+{
+ char *mangled_uri, *filename;
+ int i;
+
+ /* mangle the URI to not contain invalid characters */
+ mangled_uri = g_strdup (uri);
+ for (i = 0; i < strlen (mangled_uri); i++) {
+ switch (mangled_uri[i]) {
+ case ':' :
+ case '/' :
+ mangled_uri[i] = '_';
+ }
+ }
+
+ /* generate the file name */
+ filename = g_build_filename (g_get_home_dir (), ".evolution/cache/addressbook",
+ mangled_uri, file, NULL);
+
+ /* free memory */
+ g_free (mangled_uri);
+
+ return filename;
+}
+
+static GNOME_Evolution_Addressbook_CallStatus
+e_book_backend_mapi_load_source (EBookBackend *backend,
+ ESource *source,
+ gboolean only_if_exists)
+{
+ char *tmp;
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) backend)->priv;
+ char * offline;
+ char **tokens;
+ char *uri;
+ if (enable_debug)
+ printf("MAPI load source\n");
+ offline = e_source_get_property (source, "offline_sync");
+ if (offline && g_str_equal (offline, "1"))
+ priv->marked_for_offline = TRUE;
+
+
+
+ /* Either we are in Online mode or this is marked for offline */
+
+ priv->uri = g_strdup (e_source_get_uri (source));
+
+ tokens = g_strsplit (priv->uri, ";", 2);
+ if (tokens[0])
+ uri = g_strdup (tokens [0]);
+ priv->book_name = g_strdup (tokens[1]);
+ if (priv->book_name == NULL) {
+ g_warning ("Bookname is null for %s\n", uri);
+ return GNOME_Evolution_Addressbook_OtherError;
+ }
+ g_strfreev (tokens);
+
+ if (priv->mode == GNOME_Evolution_Addressbook_MODE_LOCAL &&
+ !priv->marked_for_offline ) {
+ return GNOME_Evolution_Addressbook_OfflineUnavailable;
+ }
+
+ if (priv->marked_for_offline) {
+ priv->summary_file_name = get_filename_from_uri (priv->uri, "cache.summary");
+ if (g_file_test (priv->summary_file_name, G_FILE_TEST_EXISTS)) {
+ printf("Loading the summary\n");
+ priv->summary = e_book_backend_summary_new (priv->summary_file_name,
+ SUMMARY_FLUSH_TIMEOUT);
+ e_book_backend_summary_load (priv->summary);
+ priv->is_summary_ready = TRUE;
+ }
+
+ /* Load the cache as well.*/
+ if (e_book_backend_cache_exists (priv->uri)) {
+ printf("Loading the cache\n");
+ priv->cache = e_book_backend_cache_new (priv->uri);
+ priv->is_cache_ready = TRUE;
+ }
+ //FIXME: We may have to do a time based reload. Or deltas should upload.
+ }
+
+ g_free (uri);
+ e_book_backend_set_is_loaded (E_BOOK_BACKEND (backend), TRUE);
+ e_book_backend_set_is_writable (backend, TRUE);
+ if (priv->mode == GNOME_Evolution_Addressbook_MODE_LOCAL) {
+ e_book_backend_set_is_writable (backend, FALSE);
+ e_book_backend_notify_writable (backend, FALSE);
+ e_book_backend_notify_connection_status (backend, FALSE);
+ if (!priv->cache) {
+ printf("Unfortunately the cache is not yet created\n");
+ return GNOME_Evolution_Addressbook_OfflineUnavailable;
+ }
+ } else {
+ e_book_backend_notify_connection_status (backend, TRUE);
+ }
+
+ priv->profile = g_strdup (e_source_get_property (source, "profile"));
+ exchange_mapi_util_mapi_id_from_string (e_source_get_property (source, "folder-id"), &priv->fid);
+
+ tmp = e_source_get_property (source, "folder-id");
+ printf("Folder is %s %016llX\n", tmp, priv->fid);
+
+ /* Once aunthentication in address book works this can be removed */
+ if (priv->mode == GNOME_Evolution_Addressbook_MODE_LOCAL) {
+ return GNOME_Evolution_Addressbook_Success;
+ }
+
+ // writable property will be set in authenticate_user callback
+ e_book_backend_set_is_loaded (E_BOOK_BACKEND (backend), TRUE);
+ e_book_backend_notify_connection_status (E_BOOK_BACKEND (backend), TRUE);
+
+
+ if (enable_debug)
+ printf("For profile %s and folder %s - %016llX\n", priv->profile, tmp, priv->fid);
+
+ return GNOME_Evolution_Addressbook_Success;
+}
+
+static char *
+e_book_backend_mapi_get_static_capabilities (EBookBackend *backend)
+{
+ if(enable_debug)
+ printf("mapi get_static_capabilities\n");
+ //FIXME: Implement this.
+
+ return g_strdup ("net,bulk-removes,do-initial-query,contact-lists");
+}
+
+gboolean
+mapi_book_build_name_id (struct mapi_nameid *nameid, gpointer data)
+{
+ EContact *contact = data;
+
+ mapi_nameid_lid_add(nameid, 0x8005, PSETID_Address);
+ mapi_nameid_lid_add(nameid, 0x8084, PSETID_Address);
+ mapi_nameid_lid_add(nameid, 0x8083, PSETID_Address);
+
+ mapi_nameid_lid_add(nameid, 0x8093, PSETID_Address);
+ mapi_nameid_lid_add(nameid, 0x80A3, PSETID_Address);
+
+ mapi_nameid_string_add(nameid, "urn:schemas:contacts:fileas", PS_PUBLIC_STRINGS);
+
+ mapi_nameid_lid_add(nameid, 0x802B, PSETID_Address);
+ mapi_nameid_lid_add(nameid, 0x8062, PSETID_Address);
+
+ mapi_nameid_lid_add(nameid, 0x801A, PSETID_Address);
+ mapi_nameid_lid_add(nameid, 0x801B, PSETID_Address);
+
+ mapi_nameid_lid_add(nameid, 0x3A4F, PS_MAPI);
+
+ mapi_nameid_lid_add(nameid, 0x8094, PSETID_Address);
+ mapi_nameid_lid_add(nameid, 0x80A4, PSETID_Address);
+
+ return TRUE;
+}
+
+#define set_str_value(field_id, hex) if (e_contact_get (contact, field_id)) set_SPropValue_proptag (&props[i++], hex, e_contact_get (contact, field_id));
+
+int
+mapi_book_build_props (struct SPropValue ** value, struct SPropTagArray * SPropTagArray, gpointer data)
+{
+ EContact *contact = data;
+ int len = -1;
+ struct SPropValue *props;
+ int i=0;
+
+ for (i=0; i<13; i++)
+ printf("hex %x\n", SPropTagArray->aulPropTag[i]);
+ i=0;
+ props = g_new (struct SPropValue, 50); //FIXME: Correct value tbd
+ set_str_value ( E_CONTACT_FILE_AS, SPropTagArray->aulPropTag[0]);
+
+ set_str_value (E_CONTACT_FULL_NAME, PR_DISPLAY_NAME);
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_CLASS, (const void *)IPM_CONTACT);
+ set_str_value (E_CONTACT_FILE_AS, PR_NORMALIZED_SUBJECT);
+ set_str_value (E_CONTACT_EMAIL_1, SPropTagArray->aulPropTag[1]);
+// set_str_value (E_CONTACT_EMAIL_1, SPropTagArray->aulPropTag[2]);
+ set_str_value (E_CONTACT_FILE_AS, SPropTagArray->aulPropTag[5]);
+
+
+// set_str_value ( E_CONTACT_EMAIL_1, 0x8083001e);
+ set_str_value ( E_CONTACT_EMAIL_2, SPropTagArray->aulPropTag[3]);
+// set_str_value ( E_CONTACT_EMAIL_2, SPropTagArray->aulPropTag[11]);
+
+ set_str_value ( E_CONTACT_EMAIL_3, SPropTagArray->aulPropTag[4]);
+// set_str_value ( E_CONTACT_EMAIL_3, SPropTagArray->aulPropTag[12]);
+
+ set_str_value (E_CONTACT_HOMEPAGE_URL, SPropTagArray->aulPropTag[6]);
+ set_str_value (E_CONTACT_FREEBUSY_URL, 0x812C001E);
+
+
+ set_str_value ( E_CONTACT_PHONE_BUSINESS, PR_OFFICE_TELEPHONE_NUMBER);
+ set_str_value ( E_CONTACT_PHONE_HOME, PR_HOME_TELEPHONE_NUMBER);
+ set_str_value ( E_CONTACT_PHONE_MOBILE, PR_MOBILE_TELEPHONE_NUMBER);
+ set_str_value ( E_CONTACT_PHONE_HOME_FAX, PR_HOME_FAX_NUMBER);
+ set_str_value ( E_CONTACT_PHONE_BUSINESS_FAX, PR_BUSINESS_FAX_NUMBER);
+ set_str_value ( E_CONTACT_PHONE_PAGER, PR_PAGER_TELEPHONE_NUMBER);
+ set_str_value ( E_CONTACT_PHONE_ASSISTANT, PR_ASSISTANT_TELEPHONE_NUMBER);
+ set_str_value ( E_CONTACT_PHONE_COMPANY, PR_COMPANY_MAIN_PHONE_NUMBER);
+
+ set_str_value (E_CONTACT_MANAGER, PR_MANAGER_NAME);
+ set_str_value (E_CONTACT_ASSISTANT, PR_ASSISTANT);
+ set_str_value (E_CONTACT_ORG, PR_COMPANY_NAME);
+ set_str_value (E_CONTACT_ORG_UNIT, PR_DEPARTMENT_NAME);
+ set_str_value (E_CONTACT_ROLE, PR_PROFESSION);
+ set_str_value (E_CONTACT_TITLE, PR_TITLE);
+
+ set_str_value (E_CONTACT_OFFICE, PR_OFFICE_LOCATION);
+ set_str_value (E_CONTACT_SPOUSE, PR_SPOUSE_NAME);
+
+ set_str_value (E_CONTACT_NOTE, PR_BODY);
+
+ //BDAY AND ANNV
+ if (e_contact_get (contact, E_CONTACT_BIRTH_DATE)) {
+ EContactDate *date = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
+ struct tm tmtime;
+ time_t lt;
+ NTTIME nt;
+ struct FILETIME t;
+
+ tmtime.tm_mday = date->day - 1;
+ tmtime.tm_mon = date->month - 1;
+ tmtime.tm_year = date->year - 1900;
+
+ lt = mktime (&tmtime);
+ unix_to_nt_time (&nt, lt);
+ t.dwLowDateTime = (nt << 32) >> 32;
+ t.dwHighDateTime = (nt >> 32);
+ printf("sending bday\n");
+ set_SPropValue_proptag (&props[i++], PR_BIRTHDAY, &t);
+ }
+
+ if (e_contact_get (contact, E_CONTACT_ANNIVERSARY)) {
+ EContactDate *date = e_contact_get (contact, E_CONTACT_ANNIVERSARY);
+ struct tm tmtime;
+ time_t lt;
+ NTTIME nt;
+ struct FILETIME t;
+
+ tmtime.tm_mday = date->day - 1;
+ tmtime.tm_mon = date->month - 1;
+ tmtime.tm_year = date->year - 1900;
+
+ lt = mktime (&tmtime);
+ unix_to_nt_time (&nt, lt);
+ t.dwLowDateTime = (nt << 32) >> 32;
+ t.dwHighDateTime = (nt >> 32);
+ printf("sending wed\n");
+ set_SPropValue_proptag (&props[i++], PR_WEDDING_ANNIVERSARY, &t);
+ }
+ //Home and Office address
+ if (e_contact_get (contact, E_CONTACT_ADDRESS_HOME)) {
+ EContactAddress *contact_addr;
+
+ contact_addr = e_contact_get (contact, E_CONTACT_ADDRESS_HOME);
+ set_SPropValue_proptag (&props[i++], SPropTagArray->aulPropTag[8], contact_addr->street);
+ set_SPropValue_proptag (&props[i++], PR_HOME_ADDRESS_POST_OFFICE_BOX, contact_addr->ext);
+ set_SPropValue_proptag (&props[i++], PR_HOME_ADDRESS_CITY, contact_addr->locality);
+ set_SPropValue_proptag (&props[i++], PR_HOME_ADDRESS_STATE_OR_PROVINCE, contact_addr->region);
+ set_SPropValue_proptag (&props[i++], PR_HOME_ADDRESS_POSTAL_CODE, contact_addr->code);
+ set_SPropValue_proptag (&props[i++], PR_HOME_ADDRESS_COUNTRY, contact_addr->country);
+ }
+
+ if (e_contact_get (contact, E_CONTACT_ADDRESS_WORK)) {
+ EContactAddress *contact_addr;
+
+ contact_addr = e_contact_get (contact, E_CONTACT_ADDRESS_WORK);
+ set_SPropValue_proptag (&props[i++], SPropTagArray->aulPropTag[9], contact_addr->street);
+ set_SPropValue_proptag (&props[i++], PR_POST_OFFICE_BOX, contact_addr->ext);
+ set_SPropValue_proptag (&props[i++], PR_LOCALITY, contact_addr->locality);
+ set_SPropValue_proptag (&props[i++], PR_STATE_OR_PROVINCE, contact_addr->region);
+ set_SPropValue_proptag (&props[i++], PR_POSTAL_CODE, contact_addr->code);
+ set_SPropValue_proptag (&props[i++], PR_COUNTRY, contact_addr->country);
+ }
+
+
+// set_str_value (E_CONTACT_NICKNAME, SPropTagArray->aulPropTag[10]);
+ if (e_contact_get (contact, E_CONTACT_IM_AIM)) {
+ GList *l = e_contact_get (contact, E_CONTACT_IM_AIM);
+ set_SPropValue_proptag (&props[i++], SPropTagArray->aulPropTag[7], l->data);
+ }
+
+ if (e_contact_get (contact, E_CONTACT_NICKNAME)) {
+ char *nick = e_contact_get (contact, E_CONTACT_NICKNAME);
+// set_SPropValue_proptag (&props[i++], SPropTagArray->aulPropTag[10], nick);
+ printf("nickname %s %x\n", nick, SPropTagArray->aulPropTag[10]);
+ }
+
+ *value =props;
+ printf("Sending %d \n", i);
+ return i;
+}
+
+static void
+e_book_backend_mapi_create_contact (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ const char *vcard )
+{
+ EContact *contact;
+ char *id;
+ mapi_id_t status;
+ int element_type;
+ char* value;
+ int i;
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) backend)->priv;
+
+ if(enable_debug)
+ printf("mapi create_contact \n");
+
+ switch (priv->mode) {
+
+ case GNOME_Evolution_Addressbook_MODE_LOCAL :
+ e_data_book_respond_create(book, opid, GNOME_Evolution_Addressbook_RepositoryOffline, NULL);
+ return;
+
+ case GNOME_Evolution_Addressbook_MODE_REMOTE :
+ contact = e_contact_new_from_vcard(vcard);
+ status = exchange_mapi_create_item (olFolderContacts, priv->fid, mapi_book_build_name_id, contact, mapi_book_build_props, contact, NULL, NULL, NULL, 0);
+ if (!status) {
+ e_data_book_respond_create(book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
+ return;
+ }
+ id = exchange_mapi_util_mapi_ids_to_uid (priv->fid, status);
+
+ /* UID of the contact is nothing but the concatenated string of hex id of folder and the message.*/
+ e_contact_set (contact, E_CONTACT_UID, id);
+ e_contact_set (contact, E_CONTACT_BOOK_URI, priv->uri);
+
+ //somehow get the mid.
+ //add to summary and cache.
+ if (priv->marked_for_offline && priv->is_cache_ready)
+ e_book_backend_cache_add_contact (priv->cache, contact);
+
+ if (priv->marked_for_offline && priv->is_summary_ready)
+ e_book_backend_summary_add_contact (priv->summary, contact);
+
+ e_data_book_respond_create(book, opid, GNOME_Evolution_Addressbook_Success, contact);
+ return;
+ }
+
+ return;
+}
+
+static void
+e_book_backend_mapi_remove_contacts (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ GList *id_list)
+{
+ GSList *list=NULL, *tmp = id_list;
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) backend)->priv;
+ mapi_id_t fid, mid;
+
+ if(enable_debug)
+ printf("mapi: remove_contacts\n");
+
+ switch (priv->mode) {
+
+ case GNOME_Evolution_Addressbook_MODE_LOCAL :
+ e_data_book_respond_remove_contacts (book, opid, GNOME_Evolution_Addressbook_RepositoryOffline, NULL);
+ return;
+
+ case GNOME_Evolution_Addressbook_MODE_REMOTE:
+
+ while (tmp) {
+ struct id_list *data = g_new (struct id_list, 1);
+ exchange_mapi_util_mapi_ids_from_uid (tmp->data, &fid, &mid);
+ data->id = mid;
+ list = g_slist_prepend (list, (gpointer) data);
+ tmp = tmp->next;
+ }
+
+ exchange_mapi_remove_items (olFolderContacts, priv->fid, list);
+ if (priv->marked_for_offline && priv->is_cache_ready) {
+ tmp = id_list;
+ while (tmp) {
+ e_book_backend_cache_remove_contact (priv->cache, tmp->data);
+ tmp = tmp->next;
+ }
+ }
+
+ if (priv->marked_for_offline && priv->is_summary_ready) {
+ tmp = id_list;
+ while (tmp) {
+ e_book_backend_summary_remove_contact (priv->summary, tmp->data);
+ tmp = tmp->next;
+ }
+ }
+
+ g_slist_free (list);
+ e_data_book_respond_remove_contacts (book, opid,
+ GNOME_Evolution_Addressbook_Success, id_list);
+ return;
+ default:
+ break;
+ }
+}
+
+static void
+e_book_backend_mapi_modify_contact (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ const char *vcard)
+{
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) backend)->priv;
+ EContact *contact;
+ mapi_id_t fid, mid;
+ gboolean status;
+ char *tmp, *id;
+ GList *l = NULL;
+
+ if(enable_debug)
+ printf("mapi: modify_contacts\n");
+
+ switch (priv->mode) {
+
+ case GNOME_Evolution_Addressbook_MODE_LOCAL :
+ e_data_book_respond_modify(book, opid, GNOME_Evolution_Addressbook_RepositoryOffline, NULL);
+ return;
+ case GNOME_Evolution_Addressbook_MODE_REMOTE :
+ contact = e_contact_new_from_vcard(vcard);
+ tmp = e_contact_get (contact, E_CONTACT_UID);
+ exchange_mapi_util_mapi_ids_from_uid (tmp, &fid, &mid);
+ printf("modify id %s\n", tmp);
+
+ status = exchange_mapi_modify_item (olFolderContacts, priv->fid, mid, mapi_book_build_name_id, contact, mapi_book_build_props, contact, NULL, NULL, NULL, 0);
+ printf("getting %016llX\n", status);
+ if (!status) {
+ e_data_book_respond_modify(book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
+ return;
+ }
+
+ e_contact_set (contact, E_CONTACT_BOOK_URI, priv->uri);
+
+ //FIXME: Write it cleanly
+ if (priv->marked_for_offline && priv->is_cache_ready)
+ printf("delete cache %d\n", e_book_backend_cache_remove_contact (priv->cache, tmp));
+
+ if (priv->marked_for_offline && priv->is_summary_ready)
+ e_book_backend_summary_remove_contact (priv->summary, tmp);
+
+ if (priv->marked_for_offline && priv->is_cache_ready)
+ e_book_backend_cache_add_contact (priv->cache, contact);
+
+ if (priv->marked_for_offline && priv->is_summary_ready)
+ e_book_backend_summary_add_contact (priv->summary, contact);
+
+
+ e_data_book_respond_modify (book, opid, GNOME_Evolution_Addressbook_Success, contact);
+
+
+ }
+}
+
+static gboolean
+create_contact_item (FetchItemsCallbackData *item_data, gpointer data)
+{
+ EContact *contact;
+ char *suid;
+
+ contact = emapidump_contact (item_data->properties);
+ suid = exchange_mapi_util_mapi_ids_to_uid (item_data->fid, item_data->mid);
+ printf("got contact %s\n", suid);
+ if (contact) {
+ /* UID of the contact is nothing but the concatenated string of hex id of folder and the message.*/
+ e_contact_set (contact, E_CONTACT_UID, suid);
+ data = contact;
+ }
+
+ g_free (suid);
+
+ return TRUE;
+}
+
+static void
+e_book_backend_mapi_get_contact (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ const char *id)
+{
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) backend)->priv;
+ EContact *contact;
+ char *vcard;
+
+ if (enable_debug)
+ printf("mapi: get_contact %s\n", id);
+
+ switch (priv->mode) {
+
+ case GNOME_Evolution_Addressbook_MODE_LOCAL:
+ contact = e_book_backend_cache_get_contact (priv->cache,
+ id);
+ if (contact) {
+ vcard = e_vcard_to_string (E_VCARD (contact),
+ EVC_FORMAT_VCARD_30);
+ e_data_book_respond_get_contact (book,
+ opid,
+ GNOME_Evolution_Addressbook_Success,
+ vcard);
+ g_free (vcard);
+ g_object_unref (contact);
+ return;
+ }
+ else {
+ e_data_book_respond_get_contact (book, opid, GNOME_Evolution_Addressbook_ContactNotFound, "");
+ return;
+ }
+
+ case GNOME_Evolution_Addressbook_MODE_REMOTE:
+
+ if (priv->marked_for_offline && e_book_backend_cache_is_populated (priv->cache)) {
+ contact = e_book_backend_cache_get_contact (priv->cache,
+ id);
+ if (contact) {
+ vcard = e_vcard_to_string (E_VCARD (contact),
+ EVC_FORMAT_VCARD_30);
+ e_data_book_respond_get_contact (book,
+ opid,
+ GNOME_Evolution_Addressbook_Success,
+ vcard);
+ g_free (vcard);
+ g_object_unref (contact);
+ return;
+ }
+ else {
+ e_data_book_respond_get_contact (book, opid, GNOME_Evolution_Addressbook_ContactNotFound, "");
+ return;
+ }
+
+ } else {
+ mapi_id_t fid, mid;
+
+ exchange_mapi_util_mapi_ids_from_uid (id, &fid, &mid);
+ exchange_mapi_connection_fetch_item (priv->fid, mid,
+ NULL, 0,
+ NULL, NULL,
+ create_contact_item, contact,
+ MAPI_OPTIONS_FETCH_ALL);
+
+ if (contact) {
+ e_contact_set (contact, E_CONTACT_BOOK_URI, priv->uri);
+ vcard = e_vcard_to_string (E_VCARD (contact),
+ EVC_FORMAT_VCARD_30);
+ e_data_book_respond_get_contact (book,
+ opid,
+ GNOME_Evolution_Addressbook_Success,
+ vcard);
+ g_free (vcard);
+ g_object_unref (contact);
+ return;
+
+ } else {
+ e_data_book_respond_get_contact (book, opid, GNOME_Evolution_Addressbook_ContactNotFound, "");
+ return;
+ }
+ }
+
+ default:
+ break;
+ }
+
+ return;
+
+}
+
+static gboolean
+create_contact_list_cb (struct mapi_SPropValue_array *array, const mapi_id_t fid, const mapi_id_t mid,
+ GSList *streams, GSList *recipients, GSList *attachments, gpointer data)
+{
+ GList *list = * (GList **) data;
+ EContact *contact;
+ char *suid;
+
+ contact = emapidump_contact (array);
+ suid = exchange_mapi_util_mapi_ids_to_uid (fid, mid);
+
+ if (contact) {
+ /* UID of the contact is nothing but the concatenated string of hex id of folder and the message.*/
+ printf("Contact added %s\n", suid);
+ e_contact_set (contact, E_CONTACT_UID, suid);
+// e_contact_set (contact, E_CONTACT_BOOK_URI, priv->uri);
+ //FIXME: Should we set this? How can we get this first?
+ list = g_list_prepend (list, e_vcard_to_string (E_VCARD (contact),
+ EVC_FORMAT_VCARD_30));
+ g_object_unref (contact);
+ if (* (GList **)data == NULL)
+ * (GList **)data = list;
+ }
+
+ g_free (suid);
+ return TRUE;
+}
+
+static const uint32_t GetPropsList[] = {
+ PR_FID,
+ PR_MID,
+ PR_INST_ID,
+ PR_INSTANCE_NUM,
+ PR_SUBJECT,
+ PR_MESSAGE_CLASS,
+ PR_HASATTACH,
+/* FIXME: is this tag fit to check if a recipient table exists or not ? */
+// PR_DISCLOSURE_OF_RECIPIENTS,
+ PR_RULE_MSG_PROVIDER,
+ PR_RULE_MSG_NAME
+};
+static const uint16_t n_GetPropsList = G_N_ELEMENTS (GetPropsList);
+
+gboolean
+mapi_book_build_name_id_for_getprops (struct mapi_nameid *nameid, gpointer data)
+{
+ mapi_nameid_lid_add(nameid, 0x8084, PSETID_Address); /* PT_STRING8 - EmailOriginalDisplayName */
+// mapi_nameid_lid_add(nameid, 0x8020, PSETID_Address);
+// mapi_nameid_lid_add(nameid, 0x8021, PSETID_Address);
+
+ return TRUE;
+}
+
+static void
+e_book_backend_mapi_get_contact_list (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ const char *query )
+{
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) backend)->priv;
+
+ printf("mapi: get contact list %s\n", query);
+ switch (priv->mode) {
+ case GNOME_Evolution_Addressbook_MODE_LOCAL:
+ if (priv->marked_for_offline && priv->cache) {
+ GList *contacts;
+ GList *vcard_strings = NULL;
+ GList *l;
+
+ contacts = e_book_backend_cache_get_contacts (priv->cache, query);
+
+ for (l = contacts; l; l = g_list_next (l)) {
+ EContact *contact = l->data;
+ vcard_strings = g_list_prepend (vcard_strings, e_vcard_to_string (E_VCARD (contact),
+ EVC_FORMAT_VCARD_30));
+ g_object_unref (contact);
+ }
+
+ g_list_free (contacts);
+ printf("get_contact_list in %s returning %d contacts\n", priv->uri, g_list_length (vcard_strings));
+ e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_Success, vcard_strings);
+ return;
+ }
+ e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_RepositoryOffline,
+ NULL);
+ return;
+
+ case GNOME_Evolution_Addressbook_MODE_REMOTE:
+ printf("Mode : Remote\n");
+ if (priv->marked_for_offline && priv->cache) {
+ GList *contacts;
+ GList *vcard_strings = NULL;
+ GList *l;
+
+ contacts = e_book_backend_cache_get_contacts (priv->cache, query);
+
+ for (l = contacts; l ;l = g_list_next (l)) {
+ EContact *contact = l->data;
+ vcard_strings = g_list_prepend (vcard_strings, e_vcard_to_string (E_VCARD (contact),
+ EVC_FORMAT_VCARD_30));
+ g_object_unref (contact);
+ }
+
+ g_list_free (contacts);
+ printf("get_contact_list in %s returning %d contacts\n", priv->uri, g_list_length (vcard_strings));
+ e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_Success, vcard_strings);
+ return ;
+ }
+ else {
+ struct mapi_SRestriction res;
+ GList *vcard_str = NULL;
+
+ printf("Not marked for cache\n");
+
+ /* Unfortunately MAPI Doesn't support searching well, we do allow only online search for emails rest all are returned as error. */
+ if (!build_restriction_emails_contains (&res, query)) {
+ e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
+ return ;
+ }
+
+ if (!exchange_mapi_connection_fetch_items (priv->fid, &res,
+ GetPropsList, n_GetPropsList,
+ mapi_book_build_name_id_for_getprops, NULL,
+ create_contact_list_cb, &vcard_str,
+ MAPI_OPTIONS_FETCH_ALL)) {
+ e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
+ return ;
+ }
+ printf("get_contact_list in %s returning %d contacts\n", priv->uri, g_list_length (vcard_str));
+ e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_Success, vcard_str);
+ return ;
+
+ }
+ }
+}
+
+typedef struct {
+ EBookBackendMAPI *bg;
+ GThread *thread;
+ EFlag *running;
+} BESearchClosure;
+
+static void
+closure_destroy (BESearchClosure *closure)
+{
+ e_flag_free (closure->running);
+ g_free (closure);
+}
+
+static BESearchClosure*
+init_closure (EDataBookView *book_view, EBookBackendMAPI *bg)
+{
+ BESearchClosure *closure = g_new (BESearchClosure, 1);
+
+ closure->bg = bg;
+ closure->thread = NULL;
+ closure->running = e_flag_new ();
+
+ g_object_set_data_full (G_OBJECT (book_view), "closure",
+ closure, (GDestroyNotify)closure_destroy);
+
+ return closure;
+}
+
+static BESearchClosure*
+get_closure (EDataBookView *book_view)
+{
+ return g_object_get_data (G_OBJECT (book_view), "closure");
+}
+
+static void
+mapi_dump_props (struct mapi_SPropValue_array *properties)
+{
+ int i;
+
+ for (i = 0; i < properties->cValues; i++) {
+ struct mapi_SPropValue *lpProp = &properties->lpProps[i];
+ const char *tmp = get_proptag_name (lpProp->ulPropTag);
+ if (tmp && *tmp)
+ printf("%s \t",tmp);
+ else
+ printf("%x \t", lpProp->ulPropTag);
+ switch(lpProp->ulPropTag & 0xFFFF) {
+ case PT_BOOLEAN:
+ printf(" (bool) - %d\n", lpProp->value.b);
+ break;
+ case PT_I2:
+ printf(" (uint16_t) - %d\n", lpProp->value.i);
+ break;
+ case PT_LONG:
+ printf(" (long) - %ld\n", lpProp->value.l);
+ break;
+ case PT_DOUBLE:
+ printf (" (double) - %lf\n", lpProp->value.dbl);
+ break;
+ case PT_I8:
+ printf (" (int) - %d\n", lpProp->value.d);
+ break;
+ case PT_SYSTIME:
+ printf (" (struct FILETIME *) - %p\n", &lpProp->value.ft);
+ break;
+ case PT_ERROR:
+ printf (" (error) - %p\n", lpProp->value.err);
+ break;
+ case PT_STRING8:
+ printf(" (string) - %s\n", lpProp->value.lpszA ? lpProp->value.lpszA : "null" );
+ break;
+ case PT_UNICODE:
+ printf(" (unicodestring) - %s\n", lpProp->value.lpszW ? lpProp->value.lpszW : "null");
+ break;
+ case PT_BINARY:
+ printf(" (struct SBinary_short *) - %p\n", &lpProp->value.bin);
+ break;
+ case PT_MV_STRING8:
+ printf(" (struct mapi_SLPSTRArray *) - %p\n", &lpProp->value.MVszA);
+ break;
+ default:
+ printf(" - NONE NULL\n");
+ }
+
+ }
+
+}
+//FIXME: Be more clever in dumping contacts. Can we have a callback mechanism for each types?
+static EContact *
+emapidump_contact(struct mapi_SPropValue_array *properties)
+{
+ EContact *contact = e_contact_new ();
+ int i;
+
+// mapi_dump_props (properties);
+ for (i=1; i<maplen; i++) {
+ gpointer value;
+
+ value = find_mapi_SPropValue_data (properties, mappings[i].mapi_id);
+ if (mappings[i].element_type == PT_STRING8 && mappings[i].contact_type == ELEMENT_TYPE_SIMPLE) {
+ if (value)
+ e_contact_set (contact, mappings[i].field_id, value);
+ } else if (mappings[i].contact_type == ELEMENT_TYPE_SIMPLE) {
+ if (value && mappings[i].element_type == PT_SYSTIME) {
+ struct FILETIME *t = value;
+ time_t time;
+ NTTIME nt;
+ char *tmp;
+ nt = t->dwHighDateTime;
+ nt = nt << 32;
+ nt |= t->dwLowDateTime;
+ time = nt_time_to_unix (nt);
+ tmp = ctime (&time);
+ e_contact_set (contact, mappings[i].field_id, tmp);
+ //g_free (tmp);
+ } else
+ printf("Nothing is printed\n");
+ } else if (mappings[i].contact_type == ELEMENT_TYPE_COMPLEX) {
+ if (mappings[i].field_id == E_CONTACT_IM_AIM) {
+ GList *list = NULL;
+ EVCardAttribute *attr;
+
+ attr = e_vcard_attribute_new ("", e_contact_vcard_attribute(E_CONTACT_IM_AIM));
+// e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_TYPE), "AIM");
+ e_vcard_attribute_add_value (attr, value);
+ list = g_list_append (list, value);
+ //printf("%s -----\n", value);
+ e_contact_set (contact, mappings[i].field_id, list);
+ //FIXME: FREE them
+ } else if (mappings[i].field_id == E_CONTACT_BIRTH_DATE
+ || mappings[i].field_id == E_CONTACT_ANNIVERSARY) {
+ struct FILETIME *t = value;
+ time_t time;
+ NTTIME nt;
+ struct tm * tmtime;
+ if (value) {
+ EContactDate *date = g_new (EContactDate, 1);
+ nt = t->dwHighDateTime;
+ nt = nt << 32;
+ nt |= t->dwLowDateTime;
+ time = nt_time_to_unix (nt);
+ tmtime = gmtime (&time);
+ //FIXME: Move to new libmapi api to get string dates.
+ date->day = tmtime->tm_mday + 1;
+ date->month = tmtime->tm_mon + 1;
+ date->year = tmtime->tm_year + 1900;
+ e_contact_set (contact, mappings[i].field_id, date);
+
+ }
+
+ } else if (mappings[i].field_id == E_CONTACT_ADDRESS_WORK
+ || mappings[i].field_id == E_CONTACT_ADDRESS_HOME) {
+ EContactAddress *contact_addr;
+
+ contact_addr = g_new0(EContactAddress, 1);
+ if (mappings[i].field_id == E_CONTACT_ADDRESS_HOME) {
+ contact_addr->address_format = NULL;
+ contact_addr->po = NULL;
+ contact_addr->street = value;
+ contact_addr->ext = find_mapi_SPropValue_data (properties, PR_HOME_ADDRESS_POST_OFFICE_BOX);
+ contact_addr->locality = find_mapi_SPropValue_data (properties, PR_HOME_ADDRESS_CITY);
+ contact_addr->region = find_mapi_SPropValue_data (properties, PR_HOME_ADDRESS_STATE_OR_PROVINCE);
+ contact_addr->code = find_mapi_SPropValue_data (properties, PR_HOME_ADDRESS_POSTAL_CODE);
+ contact_addr->country = find_mapi_SPropValue_data (properties, PR_HOME_ADDRESS_COUNTRY);
+
+ } else {
+
+ contact_addr->address_format = NULL;
+ contact_addr->po = NULL;
+ contact_addr->street = value;
+ contact_addr->ext = find_mapi_SPropValue_data (properties, PR_POST_OFFICE_BOX);
+ contact_addr->locality = find_mapi_SPropValue_data (properties, PR_LOCALITY);
+ contact_addr->region = find_mapi_SPropValue_data (properties, PR_STATE_OR_PROVINCE);
+ contact_addr->code = find_mapi_SPropValue_data (properties, PR_POSTAL_CODE);
+ contact_addr->country = find_mapi_SPropValue_data (properties, PR_COUNTRY);
+ }
+ e_contact_set (contact, mappings[i].field_id, contact_addr);
+ //FIXME: Free everything.
+
+ }
+
+
+ }
+ }
+
+ return contact;
+}
+
+static void
+get_contacts_from_cache (EBookBackendMAPI *ebmapi,
+ const char *query,
+ GPtrArray *ids,
+ EDataBookView *book_view,
+ BESearchClosure *closure)
+{
+ int i;
+
+ if (enable_debug)
+ printf ("\nread contacts from cache for the ids found in summary\n");
+ for (i = 0; i < ids->len; i ++) {
+ char *uid;
+ EContact *contact;
+
+ if (!e_flag_is_set (closure->running))
+ break;
+
+ uid = g_ptr_array_index (ids, i);
+ contact = e_book_backend_cache_get_contact (ebmapi->priv->cache, uid);
+ if (contact) {
+ e_data_book_view_notify_update (book_view, contact);
+ g_object_unref (contact);
+ }
+ }
+ if (e_flag_is_set (closure->running))
+ e_data_book_view_notify_complete (book_view,
+ GNOME_Evolution_Addressbook_Success);
+}
+
+static gboolean
+create_contact_cb (FetchItemsCallbackData *item_data, gpointer data)
+{
+ EDataBookView *book_view = data;
+ BESearchClosure *closure = get_closure (book_view);
+ EBookBackendMAPI *be = closure->bg;
+ EContact *contact;
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) be)->priv;
+ char *suid;
+
+ if (!e_flag_is_set (closure->running)) {
+ printf("Might be that the operation is cancelled. Lets ask our parent also to do.\n");
+ return FALSE;
+ }
+
+ contact = emapidump_contact (item_data->properties);
+ suid = exchange_mapi_util_mapi_ids_to_uid (item_data->fid, item_data->mid);
+
+ if (contact) {
+ /* UID of the contact is nothing but the concatenated string of hex id of folder and the message.*/
+ e_contact_set (contact, E_CONTACT_UID, suid);
+ e_contact_set (contact, E_CONTACT_BOOK_URI, priv->uri);
+ e_data_book_view_notify_update (book_view, contact);
+ g_object_unref(contact);
+ }
+
+ g_free (suid);
+ return TRUE;
+}
+
+static void
+book_view_thread (gpointer data)
+{
+ EDataBookView *book_view = data;
+ BESearchClosure *closure = get_closure (book_view);
+ EBookBackend *backend = closure->bg;
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) backend)->priv;
+ const char *query = NULL;
+ GPtrArray *ids = NULL;
+ GList *contacts = NULL, *temp_list = NULL;
+
+ if (enable_debug)
+ printf("mapi: book view\n");
+
+ bonobo_object_ref (book_view);
+ e_flag_set (closure->running);
+
+ e_data_book_view_notify_status_message (book_view, "Searching...");
+ query = e_data_book_view_get_card_query (book_view);
+
+ switch (priv->mode) {
+
+ case GNOME_Evolution_Addressbook_MODE_LOCAL:
+ if (!priv->marked_for_offline) {
+ e_data_book_view_notify_complete (book_view,
+ GNOME_Evolution_Addressbook_OfflineUnavailable);
+ bonobo_object_unref (book_view);
+ return;
+ }
+ if (!priv->cache) {
+ printf("The cache is not yet built\n");
+ e_data_book_view_notify_complete (book_view,
+ GNOME_Evolution_Addressbook_Success);
+ return;
+ }
+
+ if (priv->is_summary_ready &&
+ e_book_backend_summary_is_summary_query (priv->summary, query)) {
+ if (enable_debug)
+ printf ("reading the contacts from summary \n");
+ ids = e_book_backend_summary_search (priv->summary, query);
+ if (ids && ids->len > 0) {
+ get_contacts_from_cache (backend, query, ids, book_view, closure);
+ g_ptr_array_free (ids, TRUE);
+ }
+ bonobo_object_unref (book_view);
+ return;
+ }
+
+ /* fall back to cache */
+ if (enable_debug)
+ printf ("summary not found or a summary query reading the contacts from cache %s\n", query);
+
+ contacts = e_book_backend_cache_get_contacts (priv->cache,
+ query);
+ temp_list = contacts;
+ for (; contacts != NULL; contacts = g_list_next(contacts)) {
+ if (!e_flag_is_set (closure->running)) {
+ for (;contacts != NULL; contacts = g_list_next (contacts))
+ g_object_unref (contacts->data);
+ break;
+ }
+ e_data_book_view_notify_update (book_view,
+ E_CONTACT(contacts->data));
+ g_object_unref (contacts->data);
+ }
+ if (e_flag_is_set (closure->running))
+ e_data_book_view_notify_complete (book_view,
+ GNOME_Evolution_Addressbook_Success);
+ if (temp_list)
+ g_list_free (temp_list);
+ bonobo_object_unref (book_view);
+ return;
+
+ case GNOME_Evolution_Addressbook_MODE_REMOTE:
+
+ if (!exchange_mapi_connection_exists ()) {
+ e_book_backend_notify_auth_required (backend);
+ e_data_book_view_notify_complete (book_view,
+ GNOME_Evolution_Addressbook_AuthenticationRequired);
+ bonobo_object_unref (book_view);
+ return;
+ }
+
+
+ if (priv->marked_for_offline && priv->cache && priv->is_cache_ready) {
+ if (priv->is_summary_ready &&
+ e_book_backend_summary_is_summary_query (priv->summary, query)) {
+ if (enable_debug)
+ printf ("reading the contacts from summary \n");
+ ids = e_book_backend_summary_search (priv->summary, query);
+ if (ids && ids->len > 0) {
+ get_contacts_from_cache (backend, query, ids, book_view, closure);
+ g_ptr_array_free (ids, TRUE);
+ }
+ bonobo_object_unref (book_view);
+ return;
+ }
+
+ printf("Summary seems to be not there or not a summary query, lets fetch from cache directly\n");
+
+ /* We are already cached. Lets return from there. */
+ contacts = e_book_backend_cache_get_contacts (priv->cache,
+ query);
+ temp_list = contacts;
+ for (; contacts != NULL; contacts = g_list_next(contacts)) {
+ if (!e_flag_is_set (closure->running)) {
+ for (;contacts != NULL; contacts = g_list_next (contacts))
+ g_object_unref (contacts->data);
+ break;
+ }
+ e_data_book_view_notify_update (book_view,
+ E_CONTACT(contacts->data));
+ g_object_unref (contacts->data);
+ }
+ if (e_flag_is_set (closure->running))
+ e_data_book_view_notify_complete (book_view,
+ GNOME_Evolution_Addressbook_Success);
+ if (temp_list)
+ g_list_free (temp_list);
+ bonobo_object_unref (book_view);
+ return;
+ }
+
+ //FIXME: We need to fetch only the query from the server live and not everything.
+ /* execute the query */
+ if (!exchange_mapi_connection_fetch_items (priv->fid, NULL,
+ NULL, 0,
+ NULL, NULL,
+ create_contact_cb, book_view,
+ MAPI_OPTIONS_FETCH_ALL)) {
+ if (e_flag_is_set (closure->running))
+ e_data_book_view_notify_complete (book_view,
+ GNOME_Evolution_Addressbook_OtherError);
+ bonobo_object_unref (book_view);
+ return;
+ }
+
+ if (e_flag_is_set (closure->running))
+ e_data_book_view_notify_complete (book_view,
+ GNOME_Evolution_Addressbook_Success);
+ bonobo_object_unref (book_view);
+
+
+
+ default:
+ break;
+ }
+
+ return;
+
+
+}
+
+static void
+e_book_backend_mapi_start_book_view (EBookBackend *backend,
+ EDataBookView *book_view)
+{
+ BESearchClosure *closure = init_closure (book_view, backend);
+
+ if (enable_debug)
+ printf ("mapi: start_book_view...\n");
+ closure->thread = g_thread_create (book_view_thread, book_view, FALSE, NULL);
+ e_flag_wait (closure->running);
+
+ /* at this point we know the book view thread is actually running */
+}
+
+static void
+e_book_backend_mapi_stop_book_view (EBookBackend *backend,
+ EDataBookView *book_view)
+{
+ if(enable_debug)
+ printf("mapi: stop book view\n");
+ /* FIXME : provide implmentation */
+}
+
+static void
+e_book_backend_mapi_get_changes (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ const char *change_id )
+{
+ if(enable_debug)
+ printf("mapi: get changes\n");
+ /* FIXME : provide implmentation */
+}
+
+static gboolean
+cache_contact_cb (FetchItemsCallbackData *item_data, gpointer data)
+{
+ EBookBackendMAPI *be = data;
+ EContact *contact;
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) be)->priv;
+ char *suid;
+
+ contact = emapidump_contact (item_data->properties);
+ suid = exchange_mapi_util_mapi_ids_to_uid (item_data->fid, item_data->mid);
+
+ if (contact) {
+ /* UID of the contact is nothing but the concatenated string of hex id of folder and the message.*/
+ e_contact_set (contact, E_CONTACT_UID, suid);
+ e_contact_set (contact, E_CONTACT_BOOK_URI, priv->uri);
+ e_book_backend_cache_add_contact (priv->cache, contact);
+ e_book_backend_summary_add_contact (priv->summary, contact);
+ g_object_unref(contact);
+ }
+
+ g_free (suid);
+ return TRUE;
+}
+
+static gpointer
+build_cache (EBookBackendMAPI *ebmapi)
+{
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) ebmapi)->priv;
+ char *tmp;
+
+ //FIXME: What if book view is NULL? Can it be? Check that.
+ if (!priv->cache) {
+ printf("Caching for the first time\n");
+ priv->cache = e_book_backend_cache_new (priv->uri);
+ }
+
+ if (!priv->summary) {
+ priv->summary = e_book_backend_summary_new (priv->summary_file_name,
+ SUMMARY_FLUSH_TIMEOUT);
+ printf("Summary file name is %s\n", priv->summary_file_name);
+ }
+
+ e_file_cache_freeze_changes (E_FILE_CACHE (priv->cache));
+
+ if (!exchange_mapi_connection_fetch_items (priv->fid, NULL,
+ NULL, 0,
+ NULL, NULL,
+ cache_contact_cb, ebmapi,
+ MAPI_OPTIONS_FETCH_ALL)) {
+ printf("Error during caching addressbook\n");
+ e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+ return NULL;
+ }
+ tmp = g_strdup_printf("%d", (int)time (NULL));
+ e_book_backend_cache_set_time (priv->cache, tmp);
+ printf("setting time %s\n", tmp);
+ g_free (tmp);
+ e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+ e_book_backend_summary_save (priv->summary);
+ priv->is_cache_ready = TRUE;
+ priv->is_summary_ready = TRUE;
+ return NULL;
+}
+
+static gpointer
+update_cache (EBookBackendMAPI *ebmapi)
+{
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) ebmapi)->priv;
+ char *tmp = e_book_backend_cache_get_time (priv->cache);
+ //FIXME: What if book view is NULL? Can it be? Check that.
+ time_t t=0;
+ struct mapi_SRestriction res;
+
+ if (tmp)
+ t = atoi (tmp);
+
+
+
+// res.rt = RES_PROPERTY;
+// res.res.resProperty.relop = RES_PROPERTY;
+// res.res.resProperty.ulPropTag = PR_LAST_MODIFICATION_TIME;
+// res.res.resProperty.lpProp.ulPropTag = PR_LAST_MODIFICATION_TIME;
+// res.res.resProperty.lpProp.value.lpszA = email;
+
+#if 0
+ printf("time updated was %d\n", t);
+ /* Assume the cache and summary are already there */
+
+ e_file_cache_freeze_changes (E_FILE_CACHE (priv->cache));
+
+ if (!exchange_mapi_connection_fetch_items ( priv->fid, &res,
+ NULL, 0,
+ NULL, NULL,
+ cache_contact_cb, ebmapi,
+ MAPI_OPTIONS_FETCH_ALL)) {
+ printf("Error during caching addressbook\n");
+ e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+ return NULL;
+ }
+ e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+ e_book_backend_summary_save (priv->summary);
+ priv->is_cache_ready = TRUE;
+ priv->is_summary_ready = TRUE;
+#endif
+
+ return NULL;
+}
+
+static void
+e_book_backend_mapi_authenticate_user (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ const char *user,
+ const char *passwd,
+ const char *auth_method)
+{
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) backend)->priv;
+
+ if (enable_debug) {
+ printf ("mapi: authenticate user\n");
+ }
+
+
+ switch (priv->mode) {
+ case GNOME_Evolution_Addressbook_MODE_LOCAL:
+ e_book_backend_notify_writable (backend, FALSE);
+ e_book_backend_notify_connection_status (backend, FALSE);
+ e_data_book_respond_authenticate_user (book, opid, GNOME_Evolution_Addressbook_Success);
+ return;
+
+ case GNOME_Evolution_Addressbook_MODE_REMOTE:
+
+ if (!exchange_mapi_connection_new (priv->profile, NULL))
+ return e_data_book_respond_authenticate_user (book, opid,GNOME_Evolution_Addressbook_OtherError);
+
+ if (priv->cache && priv->is_cache_ready) {
+ printf("FIXME: Should check for an update in the cache\n");
+// g_thread_create ((GThreadFunc) update_cache,
+ // backend, FALSE, backend);
+ } else if (priv->marked_for_offline && !priv->is_cache_ready) {
+ /* Means we dont have a cache. Lets build that first */
+ printf("Preparing to build cache\n");
+ g_thread_create ((GThreadFunc) build_cache, backend, FALSE, backend);
+ }
+ e_book_backend_set_is_writable (backend, TRUE);
+ e_data_book_respond_authenticate_user (book, opid, GNOME_Evolution_Addressbook_Success);
+ return;
+
+ default :
+ break;
+ }
+}
+
+static void
+e_book_backend_mapi_get_required_fields (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid)
+{
+ GList *fields = NULL;
+
+ if (enable_debug)
+ printf ("mapi get_required_fields...\n");
+
+ fields = g_list_append (fields, (char *)e_contact_field_name (E_CONTACT_FILE_AS));
+ e_data_book_respond_get_supported_fields (book, opid,
+ GNOME_Evolution_Addressbook_Success,
+ fields);
+ g_list_free (fields);
+}
+
+static void
+e_book_backend_mapi_get_supported_fields (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid)
+{
+ GList *fields = NULL;
+ int i;
+
+ if (enable_debug)
+ printf ("mapi get_supported_fields...\n");
+
+ for (i=0; i<maplen; i++)
+ {
+ fields = g_list_append (fields, (char *)e_contact_field_name (mappings[i].field_id));
+ }
+ fields = g_list_append (fields, g_strdup (e_contact_field_name (E_CONTACT_BOOK_URI)));
+
+ e_data_book_respond_get_supported_fields (book, opid,
+ GNOME_Evolution_Addressbook_Success,
+ fields);
+ g_list_free (fields);
+
+}
+
+static void
+e_book_backend_mapi_get_supported_auth_methods (EBookBackend *backend, EDataBook *book, guint32 opid)
+{
+ GList *auth_methods = NULL;
+ char *auth_method;
+
+ if (enable_debug)
+ printf ("mapi get_supported_auth_methods...\n");
+
+ auth_method = g_strdup_printf ("plain/password");
+ auth_methods = g_list_append (auth_methods, auth_method);
+ e_data_book_respond_get_supported_auth_methods (book,
+ opid,
+ GNOME_Evolution_Addressbook_Success,
+ auth_methods);
+ g_free (auth_method);
+ g_list_free (auth_methods);
+}
+
+
+static GNOME_Evolution_Addressbook_CallStatus
+e_book_backend_mapi_cancel_operation (EBookBackend *backend, EDataBook *book)
+{
+ if (enable_debug)
+ printf ("mapi cancel_operation...\n");
+ return GNOME_Evolution_Addressbook_CouldNotCancel;
+}
+
+
+static void
+e_book_backend_mapi_remove (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid)
+{
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) backend)->priv;
+ char *cache_uri = NULL;
+ gboolean status;
+
+ if(enable_debug)
+ printf("mapi: remove\n");
+
+ switch (priv->mode) {
+
+ case GNOME_Evolution_Addressbook_MODE_LOCAL:
+ e_data_book_respond_remove (book, opid, GNOME_Evolution_Addressbook_OfflineUnavailable);
+ return;
+
+ case GNOME_Evolution_Addressbook_MODE_REMOTE:
+
+ status = exchange_mapi_remove_folder (olFolderContacts, priv->fid);
+ if (!status) {
+ e_data_book_respond_remove (book, opid, GNOME_Evolution_Addressbook_OtherError);
+ return;
+ }
+
+ if (priv->marked_for_offline && priv->is_summary_ready) {
+ g_object_unref (priv->summary);
+ priv->summary = NULL;
+ }
+
+ if (e_book_backend_cache_exists (priv->uri)) {
+
+ g_object_unref (priv->cache);
+ priv->cache= NULL;
+
+ }
+
+ /* Remove the summary and cache independent of whether they are loaded or not. */
+ cache_uri = get_filename_from_uri (priv->uri, "cache.summary");
+ if (g_file_test (cache_uri, G_FILE_TEST_EXISTS)) {
+ g_unlink (cache_uri);
+ }
+ g_free (cache_uri);
+
+ cache_uri = get_filename_from_uri (priv->uri, "cache.xml");
+ if (g_file_test (cache_uri, G_FILE_TEST_EXISTS)) {
+ g_unlink (cache_uri);
+ }
+ g_free (cache_uri);
+
+ e_data_book_respond_remove (book, opid, GNOME_Evolution_Addressbook_Success);
+ return;
+
+
+ default:
+ break;
+ }
+
+ return;
+
+ /* FIXME : provide implmentation */
+}
+
+static void
+e_book_backend_mapi_set_mode (EBookBackend *backend, int mode)
+{
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) backend)->priv;
+
+ if(enable_debug)
+ printf("mapi: set_mode \n");
+
+ priv->mode = mode;
+ if (e_book_backend_is_loaded (backend)) {
+ if (mode == GNOME_Evolution_Addressbook_MODE_LOCAL) {
+ e_book_backend_notify_writable (backend, FALSE);
+ e_book_backend_notify_connection_status (backend, FALSE);
+ /* FIXME: Uninitialize mapi here. may be.*/
+ }
+ else if (mode == GNOME_Evolution_Addressbook_MODE_REMOTE) {
+ e_book_backend_notify_writable (backend, TRUE);
+ e_book_backend_notify_connection_status (backend, TRUE);
+ e_book_backend_notify_auth_required (backend); //FIXME: WTH is this required.
+ }
+ }
+}
+
+static void
+e_book_backend_mapi_dispose (GObject *object)
+{
+ /* FIXME : provide implmentation */
+ EBookBackendMAPIPrivate *priv = ((EBookBackendMAPI *) object)->priv;
+
+ if (priv->profile) {
+ g_free (priv->profile);
+ priv->profile = NULL;
+ }
+ if (priv->uri) {
+ g_free (priv->uri);
+ priv->uri = NULL;
+ }
+
+}
+
+
+
+static void e_book_backend_mapi_class_init (EBookBackendMAPIClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ EBookBackendClass *parent_class;
+
+
+ e_book_backend_mapi_parent_class = g_type_class_peek_parent (klass);
+
+ parent_class = E_BOOK_BACKEND_CLASS (klass);
+
+ /* Set the virtual methods. */
+ parent_class->load_source = e_book_backend_mapi_load_source;
+ parent_class->get_static_capabilities = e_book_backend_mapi_get_static_capabilities;
+ parent_class->create_contact = e_book_backend_mapi_create_contact;
+ parent_class->remove_contacts = e_book_backend_mapi_remove_contacts;
+ parent_class->modify_contact = e_book_backend_mapi_modify_contact;
+ parent_class->get_contact = e_book_backend_mapi_get_contact;
+ parent_class->get_contact_list = e_book_backend_mapi_get_contact_list;
+ parent_class->start_book_view = e_book_backend_mapi_start_book_view;
+ parent_class->stop_book_view = e_book_backend_mapi_stop_book_view;
+ parent_class->get_changes = e_book_backend_mapi_get_changes;
+ parent_class->authenticate_user = e_book_backend_mapi_authenticate_user;
+ parent_class->get_required_fields = e_book_backend_mapi_get_required_fields;
+ parent_class->get_supported_fields = e_book_backend_mapi_get_supported_fields;
+ parent_class->get_supported_auth_methods = e_book_backend_mapi_get_supported_auth_methods;
+ parent_class->cancel_operation = e_book_backend_mapi_cancel_operation;
+ parent_class->remove = e_book_backend_mapi_remove;
+ parent_class->set_mode = e_book_backend_mapi_set_mode;
+ object_class->dispose = e_book_backend_mapi_dispose;
+
+}
+
+EBookBackend *e_book_backend_mapi_new (void)
+{
+ EBookBackendMAPI *backend;
+
+
+ backend = g_object_new (E_TYPE_BOOK_BACKEND_MAPI, NULL);
+ return E_BOOK_BACKEND (backend);
+}
+
+
+static void e_book_backend_mapi_init (EBookBackendMAPI *backend)
+{
+ EBookBackendMAPIPrivate *priv;
+
+ priv= g_new0 (EBookBackendMAPIPrivate, 1);
+ /* Priv Struct init */
+ backend->priv = priv;
+
+ priv->marked_for_offline = FALSE;
+ priv->uri = NULL;
+ priv->cache = NULL;
+ priv->is_summary_ready = FALSE;
+ priv->is_cache_ready = FALSE;
+
+ if (g_getenv ("MAPI_DEBUG"))
+ enable_debug = TRUE;
+ else
+ enable_debug = FALSE;
+
+
+}
+
+
+GType e_book_backend_mapi_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo info = {
+ sizeof (EBookBackendMAPIClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) e_book_backend_mapi_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EBookBackendMAPI),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) e_book_backend_mapi_init
+ };
+
+ type = g_type_register_static (E_TYPE_BOOK_BACKEND, "EBookBackendMAPI", &info, 0);
+ }
+
+ return type;
+}
Added: trunk/src/addressbook/e-book-backend-mapi.h
==============================================================================
--- (empty file)
+++ trunk/src/addressbook/e-book-backend-mapi.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,60 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef __E_BOOK_BACKEND_MAPI_H__
+#define __E_BOOK_BACKEND_MAPI_H__
+
+#include <libedata-book/e-book-backend.h>
+#include <libedata-book/e-book-backend-sync.h>
+#include "exchange-mapi-connection.h"
+#include "exchange-mapi-defs.h"
+#include "exchange-mapi-utils.h"
+
+/* #include "db.h" */
+
+
+#define E_TYPE_BOOK_BACKEND_MAPI (e_book_backend_mapi_get_type ())
+#define E_BOOK_BACKEND_MAPI(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_BOOK_BACKEND_MAPI, EBookBackendMAPI))
+#define E_BOOK_BACKEND_MAPI_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), E_TYPE_BOOK_BACKEND_MAPI, EBookBackendMAPIClass))
+#define E_IS_BOOK_BACKEND_MAPI(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_BOOK_BACKEND_MAPI))
+#define E_IS_BOOK_BACKEND_MAPI_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_BOOK_BACKEND_MAPI))
+#define E_BOOK_BACKEND_MAPI_GET_CLASS(k) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_BOOK_BACKEND_MAPI, EBookBackendMAPIClass))
+
+typedef struct _EBookBackendMAPIPrivate EBookBackendMAPIPrivate;
+
+typedef struct
+{
+ EBookBackend parent_object;
+ EBookBackendMAPIPrivate *priv;
+} EBookBackendMAPI;
+
+typedef struct
+{
+ EBookBackendClass parent_class;
+} EBookBackendMAPIClass;
+
+EBookBackend *e_book_backend_mapi_new (void);
+GType e_book_backend_mapi_get_type (void);
+
+#endif /* ! __E_BOOK_BACKEND_MAPI_H__ */
+
Added: trunk/src/calendar/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/calendar/Makefile.am Wed Nov 19 04:28:20 2008
@@ -0,0 +1,31 @@
+INCLUDES = \
+ -DG_LOG_DOMAIN=\"libecalbackendmapi\" \
+ -I$(top_srcdir)/src/calendar \
+ -I$(top_builddir)/src/calendar \
+ -I$(top_srcdir)/src/libexchangemapi \
+ -I$(top_builddir)/src/libexchangemapi \
+ $(LIBEBACKEND_CFLAGS) \
+ $(EVOLUTION_CALENDAR_CFLAGS) \
+ $(LIBECAL_CFLAGS) \
+ $(LIBEDATACAL_CFLAGS) \
+ $(LIBMAPI_CFLAGS)
+
+extension_LTLIBRARIES = libecalbackendmapi.la
+
+libecalbackendmapi_la_SOURCES = \
+ e-cal-backend-mapi-factory.c \
+ e-cal-backend-mapi-factory.h \
+ e-cal-backend-mapi.c \
+ e-cal-backend-mapi.h
+
+libecalbackendmapi_la_LIBADD = \
+ $(top_builddir)/src/libexchangemapi/libexchangemapi-1.0.la \
+ $(LIBEBACKEND_LIBS) \
+ $(EVOLUTION_CALENDAR_LIBS) \
+ $(LIBECAL_LIBS) \
+ $(LIBEDATACAL_LIBS) \
+ $(LIBMAPI_LIBS)
+
+libecalbackendmapi_la_LDFLAGS = \
+ -module -avoid-version $(NO_UNDEFINED)
+
Added: trunk/src/calendar/e-cal-backend-mapi-factory.c
==============================================================================
--- (empty file)
+++ trunk/src/calendar/e-cal-backend-mapi-factory.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,214 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-backend-mapi-factory.h"
+#include "e-cal-backend-mapi.h"
+
+#define d(x)
+
+typedef struct {
+ ECalBackendFactory parent_object;
+} ECalBackendMAPIFactory;
+
+typedef struct {
+ ECalBackendFactoryClass parent_class;
+} ECalBackendMAPIFactoryClass;
+
+static void
+e_cal_backend_mapi_factory_instance_init (ECalBackendMAPIFactory *factory)
+{
+}
+
+static const char *
+_get_protocol (ECalBackendFactory *factory)
+{
+ return "mapi";
+}
+
+static ECalBackend*
+_todos_new_backend (ECalBackendFactory *factory, ESource *source)
+{
+ return g_object_new (e_cal_backend_mapi_get_type (),
+ "source", source,
+ "kind", ICAL_VTODO_COMPONENT,
+ NULL);
+}
+
+static icalcomponent_kind
+_todos_get_kind (ECalBackendFactory *factory)
+{
+ return ICAL_VTODO_COMPONENT;
+}
+
+static void
+todos_backend_factory_class_init (ECalBackendMAPIFactoryClass *klass)
+{
+ E_CAL_BACKEND_FACTORY_CLASS (klass)->get_protocol = _get_protocol;
+ E_CAL_BACKEND_FACTORY_CLASS (klass)->get_kind = _todos_get_kind;
+ E_CAL_BACKEND_FACTORY_CLASS (klass)->new_backend = _todos_new_backend;
+}
+
+static GType
+todos_backend_factory_get_type (GTypeModule *module)
+{
+ GType type;
+
+ GTypeInfo info = {
+ sizeof (ECalBackendMAPIFactoryClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) todos_backend_factory_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (ECalBackend),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) e_cal_backend_mapi_factory_instance_init
+ };
+
+ type = g_type_module_register_type (module,
+ E_TYPE_CAL_BACKEND_FACTORY,
+ "ECalBackendMAPITodosFactory",
+ &info, 0);
+
+ return type;
+}
+
+static ECalBackend*
+_events_new_backend (ECalBackendFactory *factory, ESource *source)
+{
+ return g_object_new (e_cal_backend_mapi_get_type (),
+ "source", source,
+ "kind", ICAL_VEVENT_COMPONENT,
+ NULL);
+}
+
+static icalcomponent_kind
+_events_get_kind (ECalBackendFactory *factory)
+{
+ return ICAL_VEVENT_COMPONENT;
+}
+
+static void
+events_backend_factory_class_init (ECalBackendMAPIFactoryClass *klass)
+{
+ E_CAL_BACKEND_FACTORY_CLASS (klass)->get_protocol = _get_protocol;
+ E_CAL_BACKEND_FACTORY_CLASS (klass)->get_kind = _events_get_kind;
+ E_CAL_BACKEND_FACTORY_CLASS (klass)->new_backend = _events_new_backend;
+}
+
+static GType
+events_backend_factory_get_type (GTypeModule *module)
+{
+ GType type;
+
+ GTypeInfo info = {
+ sizeof (ECalBackendMAPIFactoryClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) events_backend_factory_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (ECalBackend),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) e_cal_backend_mapi_factory_instance_init
+ };
+
+ type = g_type_module_register_type (module,
+ E_TYPE_CAL_BACKEND_FACTORY,
+ "ECalBackendMAPIEventsFactory",
+ &info, 0);
+
+ return type;
+}
+
+/* NOTE: Outlook "Notes" = Evolution "Memos" */
+static ECalBackend*
+_journal_new_backend (ECalBackendFactory *factory, ESource *source)
+{
+ return g_object_new (e_cal_backend_mapi_get_type (),
+ "source", source,
+ "kind", ICAL_VJOURNAL_COMPONENT,
+ NULL);
+}
+
+static icalcomponent_kind
+_journal_get_kind (ECalBackendFactory *factory)
+{
+ return ICAL_VJOURNAL_COMPONENT;
+}
+
+static void
+journal_backend_factory_class_init (ECalBackendMAPIFactoryClass *klass)
+{
+ E_CAL_BACKEND_FACTORY_CLASS (klass)->get_protocol = _get_protocol;
+ E_CAL_BACKEND_FACTORY_CLASS (klass)->get_kind = _journal_get_kind;
+ E_CAL_BACKEND_FACTORY_CLASS (klass)->new_backend = _journal_new_backend;
+}
+
+static GType
+journal_backend_factory_get_type (GTypeModule *module)
+{
+ GType type;
+
+ GTypeInfo info = {
+ sizeof (ECalBackendMAPIFactoryClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) journal_backend_factory_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (ECalBackend),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) e_cal_backend_mapi_factory_instance_init
+ };
+
+ type = g_type_module_register_type (module,
+ E_TYPE_CAL_BACKEND_FACTORY,
+ "ECalBackendMAPIJournalFactory",
+ &info, 0);
+
+ return type;
+}
+
+static GType mapi_types[3];
+
+void
+eds_module_initialize (GTypeModule *module)
+{
+ mapi_types[0] = todos_backend_factory_get_type (module);
+ mapi_types[1] = events_backend_factory_get_type (module);
+ mapi_types[2] = journal_backend_factory_get_type (module);
+}
+
+void
+eds_module_shutdown (void)
+{
+}
+
+void
+eds_module_list_types (const GType **types, int *num_types)
+{
+ *types = mapi_types;
+ *num_types = 3;
+}
+
Added: trunk/src/calendar/e-cal-backend-mapi-factory.h
==============================================================================
--- (empty file)
+++ trunk/src/calendar/e-cal-backend-mapi-factory.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef _E_CAL_BACKEND_MAPI_FACTORY_H_
+#define _E_CAL_BACKEND_MAPI_FACTORY_H_
+
+#include <libedata-cal/e-cal-backend-factory.h>
+
+G_BEGIN_DECLS
+
+void eds_module_initialize (GTypeModule *module);
+void eds_module_shutdown (void);
+void eds_module_list_types (const GType **types, int *num_types);
+
+G_END_DECLS
+
+#endif /* _E_CAL_BACKEND_MAPI_FACTORY_H_ */
+
Added: trunk/src/calendar/e-cal-backend-mapi.c
==============================================================================
--- (empty file)
+++ trunk/src/calendar/e-cal-backend-mapi.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,2421 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "e-cal-backend-mapi.h"
+
+#include <libedata-cal/e-cal-backend-cache.h>
+#include <libedataserver/e-xml-hash-utils.h>
+
+#include <exchange-mapi-connection.h>
+#include <exchange-mapi-cal-utils.h>
+#include <exchange-mapi-utils.h>
+
+#define d(x) x
+
+#ifdef G_OS_WIN32
+/* Undef the similar macro from pthread.h, it doesn't check if
+ * gmtime() returns NULL.
+ */
+#undef gmtime_r
+
+/* The gmtime() in Microsoft's C library is MT-safe */
+#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
+#endif
+
+typedef struct {
+ GCond *cond;
+ GMutex *mutex;
+ gboolean exit;
+} SyncDelta;
+
+/* Private part of the CalBackendMAPI structure */
+struct _ECalBackendMAPIPrivate {
+ mapi_id_t fid;
+ uint32_t olFolder;
+ gchar *profile;
+
+ /* These fields are entirely for access rights */
+ gchar *owner_name;
+ gchar *owner_email;
+ gchar *user_name;
+ gchar *user_email;
+
+ /* A mutex to control access to the private structure */
+ GMutex *mutex;
+ ECalBackendCache *cache;
+ gboolean read_only;
+ gchar *uri;
+ gchar *username;
+ gchar *password;
+ CalMode mode;
+ gboolean mode_changed;
+ icaltimezone *default_zone;
+
+ /* timeout handler for syncing sendoptions */
+ guint sendoptions_sync_timeout;
+
+ gchar *local_attachments_store;
+
+ /* used exclusively for delta fetching */
+ guint timeout_id;
+ GThread *dthread;
+ SyncDelta *dlock;
+ GSList *cache_keys;
+};
+
+#define PARENT_TYPE E_TYPE_CAL_BACKEND_SYNC
+static ECalBackendClass *parent_class = NULL;
+
+#define CACHE_REFRESH_INTERVAL 600000
+
+static gboolean authenticated = FALSE;
+static GStaticMutex auth_mutex = G_STATIC_MUTEX_INIT;
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_authenticate (ECalBackend *backend)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ if (authenticated || exchange_mapi_connection_exists () || exchange_mapi_connection_new (priv->profile, priv->password)) {
+ authenticated = TRUE;
+ return GNOME_Evolution_Calendar_Success;
+ } else {
+ authenticated = FALSE;
+ e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Authentication failed"));
+ return GNOME_Evolution_Calendar_AuthenticationFailed;
+ }
+}
+
+/***** OBJECT CLASS FUNCTIONS *****/
+static void
+e_cal_backend_mapi_dispose (GObject *object)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ cbmapi = E_CAL_BACKEND_MAPI (object);
+ priv = cbmapi->priv;
+
+ if (G_OBJECT_CLASS (parent_class)->dispose)
+ (* G_OBJECT_CLASS (parent_class)->dispose) (object);
+}
+
+static void
+e_cal_backend_mapi_finalize (GObject *object)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (E_IS_CAL_BACKEND_MAPI (object));
+
+ cbmapi = E_CAL_BACKEND_MAPI (object);
+ priv = cbmapi->priv;
+
+ /* Clean up */
+ if (priv->timeout_id) {
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = 0;
+ }
+
+ if (priv->dlock) {
+ g_mutex_lock (priv->dlock->mutex);
+ priv->dlock->exit = TRUE;
+ g_mutex_unlock (priv->dlock->mutex);
+
+ g_cond_signal (priv->dlock->cond);
+
+ if (priv->dthread)
+ g_thread_join (priv->dthread);
+
+ g_mutex_free (priv->dlock->mutex);
+ g_cond_free (priv->dlock->cond);
+ g_free (priv->dlock);
+ priv->dthread = NULL;
+ }
+
+ if (priv->mutex) {
+ g_mutex_free (priv->mutex);
+ priv->mutex = NULL;
+ }
+
+ if (priv->cache) {
+ g_object_unref (priv->cache);
+ priv->cache = NULL;
+ }
+
+ if (priv->username) {
+ g_free (priv->username);
+ priv->username = NULL;
+ }
+
+ if (priv->password) {
+ g_free (priv->password);
+ priv->password = NULL;
+ }
+
+ if (priv->profile) {
+ g_free (priv->profile);
+ priv->profile = NULL;
+ }
+
+ if (priv->user_name) {
+ g_free (priv->user_name);
+ priv->user_name = NULL;
+ }
+
+ if (priv->user_email) {
+ g_free (priv->user_email);
+ priv->user_email = NULL;
+ }
+
+ if (priv->owner_name) {
+ g_free (priv->owner_name);
+ priv->owner_name = NULL;
+ }
+
+ if (priv->owner_email) {
+ g_free (priv->owner_email);
+ priv->owner_email = NULL;
+ }
+
+ if (priv->local_attachments_store) {
+ g_free (priv->local_attachments_store);
+ priv->local_attachments_store = NULL;
+ }
+
+ if (priv->sendoptions_sync_timeout) {
+ g_source_remove (priv->sendoptions_sync_timeout);
+ priv->sendoptions_sync_timeout = 0;
+ }
+
+ if (priv->default_zone) {
+ icaltimezone_free (priv->default_zone, 1);
+ priv->default_zone = NULL;
+ }
+
+ g_free (priv);
+ cbmapi->priv = NULL;
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ (* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+
+/***** SYNC CLASS FUNCTIONS *****/
+static ECalBackendSyncStatus
+e_cal_backend_mapi_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ *read_only = priv->read_only;
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_get_cal_address (ECalBackendSync *backend, EDataCal *cal, char **address)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ *address = g_strdup (priv->user_email);
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_get_alarm_email_address (ECalBackendSync *backend, EDataCal *cal, char **address)
+{
+ /* We don't support email alarms. This should not have been called. */
+
+ *address = NULL;
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_get_ldap_attribute (ECalBackendSync *backend, EDataCal *cal, char **attribute)
+{
+ /* This is just a hack for SunONE */
+ *attribute = NULL;
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_get_static_capabilities (ECalBackendSync *backend, EDataCal *cal, char **capabilities)
+{
+ /* FIXME: what else ? */
+
+ *capabilities = g_strdup (
+ CAL_STATIC_CAPABILITY_NO_ALARM_REPEAT ","
+ CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS ","
+// CAL_STATIC_CAPABILITY_NO_DISPLAY_ALARMS ","
+ CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS ","
+ CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS ","
+ CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY ","
+ CAL_STATIC_CAPABILITY_REMOVE_ALARMS ","
+
+// CAL_STATIC_CAPABILITY_NO_SHARED_MEMOS ","
+// CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT ","
+ CAL_STATIC_CAPABILITY_NO_THISANDFUTURE ","
+ CAL_STATIC_CAPABILITY_NO_THISANDPRIOR ","
+// CAL_STATIC_CAPABILITY_NO_TRANSPARENCY ","
+// CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ATTEND ","
+// CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS ","
+// CAL_STATIC_CAPABILITY_CREATE_MESSAGES ","
+// CAL_STATIC_CAPABILITY_SAVE_SCHEDULES ","
+ CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK ","
+ CAL_STATIC_CAPABILITY_NO_CONV_TO_RECUR ","
+// CAL_STATIC_CAPABILITY_NO_GEN_OPTIONS ","
+// CAL_STATIC_CAPABILITY_REQ_SEND_OPTIONS ","
+// CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER ","
+// CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ACCEPT ","
+// CAL_STATIC_CAPABILITY_DELEGATE_SUPPORTED ","
+// CAL_STATIC_CAPABILITY_NO_ORGANIZER ","
+// CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY ","
+ CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING
+ );
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_remove (ECalBackendSync *backend, EDataCal *cal)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ gboolean status = FALSE;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ if (priv->mode == CAL_MODE_LOCAL)
+ return GNOME_Evolution_Calendar_RepositoryOffline;
+
+ /* FIXME: check for return status and respond */
+ if (!authenticated) {
+ g_static_mutex_lock (&auth_mutex);
+ e_cal_backend_mapi_authenticate (E_CAL_BACKEND (cbmapi));
+ g_static_mutex_unlock (&auth_mutex);
+ }
+
+ if (!authenticated)
+ return GNOME_Evolution_Calendar_AuthenticationFailed;
+
+ status = exchange_mapi_remove_folder (priv->olFolder, priv->fid);
+ if (!status)
+ return GNOME_Evolution_Calendar_OtherError;
+
+ g_mutex_lock (priv->mutex);
+
+ /* remove the cache */
+ if (priv->cache)
+ e_file_cache_remove (E_FILE_CACHE (priv->cache));
+
+ g_mutex_unlock (priv->mutex);
+
+ /* anything else ? */
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static const char *
+get_element_type (icalcomponent_kind kind)
+{
+
+ const char *type = "";
+
+ if (kind == ICAL_VEVENT_COMPONENT)
+ type = "Appointment";
+ else if (kind == ICAL_VTODO_COMPONENT)
+ type = "Task";
+ else if (kind == ICAL_VJOURNAL_COMPONENT)
+ type = "Note";
+
+ return type;
+
+}
+
+static void
+notify_progress (ECalBackendMAPI *cbmapi, guint64 index, guint64 total)
+{
+ guint percent = ((float)index/total) * 100 ;
+ if (percent > 100)
+ percent = 99;
+
+ gchar *progress_string = g_strdup_printf (_("Loading %s items"), get_element_type (e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi))));
+
+ e_cal_backend_notify_view_progress (E_CAL_BACKEND (cbmapi), progress_string, percent);
+
+ g_free (progress_string);
+}
+
+static gboolean
+mapi_cal_get_changes_cb (FetchItemsCallbackData *item_data, gpointer data)
+{
+ struct mapi_SPropValue_array *array = item_data->properties;
+ const mapi_id_t mid = item_data->mid;
+ GSList *streams = item_data->streams;
+ GSList *recipients = item_data->recipients;
+ GSList *attachments = item_data->attachments;
+ ECalBackendMAPI *cbmapi = data;
+ ECalBackendMAPIPrivate *priv = cbmapi->priv;
+ icalcomponent_kind kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
+ gchar *tmp = NULL;
+ ECalComponent *cache_comp = NULL;
+ const bool *recurring;
+
+// exchange_mapi_debug_property_dump (array);
+
+ recurring = NULL;
+ /* FIXME: Evolution does not support recurring tasks */
+ recurring = (const bool *)find_mapi_SPropValue_data(array, PROP_TAG(PT_BOOLEAN, 0x8126));
+ if (recurring && *recurring) {
+ g_warning ("Encountered a recurring task.");
+ exchange_mapi_util_free_stream_list (&streams);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_attachment_list (&attachments);
+ return TRUE;
+ }
+
+ tmp = exchange_mapi_util_mapi_id_to_string (mid);
+ cache_comp = e_cal_backend_cache_get_component (priv->cache, tmp, NULL);
+
+ if (cache_comp == NULL) {
+ ECalComponent *comp = exchange_mapi_cal_util_mapi_props_to_comp (kind, tmp, array,
+ streams, recipients, attachments,
+ priv->local_attachments_store, priv->default_zone);
+
+ if (E_IS_CAL_COMPONENT (comp)) {
+ char *comp_str;
+
+ e_cal_component_commit_sequence (comp);
+ comp_str = e_cal_component_get_as_string (comp);
+
+ e_cal_backend_notify_object_created (E_CAL_BACKEND (cbmapi), (const char *) comp_str);
+ e_cal_backend_cache_put_component (priv->cache, comp);
+
+ g_free (comp_str);
+ }
+ g_object_unref (comp);
+ } else {
+ struct timeval t;
+
+ if (get_mapi_SPropValue_array_date_timeval (&t, array, PR_LAST_MODIFICATION_TIME) == MAPI_E_SUCCESS) {
+ struct icaltimetype itt, *cache_comp_lm = NULL;
+
+ itt = icaltime_from_timet_with_zone (t.tv_sec, 0, 0);
+ icaltime_set_timezone (&itt, icaltimezone_get_utc_timezone ());
+
+ e_cal_component_get_last_modified (cache_comp, &cache_comp_lm);
+ if (!cache_comp_lm || (icaltime_compare (itt, *cache_comp_lm) != 0)) {
+ ECalComponent *comp;
+ char *cache_comp_str = NULL, *modif_comp_str = NULL;
+
+ e_cal_component_commit_sequence (cache_comp);
+ cache_comp_str = e_cal_component_get_as_string (cache_comp);
+
+ comp = exchange_mapi_cal_util_mapi_props_to_comp (kind, tmp, array,
+ streams, recipients, attachments,
+ priv->local_attachments_store, priv->default_zone);
+
+ e_cal_component_commit_sequence (comp);
+ modif_comp_str = e_cal_component_get_as_string (comp);
+
+ e_cal_backend_notify_object_modified (E_CAL_BACKEND (cbmapi), cache_comp_str, modif_comp_str);
+ e_cal_backend_cache_put_component (priv->cache, comp);
+
+ g_object_unref (comp);
+ g_free (cache_comp_str);
+ g_free (modif_comp_str);
+ }
+ g_object_unref (cache_comp);
+ g_free (cache_comp_lm);
+ }
+ }
+
+ g_free (tmp);
+ exchange_mapi_util_free_stream_list (&streams);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_attachment_list (&attachments);
+
+ notify_progress (cbmapi, item_data->index, item_data->total);
+
+ return TRUE;
+}
+
+static gboolean
+handle_deleted_items_cb (FetchItemsCallbackData *item_data, gpointer data)
+{
+ const mapi_id_t mid = item_data->mid;
+ ECalBackendMAPI *cbmapi = data;
+ ECalBackendMAPIPrivate *priv = cbmapi->priv;
+ gchar *tmp = NULL;
+ GSList *cache_comp_uid = NULL;
+
+ tmp = exchange_mapi_util_mapi_id_to_string (mid);
+ cache_comp_uid = g_slist_find_custom (priv->cache_keys, tmp, (GCompareFunc) (g_ascii_strcasecmp));
+ if (cache_comp_uid != NULL)
+ priv->cache_keys = g_slist_remove_link (priv->cache_keys, cache_comp_uid);
+
+ g_free (tmp);
+ return TRUE;
+}
+
+/* Simple workflow for fetching deltas:
+ * Poke cache for server_utc_time -> if exists, fetch all items modified after that time,
+ * note current time before fetching and update cache with the same after fetching.
+ * If server_utc_time does not exist OR is invalid, fetch all items
+ * (we anyway process the results only if last_modified has changed).
+ */
+
+static gboolean
+get_deltas (gpointer handle)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ icalcomponent_kind kind;
+ static GStaticMutex updating = G_STATIC_MUTEX_INIT;
+ icaltimetype itt_current, itt_cache = icaltime_null_time();
+ time_t current_time;
+ struct tm tm;
+ gchar *time_string = NULL;
+ gchar t_str [26];
+ const char *serv_time;
+ struct mapi_SRestriction res;
+ gboolean use_restriction = FALSE;
+ GSList *ls = NULL;
+
+ if (!handle)
+ return FALSE;
+
+ cbmapi = (ECalBackendMAPI *) handle;
+ priv= cbmapi->priv;
+ kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
+
+ if (priv->mode == CAL_MODE_LOCAL)
+ return FALSE;
+
+ g_static_mutex_lock (&updating);
+
+ serv_time = e_cal_backend_cache_get_server_utc_time (priv->cache);
+ if (serv_time)
+ itt_cache = icaltime_from_string (serv_time);
+ if (!icaltime_is_null_time (itt_cache)) {
+ struct SPropValue sprop;
+ struct timeval t;
+
+ use_restriction = TRUE;
+ res.rt = RES_PROPERTY;
+ res.res.resProperty.relop = RELOP_GE;
+ res.res.resProperty.ulPropTag = PR_LAST_MODIFICATION_TIME;
+
+ t.tv_sec = icaltime_as_timet_with_zone (itt_cache, icaltimezone_get_utc_timezone ());
+ t.tv_usec = 0;
+ set_SPropValue_proptag_date_timeval (&sprop, PR_LAST_MODIFICATION_TIME, &t);
+ cast_mapi_SPropValue (&(res.res.resProperty.lpProp), &sprop);
+ } else
+ g_warning ("Cache time-stamp not found.");
+
+ itt_current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
+ current_time = icaltime_as_timet_with_zone (itt_current, icaltimezone_get_utc_timezone ());
+ gmtime_r (¤t_time, &tm);
+ strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
+
+ e_cal_backend_notify_view_progress_start (E_CAL_BACKEND (cbmapi));
+
+// e_file_cache_freeze_changes (E_FILE_CACHE (priv->cache));
+ /* FIXME: GetProps does not seem to work for tasks :-( */
+ if (kind == ICAL_VTODO_COMPONENT) {
+ if (!exchange_mapi_connection_fetch_items (priv->fid, use_restriction ? &res : NULL,
+ NULL, 0, NULL, NULL,
+ mapi_cal_get_changes_cb, cbmapi,
+ MAPI_OPTIONS_FETCH_ALL)) {
+ /* FIXME: better string please... */
+ e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Error fetching changes from the server. Removing the cache might help."));
+// e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+ g_static_mutex_unlock (&updating);
+ return FALSE;
+ }
+ } else if (!exchange_mapi_connection_fetch_items (priv->fid, use_restriction ? &res : NULL,
+ cal_GetPropsList, G_N_ELEMENTS (cal_GetPropsList),
+ exchange_mapi_cal_util_build_name_id, GINT_TO_POINTER(kind),
+ mapi_cal_get_changes_cb, cbmapi,
+ MAPI_OPTIONS_FETCH_ALL)) {
+ /* FIXME: better string please... */
+ e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Error fetching changes from the server. Removing the cache might help."));
+// e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+ g_static_mutex_unlock (&updating);
+ return FALSE;
+ }
+// e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+
+ e_cal_backend_notify_view_done (E_CAL_BACKEND (cbmapi), GNOME_Evolution_Calendar_Success);
+
+ time_string = g_strdup (t_str);
+ e_cal_backend_cache_put_server_utc_time (priv->cache, time_string);
+ g_free (time_string);
+
+ /* handle deleted items here by going over the entire cache and
+ * checking for deleted items.*/
+
+ /* e_cal_backend_cache_get_keys returns a list of all the keys.
+ * The items in the list are pointers to internal data,
+ * so should not be freed, only the list should. */
+ priv->cache_keys = e_cal_backend_cache_get_keys (priv->cache);
+ if (!exchange_mapi_connection_fetch_items (priv->fid, NULL,
+ cal_IDList, G_N_ELEMENTS (cal_IDList),
+ NULL, NULL,
+ handle_deleted_items_cb, cbmapi,
+ 0)) {
+ /* FIXME: better string please... */
+ e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Error fetching changes from the server. Removing the cache might help."));
+ priv->cache_keys = NULL;
+ g_static_mutex_unlock (&updating);
+ return FALSE;
+ }
+
+ e_file_cache_freeze_changes (E_FILE_CACHE (priv->cache));
+ for (ls = priv->cache_keys; ls ; ls = g_slist_next (ls)) {
+ ECalComponent *comp = NULL;
+ icalcomponent *icalcomp = NULL;
+
+ comp = e_cal_backend_cache_get_component (priv->cache, (const char *) ls->data, NULL);
+
+ if (!comp)
+ continue;
+
+ icalcomp = e_cal_component_get_icalcomponent (comp);
+ if (kind == icalcomponent_isa (icalcomp)) {
+ char *comp_str = NULL;
+ ECalComponentId *id = e_cal_component_get_id (comp);
+
+ comp_str = e_cal_component_get_as_string (comp);
+ e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbmapi),
+ id, comp_str, NULL);
+ e_cal_backend_cache_remove_component (priv->cache, (const char *) id->uid, id->rid);
+
+ e_cal_component_free_id (id);
+ g_free (comp_str);
+ }
+ g_object_unref (comp);
+ }
+ e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+
+// g_slist_free (priv->cache_keys);
+ priv->cache_keys = NULL;
+
+ g_static_mutex_unlock (&updating);
+ return TRUE;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object)
+{
+ ECalComponent *comp;
+
+ comp = e_cal_component_new ();
+
+ switch (e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
+ case ICAL_VEVENT_COMPONENT:
+ e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
+ break;
+ case ICAL_VTODO_COMPONENT:
+ e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
+ break;
+ case ICAL_VJOURNAL_COMPONENT:
+ e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL);
+ break;
+ default:
+ g_object_unref (comp);
+ return GNOME_Evolution_Calendar_ObjectNotFound;
+ }
+
+ *object = e_cal_component_get_as_string (comp);
+ g_object_unref (comp);
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_get_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, char **object)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ ECalComponent *comp;
+
+ cbmapi = (ECalBackendMAPI *)(backend);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GNOME_Evolution_Calendar_OtherError);
+
+ priv = cbmapi->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ /* search the object in the cache */
+ comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
+
+ if (comp) {
+ g_mutex_unlock (priv->mutex);
+ if (e_cal_backend_get_kind (E_CAL_BACKEND (backend)) ==
+ icalcomponent_isa (e_cal_component_get_icalcomponent (comp)))
+ *object = e_cal_component_get_as_string (comp);
+ else
+ *object = NULL;
+
+ g_object_unref (comp);
+
+ return *object ? GNOME_Evolution_Calendar_Success : GNOME_Evolution_Calendar_ObjectNotFound;
+ }
+
+ g_mutex_unlock (priv->mutex);
+
+ /* callers will never have a uid that is in server but not in cache */
+ return GNOME_Evolution_Calendar_ObjectNotFound;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_get_object_list (ECalBackendSync *backend, EDataCal *cal, const char *sexp, GList **objects)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ GList *components, *l;
+ ECalBackendSExp *cbsexp;
+ gboolean search_needed = TRUE;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ g_mutex_lock (priv->mutex);
+
+// d(g_message (G_STRLOC ": Getting object list (%s)", sexp));
+
+ if (!strcmp (sexp, "#t"))
+ search_needed = FALSE;
+
+ cbsexp = e_cal_backend_sexp_new (sexp);
+
+ if (!cbsexp) {
+ g_mutex_unlock (priv->mutex);
+ return GNOME_Evolution_Calendar_InvalidQuery;
+ }
+
+ *objects = NULL;
+ components = e_cal_backend_cache_get_components (priv->cache);
+
+ for (l = components; l != NULL; l = l->next) {
+ ECalComponent *comp = E_CAL_COMPONENT (l->data);
+ if (e_cal_backend_get_kind (E_CAL_BACKEND (backend)) ==
+ icalcomponent_isa (e_cal_component_get_icalcomponent (comp))) {
+ if ((!search_needed) ||
+ (e_cal_backend_sexp_match_comp (cbsexp, comp, E_CAL_BACKEND (backend)))) {
+ *objects = g_list_append (*objects, e_cal_component_get_as_string (comp));
+ }
+ }
+ }
+
+ g_object_unref (cbsexp);
+ g_list_foreach (components, (GFunc) g_object_unref, NULL);
+ g_list_free (components);
+ g_mutex_unlock (priv->mutex);
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_get_attachment_list (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, GSList **list)
+{
+ /* TODO implement the function */
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static guint
+get_cache_refresh_interval (void)
+{
+ guint time_interval;
+ const char *time_interval_string = NULL;
+
+ time_interval = CACHE_REFRESH_INTERVAL;
+ time_interval_string = g_getenv ("GETQM_TIME_INTERVAL");
+ if (time_interval_string) {
+ time_interval = g_ascii_strtod (time_interval_string, NULL);
+ time_interval *= (60*1000);
+ }
+
+ return time_interval;
+}
+
+static gpointer
+delta_thread (gpointer data)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ GTimeVal timeout;
+
+ cbmapi = (ECalBackendMAPI *)(data);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GINT_TO_POINTER (GNOME_Evolution_Calendar_OtherError));
+
+ priv = cbmapi->priv;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ while (TRUE) {
+ gboolean succeeded = get_deltas (cbmapi);
+
+ g_mutex_lock (priv->dlock->mutex);
+
+ if (!succeeded || priv->dlock->exit)
+ break;
+
+ g_get_current_time (&timeout);
+ g_time_val_add (&timeout, get_cache_refresh_interval () * 1000);
+ g_cond_timed_wait (priv->dlock->cond, priv->dlock->mutex, &timeout);
+
+ if (priv->dlock->exit)
+ break;
+
+ g_mutex_unlock (priv->dlock->mutex);
+ }
+
+ g_mutex_unlock (priv->dlock->mutex);
+ priv->dthread = NULL;
+ return NULL;
+}
+
+static gboolean
+fetch_deltas (ECalBackendMAPI *cbmapi)
+{
+ ECalBackendMAPIPrivate *priv;
+ GError *error = NULL;
+
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GNOME_Evolution_Calendar_OtherError);
+
+ priv = cbmapi->priv;
+
+ /* If the thread is already running just return back */
+ if (priv->dthread)
+ return FALSE;
+
+ if (!priv->dlock) {
+ priv->dlock = g_new0 (SyncDelta, 1);
+ priv->dlock->mutex = g_mutex_new ();
+ priv->dlock->cond = g_cond_new ();
+ }
+
+ priv->dlock->exit = FALSE;
+ priv->dthread = g_thread_create ((GThreadFunc) delta_thread, cbmapi, TRUE, &error);
+ if (!priv->dthread) {
+ g_warning (G_STRLOC ": %s", error->message);
+ g_error_free (error);
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+start_fetch_deltas (gpointer data)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ cbmapi = (ECalBackendMAPI *)(data);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GNOME_Evolution_Calendar_OtherError);
+
+ priv = cbmapi->priv;
+
+ fetch_deltas (cbmapi);
+
+ priv->timeout_id = 0;
+
+ return FALSE;
+}
+
+static gboolean
+mapi_cal_cache_create_cb (FetchItemsCallbackData *item_data, gpointer data)
+{
+ struct mapi_SPropValue_array *properties = item_data->properties;
+ const mapi_id_t mid = item_data->mid;
+ GSList *streams = item_data->streams;
+ GSList *recipients = item_data->recipients;
+ GSList *attachments = item_data->attachments;
+ ECalBackendMAPI *cbmapi = E_CAL_BACKEND_MAPI (data);
+ ECalBackendMAPIPrivate *priv = cbmapi->priv;
+ icalcomponent_kind kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
+ ECalComponent *comp = NULL;
+ gchar *tmp = NULL;
+ const bool *recurring = NULL;
+
+// exchange_mapi_debug_property_dump (properties);
+
+ switch (kind) {
+ case ICAL_VTODO_COMPONENT:
+ /* FIXME: Evolution does not support recurring tasks */
+ recurring = (const bool *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BOOLEAN, 0x8126));
+ if (recurring && *recurring) {
+ g_warning ("Encountered a recurring task.");
+ exchange_mapi_util_free_stream_list (&streams);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_attachment_list (&attachments);
+ return TRUE;
+ }
+ break;
+ case ICAL_VEVENT_COMPONENT :
+ case ICAL_VJOURNAL_COMPONENT:
+ break;
+ default:
+ return FALSE;
+ }
+
+ tmp = exchange_mapi_util_mapi_id_to_string (mid);
+ comp = exchange_mapi_cal_util_mapi_props_to_comp (kind, tmp, properties,
+ streams, recipients, attachments,
+ priv->local_attachments_store, priv->default_zone);
+ g_free (tmp);
+
+ if (E_IS_CAL_COMPONENT (comp)) {
+ gchar *comp_str;
+ e_cal_component_commit_sequence (comp);
+ comp_str = e_cal_component_get_as_string (comp);
+ e_cal_backend_notify_object_created (E_CAL_BACKEND (cbmapi), (const char *) comp_str);
+ g_free (comp_str);
+ e_cal_backend_cache_put_component (priv->cache, comp);
+ g_object_unref (comp);
+ }
+
+ exchange_mapi_util_free_stream_list (&streams);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_attachment_list (&attachments);
+
+ notify_progress (cbmapi, item_data->index, item_data->total);
+
+ return TRUE;
+}
+
+static ECalBackendSyncStatus
+populate_cache (ECalBackendMAPI *cbmapi)
+{
+ ECalBackendMAPIPrivate *priv;
+ ESource *source = NULL;
+ icalcomponent_kind kind;
+ icaltimetype itt_current;
+ time_t current_time;
+ struct tm tm;
+ gchar *time_string = NULL;
+ gchar t_str [26];
+
+ priv = cbmapi->priv;
+ source = e_cal_backend_get_source (E_CAL_BACKEND (cbmapi));
+ kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
+
+ g_mutex_lock (priv->mutex);
+
+ itt_current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
+ current_time = icaltime_as_timet_with_zone (itt_current, icaltimezone_get_utc_timezone ());
+ gmtime_r (¤t_time, &tm);
+ strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
+
+ e_cal_backend_notify_view_progress_start (E_CAL_BACKEND (cbmapi));
+
+// e_file_cache_freeze_changes (E_FILE_CACHE (priv->cache));
+ /* FIXME: GetProps does not seem to work for tasks :-( */
+ if (kind == ICAL_VTODO_COMPONENT) {
+ if (!exchange_mapi_connection_fetch_items (priv->fid, NULL,
+ NULL, 0, NULL, NULL,
+ mapi_cal_cache_create_cb, cbmapi,
+ MAPI_OPTIONS_FETCH_ALL)) {
+ e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Could not create cache file"));
+ e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+ g_mutex_unlock (priv->mutex);
+ return GNOME_Evolution_Calendar_OtherError;
+ }
+ } else if (!exchange_mapi_connection_fetch_items (priv->fid, NULL,
+ cal_GetPropsList, G_N_ELEMENTS (cal_GetPropsList),
+ exchange_mapi_cal_util_build_name_id, GINT_TO_POINTER(kind),
+ mapi_cal_cache_create_cb, cbmapi,
+ MAPI_OPTIONS_FETCH_ALL)) {
+ e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Could not create cache file"));
+ e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+ g_mutex_unlock (priv->mutex);
+ return GNOME_Evolution_Calendar_OtherError;
+ }
+// e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+
+ e_cal_backend_notify_view_done (E_CAL_BACKEND (cbmapi), GNOME_Evolution_Calendar_Success);
+
+ time_string = g_strdup (t_str);
+ e_cal_backend_cache_put_server_utc_time (priv->cache, time_string);
+ g_free (time_string);
+
+ e_cal_backend_cache_set_marker (priv->cache);
+
+ g_mutex_unlock (priv->mutex);
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static gpointer
+cache_init (ECalBackendMAPI *cbmapi)
+{
+ ECalBackendMAPIPrivate *priv = cbmapi->priv;
+ icalcomponent_kind kind;
+ ECalBackendSyncStatus status;
+
+ kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
+
+ priv->mode = CAL_MODE_REMOTE;
+
+ if (!e_cal_backend_cache_get_marker (priv->cache)) {
+ /* Populate the cache for the first time.*/
+ status = populate_cache (cbmapi);
+ if (status != GNOME_Evolution_Calendar_Success) {
+ g_warning (G_STRLOC ": Could not populate the cache");
+ /*FIXME why dont we do a notify here */
+ return GINT_TO_POINTER(GNOME_Evolution_Calendar_PermissionDenied);
+ } else {
+ /* Set delta fetch timeout */
+ priv->timeout_id = g_timeout_add (get_cache_refresh_interval (), start_fetch_deltas, (gpointer) cbmapi);
+
+ return NULL;
+ }
+ }
+
+ g_mutex_lock (priv->mutex);
+ fetch_deltas (cbmapi);
+ g_mutex_unlock (priv->mutex);
+
+ return NULL;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_connect (ECalBackendMAPI *cbmapi)
+{
+ ECalBackendMAPIPrivate *priv;
+ ESource *source;
+ ECalSourceType source_type;
+ GThread *thread;
+ GError *error = NULL;
+
+ priv = cbmapi->priv;
+
+ if (!priv->fid)
+ return GNOME_Evolution_Calendar_OtherError;
+
+ source = e_cal_backend_get_source (E_CAL_BACKEND (cbmapi));
+
+ if (!authenticated) {
+ e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Authentication failed"));
+ return GNOME_Evolution_Calendar_AuthenticationFailed;
+ }
+
+ /* We have established a connection */
+ if (priv->cache && priv->fid) {
+ priv->mode = CAL_MODE_REMOTE;
+ if (priv->mode_changed && !priv->dthread) {
+ priv->mode_changed = FALSE;
+ fetch_deltas (cbmapi);
+ }
+
+ /* FIXME: put server UTC time in cache */
+ return GNOME_Evolution_Calendar_Success;
+ }
+
+ priv->mode_changed = FALSE;
+
+ switch (e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi))) {
+ case ICAL_VEVENT_COMPONENT:
+ source_type = E_CAL_SOURCE_TYPE_EVENT;
+ break;
+ case ICAL_VTODO_COMPONENT:
+ source_type = E_CAL_SOURCE_TYPE_TODO;
+ break;
+ case ICAL_VJOURNAL_COMPONENT:
+ source_type = E_CAL_SOURCE_TYPE_JOURNAL;
+ break;
+ default:
+ source_type = E_CAL_SOURCE_TYPE_EVENT;
+ break;
+ }
+
+ priv->cache = e_cal_backend_cache_new (e_cal_backend_get_uri (E_CAL_BACKEND (cbmapi)), source_type);
+ if (!priv->cache) {
+ e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Could not create cache file"));
+ return GNOME_Evolution_Calendar_OtherError;
+ }
+
+ e_cal_backend_cache_put_default_timezone (priv->cache, priv->default_zone);
+
+ /* spawn a new thread for caching the calendar items */
+ thread = g_thread_create ((GThreadFunc) cache_init, cbmapi, FALSE, &error);
+ if (!thread) {
+ g_warning (G_STRLOC ": %s", error->message);
+ g_error_free (error);
+ e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Could not create thread for populating cache"));
+ return GNOME_Evolution_Calendar_OtherError;
+ }
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists, const char *username, const char *password)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ ECalBackendSyncStatus status;
+ ECalSourceType source_type;
+ ESource *esource;
+ const char *source = NULL, *fid = NULL;
+ char *filename;
+ char *mangled_uri;
+ int i;
+ uint32_t olFolder = 0;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ esource = e_cal_backend_get_source (E_CAL_BACKEND (cbmapi));
+ fid = e_source_get_property (esource, "folder-id");
+ if (!(fid && *fid))
+ return GNOME_Evolution_Calendar_OtherError;
+
+ g_mutex_lock (priv->mutex);
+
+ cbmapi->priv->read_only = FALSE;
+
+ switch (e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi))) {
+ case ICAL_VEVENT_COMPONENT:
+ source_type = E_CAL_SOURCE_TYPE_EVENT;
+ source = "calendar";
+ olFolder = olFolderCalendar;
+ break;
+ case ICAL_VTODO_COMPONENT:
+ source_type = E_CAL_SOURCE_TYPE_TODO;
+ source = "tasks";
+ olFolder = olFolderTasks;
+ break;
+ case ICAL_VJOURNAL_COMPONENT:
+ source_type = E_CAL_SOURCE_TYPE_JOURNAL;
+ source = "journal";
+ olFolder = olFolderNotes;
+ break;
+ default:
+ source_type = E_CAL_SOURCE_TYPE_EVENT;
+ break;
+ }
+
+ /* Not for remote */
+ if (priv->mode == CAL_MODE_LOCAL) {
+ const gchar *display_contents = NULL;
+
+ cbmapi->priv->read_only = TRUE;
+ display_contents = e_source_get_property (esource, "offline_sync");
+
+ if (!display_contents || !g_str_equal (display_contents, "1")) {
+ g_mutex_unlock (priv->mutex);
+ return GNOME_Evolution_Calendar_RepositoryOffline;
+ }
+
+ /* Cache created here for the first time */
+ if (!priv->cache) {
+ priv->cache = e_cal_backend_cache_new (e_cal_backend_get_uri (E_CAL_BACKEND (cbmapi)), source_type);
+ if (!priv->cache) {
+ g_mutex_unlock (priv->mutex);
+ e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Could not create cache file"));
+ return GNOME_Evolution_Calendar_OtherError;
+ }
+ }
+ e_cal_backend_cache_put_default_timezone (priv->cache, priv->default_zone);
+ g_mutex_unlock (priv->mutex);
+ return GNOME_Evolution_Calendar_Success;
+ }
+
+ priv->username = g_strdup (username);
+ priv->password = g_strdup (password);
+
+ priv->profile = g_strdup (e_source_get_property (esource, "profile"));
+ priv->user_name = g_strdup (e_source_get_property (esource, "acl-user-name"));
+ priv->user_email = g_strdup (e_source_get_property (esource, "acl-user-email"));
+ priv->owner_name = g_strdup (e_source_get_property (esource, "acl-owner-name"));
+ priv->owner_email = g_strdup (e_source_get_property (esource, "acl-owner-email"));
+
+ exchange_mapi_util_mapi_id_from_string (fid, &priv->fid);
+ priv->olFolder = olFolder;
+
+ /* Set the local attachment store */
+ mangled_uri = g_strdup (e_cal_backend_get_uri (E_CAL_BACKEND (cbmapi)));
+ /* mangle the URI to not contain invalid characters */
+ for (i = 0; i < strlen (mangled_uri); i++) {
+ switch (mangled_uri[i]) {
+ case ':' :
+ case '/' :
+ mangled_uri[i] = '_';
+ }
+ }
+
+ filename = g_build_filename (g_get_home_dir (),
+ ".evolution/cache/", source,
+ mangled_uri,
+ G_DIR_SEPARATOR_S,
+ NULL);
+
+ g_free (mangled_uri);
+ priv->local_attachments_store =
+ g_filename_to_uri (filename, NULL, NULL);
+ g_free (filename);
+
+ g_mutex_unlock (priv->mutex);
+
+ g_static_mutex_lock (&auth_mutex);
+ status = e_cal_backend_mapi_authenticate (E_CAL_BACKEND (cbmapi));
+ g_static_mutex_unlock (&auth_mutex);
+
+ if (status == GNOME_Evolution_Calendar_Success)
+ return e_cal_backend_mapi_connect (cbmapi);
+ else
+ return status;
+}
+
+static gboolean
+capture_req_props (FetchItemsCallbackData *item_data, gpointer data)
+{
+ struct mapi_SPropValue_array *properties = item_data->properties;
+ struct cbdata *cbdata = (struct cbdata *) data;
+ const uint32_t *ui32;
+
+ ui32 = (const uint32_t *)find_mapi_SPropValue_data(properties, PR_OWNER_APPT_ID);
+ if (ui32)
+ cbdata->appt_id = *ui32;
+ ui32 = (const uint32_t *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_LONG, 0x8201));
+ if (ui32)
+ cbdata->appt_seq = *ui32;
+ cbdata->cleanglobalid = (const struct SBinary *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BINARY, 0x0023));
+ cbdata->globalid = (const struct SBinary *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BINARY, 0x0003));
+ cbdata->username = exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_NAME);
+ cbdata->useridtype = exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_ADDRTYPE);
+ cbdata->userid = exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_EMAIL_ADDRESS);
+ cbdata->ownername = exchange_mapi_util_find_array_propval (properties, PR_SENDER_NAME);
+ cbdata->owneridtype = exchange_mapi_util_find_array_propval (properties, PR_SENDER_ADDRTYPE);
+ cbdata->ownerid = exchange_mapi_util_find_array_propval (properties, PR_SENDER_EMAIL_ADDRESS);
+
+ return TRUE;
+}
+
+static void
+get_server_data (ECalBackendMAPI *cbmapi, icalcomponent *comp, struct cbdata *cbdata)
+{
+ ECalBackendMAPIPrivate *priv = cbmapi->priv;
+ const char *uid;
+ mapi_id_t mid;
+ struct mapi_SRestriction res;
+ struct SPropValue sprop;
+ struct SBinary sb;
+ uint32_t proptag = 0x0;
+ struct SPropTagArray *array;
+
+ uid = icalcomponent_get_uid (comp);
+ exchange_mapi_util_mapi_id_from_string (uid, &mid);
+ if (exchange_mapi_connection_fetch_item (priv->fid, mid,
+ NULL, 0,
+ NULL, NULL,
+ capture_req_props, cbdata,
+ MAPI_OPTIONS_FETCH_GENERIC_STREAMS))
+
+ return;
+
+ array = exchange_mapi_util_resolve_named_prop (priv->olFolder, priv->fid, 0x0023, PSETID_Meeting);
+ proptag = array->aulPropTag[0];
+
+ res.rt = RES_PROPERTY;
+ res.res.resProperty.relop = RELOP_EQ;
+ res.res.resProperty.ulPropTag = proptag;
+
+ exchange_mapi_cal_util_generate_globalobjectid (TRUE, uid, &sb);
+
+ set_SPropValue_proptag (&sprop, proptag, (const void *) &sb);
+ cast_mapi_SPropValue (&(res.res.resProperty.lpProp), &sprop);
+
+ exchange_mapi_connection_fetch_items (priv->fid, &res,
+ NULL, 0,
+ NULL, NULL,
+ capture_req_props, cbdata,
+ MAPI_OPTIONS_FETCH_GENERIC_STREAMS);
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_create_object (ECalBackendSync *backend, EDataCal *cal, char **calobj, char **uid)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ icalcomponent_kind kind;
+ icalcomponent *icalcomp;
+ ECalComponent *comp;
+ const char *compuid;
+ mapi_id_t mid = 0;
+ gchar *tmp = NULL;
+ GSList *recipients = NULL;
+ GSList *attachments = NULL;
+ GSList *streams = NULL;
+ struct cbdata cbdata;
+ struct SBinary globalid;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+ kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
+
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GNOME_Evolution_Calendar_InvalidObject);
+ g_return_val_if_fail (calobj != NULL && *calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);
+
+ if (priv->mode == CAL_MODE_LOCAL)
+ return GNOME_Evolution_Calendar_RepositoryOffline;
+
+ /* check the component for validity */
+ icalcomp = icalparser_parse_string (*calobj);
+ if (!icalcomp)
+ return GNOME_Evolution_Calendar_InvalidObject;
+
+ if (kind != icalcomponent_isa (icalcomp)) {
+ icalcomponent_free (icalcomp);
+ return GNOME_Evolution_Calendar_InvalidObject;
+ }
+
+ comp = e_cal_component_new ();
+ e_cal_component_set_icalcomponent (comp, icalcomp);
+
+ /* FIXME: [WIP] Add support for recurrences */
+ if (e_cal_component_has_recurrences (comp)) {
+ GByteArray *ba = exchange_mapi_cal_util_rrule_to_bin (comp, NULL);
+ if (ba) {
+ struct SPropTagArray *tag_array;
+ ExchangeMAPIStream *stream = g_new0 (ExchangeMAPIStream, 1);
+ stream->value = ba;
+ tag_array = exchange_mapi_util_resolve_named_prop (priv->olFolder, priv->fid, 0x8216, PSETID_Appointment);
+ if (tag_array) {
+ stream->proptag = tag_array->aulPropTag[0];
+ streams = g_slist_append (streams, stream);
+ }
+ }
+ }
+
+ /* FIXME: [WIP] Add support for meetings/assigned tasks */
+ if (e_cal_component_has_attendees (comp))
+ exchange_mapi_cal_util_fetch_recipients (comp, &recipients);
+
+ if (e_cal_component_has_attachments (comp))
+ exchange_mapi_cal_util_fetch_attachments (comp, &attachments, priv->local_attachments_store);
+
+ cbdata.username = e_cal_backend_mapi_get_user_name (cbmapi);
+ cbdata.useridtype = "SMTP";
+ cbdata.userid = e_cal_backend_mapi_get_user_email (cbmapi);
+ cbdata.ownername = e_cal_backend_mapi_get_owner_name (cbmapi);
+ cbdata.owneridtype = "SMTP";
+ cbdata.ownerid = e_cal_backend_mapi_get_owner_email (cbmapi);
+
+ /* Check if object exists */
+ switch (priv->mode) {
+ case CAL_MODE_ANY:
+ case CAL_MODE_REMOTE:
+ /* Create an appointment */
+ cbdata.comp = comp;
+ cbdata.is_modify = FALSE;
+ cbdata.msgflags = MSGFLAG_READ;
+ cbdata.meeting_type = (recipients != NULL) ? MEETING_OBJECT : NOT_A_MEETING;
+ cbdata.resp = (recipients != NULL) ? olResponseOrganized : olResponseNone;
+ cbdata.appt_id = exchange_mapi_cal_util_get_new_appt_id (priv->fid);
+ cbdata.appt_seq = 0;
+ e_cal_component_get_uid (comp, &compuid);
+ exchange_mapi_cal_util_generate_globalobjectid (TRUE, compuid, &globalid);
+ cbdata.globalid = &globalid;
+ cbdata.cleanglobalid = &globalid;
+
+ mid = exchange_mapi_create_item (priv->olFolder, priv->fid,
+ exchange_mapi_cal_util_build_name_id, GINT_TO_POINTER(kind),
+ exchange_mapi_cal_util_build_props, &cbdata,
+ recipients, attachments, streams, MAPI_OPTIONS_DONT_SUBMIT);
+ g_free (cbdata.props);
+// g_free (globalid.lpb);
+ if (!mid) {
+ g_object_unref (comp);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_stream_list (&streams);
+ exchange_mapi_util_free_attachment_list (&attachments);
+ return GNOME_Evolution_Calendar_OtherError;
+ }
+
+ tmp = exchange_mapi_util_mapi_id_to_string (mid);
+ e_cal_component_set_uid (comp, tmp);
+ g_free (tmp);
+
+ e_cal_component_commit_sequence (comp);
+ e_cal_backend_cache_put_component (priv->cache, comp);
+ *calobj = e_cal_component_get_as_string (comp);
+ e_cal_backend_notify_object_created (E_CAL_BACKEND (cbmapi), *calobj);
+ break;
+ default:
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_stream_list (&streams);
+ exchange_mapi_util_free_attachment_list (&attachments);
+ return GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED;
+ }
+
+ /* blatant HACK /me blames some stupid design in e-d-s */
+ if (e_cal_component_has_attachments (comp) && !fetch_deltas(cbmapi))
+ g_cond_signal (priv->dlock->cond);
+
+ g_object_unref (comp);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_stream_list (&streams);
+ exchange_mapi_util_free_attachment_list (&attachments);
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static gboolean
+modifier_is_organizer (ECalBackendMAPI *cbmapi, ECalComponent *comp)
+{
+ ECalComponentOrganizer org;
+ const char *ownerid, *orgid;
+
+ if (!e_cal_component_has_organizer(comp))
+ return TRUE;
+
+ e_cal_component_get_organizer (comp, &org);
+ if (!g_ascii_strncasecmp (org.value, "mailto:", 7))
+ orgid = (org.value) + 7;
+ else
+ orgid = org.value;
+ ownerid = e_cal_backend_mapi_get_owner_email (cbmapi);
+
+ return (!g_ascii_strcasecmp(orgid, ownerid) ? TRUE : FALSE);
+}
+
+static OlResponseStatus
+get_trackstatus_from_partstat (icalparameter_partstat partstat)
+{
+ switch (partstat) {
+ case ICAL_PARTSTAT_ACCEPTED : return olResponseAccepted;
+ case ICAL_PARTSTAT_TENTATIVE : return olResponseTentative;
+ case ICAL_PARTSTAT_DECLINED : return olResponseDeclined;
+ default : return olResponseTentative;
+ }
+}
+
+static OlResponseStatus
+find_my_response (ECalBackendMAPI *cbmapi, ECalComponent *comp)
+{
+ icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
+ icalproperty *attendee;
+ gchar *att = NULL;
+ OlResponseStatus val = olResponseTentative;
+
+ att = g_strdup_printf ("MAILTO:%s", e_cal_backend_mapi_get_owner_email (cbmapi));
+ attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+ while (attendee) {
+ const char *value = icalproperty_get_attendee (attendee);
+ if (!g_ascii_strcasecmp (value, att)) {
+ icalparameter *param = icalproperty_get_first_parameter (attendee, ICAL_PARTSTAT_PARAMETER);
+ val = get_trackstatus_from_partstat (icalparameter_get_partstat(param));
+ break;
+ }
+ attendee = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+ }
+ g_free (att);
+
+ return val;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_modify_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj,
+ CalObjModType mod, char **old_object, char **new_object)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ icalcomponent_kind kind;
+ icalcomponent *icalcomp;
+ ECalComponent *comp, *cache_comp = NULL;
+ gboolean status;
+ mapi_id_t mid;
+ const char *uid = NULL, *rid = NULL;
+ GSList *recipients = NULL;
+ GSList *streams = NULL;
+ GSList *attachments = NULL;
+ struct cbdata cbdata;
+ gboolean no_increment = FALSE;
+ icalproperty *prop;
+
+ *old_object = *new_object = NULL;
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+ kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
+
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GNOME_Evolution_Calendar_InvalidObject);
+ g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);
+
+ if (priv->mode == CAL_MODE_LOCAL)
+ return GNOME_Evolution_Calendar_RepositoryOffline;
+
+ if (mod != CALOBJ_MOD_ALL) {
+ e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Support for modifying single instances of a recurring appointment is not yet implemented. No change was made to the appointment on the server."));
+ return GNOME_Evolution_Calendar_Success;
+ }
+
+ /* check the component for validity */
+ icalcomp = icalparser_parse_string (calobj);
+ if (!icalcomp)
+ return GNOME_Evolution_Calendar_InvalidObject;
+
+ prop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
+ while (prop) {
+ const char *name = icalproperty_get_x_name (prop);
+ if (!g_ascii_strcasecmp (name, "X-EVOLUTION-IS-REPLY")) {
+ no_increment = TRUE;
+ icalcomponent_remove_property (icalcomp, prop);
+ }
+ prop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
+ }
+
+ comp = e_cal_component_new ();
+ e_cal_component_set_icalcomponent (comp, icalcomp);
+
+ /* FIXME: [WIP] Add support for recurrences */
+ if (e_cal_component_has_recurrences (comp)) {
+ GByteArray *ba = exchange_mapi_cal_util_rrule_to_bin (comp, NULL);
+ if (ba) {
+ struct SPropTagArray *tag_array;
+ ExchangeMAPIStream *stream = g_new0 (ExchangeMAPIStream, 1);
+ stream->value = ba;
+ tag_array = exchange_mapi_util_resolve_named_prop (priv->olFolder, priv->fid, 0x8216, PSETID_Appointment);
+ if (tag_array) {
+ stream->proptag = tag_array->aulPropTag[0];
+ streams = g_slist_append (streams, stream);
+ }
+ }
+ }
+
+ if (e_cal_component_has_attendees (comp))
+ exchange_mapi_cal_util_fetch_recipients (comp, &recipients);
+
+ if (e_cal_component_has_attachments (comp))
+ exchange_mapi_cal_util_fetch_attachments (comp, &attachments, priv->local_attachments_store);
+
+ e_cal_component_get_uid (comp, &uid);
+// rid = e_cal_component_get_recurid_as_string (comp);
+
+ switch (priv->mode) {
+ case CAL_MODE_ANY :
+ case CAL_MODE_REMOTE : /* when online, send the item to the server */
+ /* check if the object exists */
+ cache_comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
+ if (!cache_comp) {
+ get_deltas (cbmapi);
+ cache_comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
+ }
+
+ if (!cache_comp) {
+ g_message ("CRITICAL : Could not find the object in cache");
+ g_object_unref (comp);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_stream_list (&streams);
+ exchange_mapi_util_free_attachment_list (&attachments);
+ return GNOME_Evolution_Calendar_ObjectNotFound;
+ }
+ exchange_mapi_util_mapi_id_from_string (uid, &mid);
+
+ cbdata.comp = comp;
+ cbdata.msgflags = MSGFLAG_READ;
+ cbdata.is_modify = TRUE;
+
+ get_server_data (cbmapi, icalcomp, &cbdata);
+ if (modifier_is_organizer(cbmapi, comp)) {
+ cbdata.meeting_type = (recipients != NULL) ? MEETING_OBJECT : NOT_A_MEETING;
+ cbdata.resp = (recipients != NULL) ? olResponseOrganized : olResponseNone;
+ if (!no_increment)
+ cbdata.appt_seq += 1;
+ cbdata.username = e_cal_backend_mapi_get_user_name (cbmapi);
+ cbdata.useridtype = "SMTP";
+ cbdata.userid = e_cal_backend_mapi_get_user_email (cbmapi);
+ cbdata.ownername = e_cal_backend_mapi_get_owner_name (cbmapi);
+ cbdata.owneridtype = "SMTP";
+ cbdata.ownerid = e_cal_backend_mapi_get_owner_email (cbmapi);
+ } else {
+ cbdata.resp = (recipients != NULL) ? find_my_response(cbmapi, comp) : olResponseNone;
+ cbdata.meeting_type = (recipients != NULL) ? MEETING_OBJECT_RCVD : NOT_A_MEETING;
+ }
+
+ status = exchange_mapi_modify_item (priv->olFolder, priv->fid, mid,
+ exchange_mapi_cal_util_build_name_id, GINT_TO_POINTER(kind),
+ exchange_mapi_cal_util_build_props, &cbdata,
+ recipients, attachments, streams, MAPI_OPTIONS_DONT_SUBMIT);
+ g_free (cbdata.props);
+ if (!status) {
+ g_object_unref (comp);
+ g_object_unref (cache_comp);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_stream_list (&streams);
+ exchange_mapi_util_free_attachment_list (&attachments);
+ return GNOME_Evolution_Calendar_OtherError;
+ }
+ break;
+ default :
+ g_object_unref (comp);
+ g_object_unref (cache_comp);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_stream_list (&streams);
+ exchange_mapi_util_free_attachment_list (&attachments);
+ return GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED;
+ }
+
+ *old_object = e_cal_component_get_as_string (cache_comp);
+ *new_object = e_cal_component_get_as_string (comp);
+
+ g_object_unref (comp);
+ g_object_unref (cache_comp);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_stream_list (&streams);
+ exchange_mapi_util_free_attachment_list (&attachments);
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_remove_object (ECalBackendSync *backend, EDataCal *cal,
+ const char *uid, const char *rid, CalObjModType mod,
+ char **old_object, char **object)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ icalcomponent *icalcomp;
+ ECalBackendSyncStatus status;
+ char *calobj = NULL;
+ mapi_id_t mid;
+
+ *old_object = *object = NULL;
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GNOME_Evolution_Calendar_InvalidObject);
+
+ if (priv->mode == CAL_MODE_LOCAL)
+ return GNOME_Evolution_Calendar_RepositoryOffline;
+
+ switch (priv->mode) {
+ case CAL_MODE_ANY :
+ case CAL_MODE_REMOTE : /* when online, modify/delete the item from the server */
+ /* check if the object exists */
+ /* FIXME: we may have detached instances which need to be removed */
+ status = e_cal_backend_mapi_get_object (backend, cal, uid, NULL, &calobj);
+ if (status != GNOME_Evolution_Calendar_Success)
+ return status;
+
+ /* check the component for validity */
+ icalcomp = icalparser_parse_string (calobj);
+ if (!icalcomp) {
+ g_free (calobj);
+ return GNOME_Evolution_Calendar_InvalidObject;
+ }
+
+ exchange_mapi_util_mapi_id_from_string (uid, &mid);
+
+ if (mod == CALOBJ_MOD_THIS && rid && *rid) {
+ char *obj = NULL, *new_object = NULL, *new_calobj = NULL;
+ struct icaltimetype time_rid;
+
+ /*remove a single instance of a recurring event and modify */
+ time_rid = icaltime_from_string (rid);
+ e_cal_util_remove_instances (icalcomp, time_rid, mod);
+ new_calobj = (char *) icalcomponent_as_ical_string (icalcomp);
+ status = e_cal_backend_mapi_modify_object (backend, cal, new_calobj, CALOBJ_MOD_ALL, &obj, &new_object);
+ if (status == GNOME_Evolution_Calendar_Success) {
+ *old_object = obj;
+ *object = new_object;
+ }
+ g_free (new_calobj);
+ } else {
+ GSList *list=NULL, *l, *comp_list = e_cal_backend_cache_get_components_by_uid (priv->cache, uid);
+
+// if (e_cal_component_has_attendees (E_CAL_COMPONENT (comp_list->data))) {
+// } else {
+ struct id_list *data = g_new (struct id_list, 1);
+ data->id = mid;
+ list = g_slist_prepend (list, (gpointer) data);
+// }
+
+ if (exchange_mapi_remove_items (priv->olFolder, priv->fid, list)) {
+ for (l = comp_list; l; l = l->next) {
+ ECalComponent *comp = E_CAL_COMPONENT (l->data);
+ ECalComponentId *id = e_cal_component_get_id (comp);
+
+ e_cal_backend_cache_remove_component (priv->cache, id->uid, id->rid);
+ if (!id->rid || !g_str_equal (id->rid, rid))
+ e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbmapi), id, e_cal_component_get_as_string (comp), NULL);
+ e_cal_component_free_id (id);
+
+ g_object_unref (comp);
+ }
+ *old_object = g_strdup (calobj);
+ *object = NULL;
+ status = GNOME_Evolution_Calendar_Success;
+ } else
+ status = GNOME_Evolution_Calendar_OtherError;
+
+ g_slist_free (list);
+ g_slist_free (comp_list);
+ }
+ g_free (calobj);
+ break;
+ default :
+ status = GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED;
+ break;
+ }
+
+ return status;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_discard_alarm (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *auid)
+{
+
+ return GNOME_Evolution_Calendar_Success;
+
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_send_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj,
+ GList **users, char **modified_calobj)
+{
+ ECalBackendSyncStatus status = GNOME_Evolution_Calendar_OtherError;
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ icalcomponent_kind kind;
+ icalcomponent *icalcomp;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+ kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
+
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GNOME_Evolution_Calendar_InvalidObject);
+ g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);
+
+ if (priv->mode == CAL_MODE_LOCAL)
+ return GNOME_Evolution_Calendar_RepositoryOffline;
+
+ /* check the component for validity */
+ icalcomp = icalparser_parse_string (calobj);
+ if (!icalcomp)
+ return GNOME_Evolution_Calendar_InvalidObject;
+
+ *modified_calobj = NULL;
+ *users = NULL;
+
+ if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
+ icalproperty_method method = icalcomponent_get_method (icalcomp);
+ icalcomponent *subcomp = icalcomponent_get_first_component (icalcomp, kind);
+ while (subcomp) {
+ ECalComponent *comp = e_cal_component_new ();
+ struct cbdata cbdata;
+ mapi_id_t mid = 0;
+ GSList *recipients = NULL;
+ GSList *attachments = NULL;
+
+ e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp));
+
+ /* FIXME: Add support for recurrences */
+ if (e_cal_component_has_recurrences (comp)) {
+ g_object_unref (comp);
+ return GNOME_Evolution_Calendar_OtherError;
+ }
+
+ if (e_cal_component_has_attachments (comp))
+ exchange_mapi_cal_util_fetch_attachments (comp, &attachments, priv->local_attachments_store);
+
+ cbdata.comp = comp;
+ cbdata.is_modify = TRUE;
+ cbdata.msgflags = MSGFLAG_READ | MSGFLAG_SUBMIT | MSGFLAG_UNSENT;
+
+ switch (method) {
+ case ICAL_METHOD_REQUEST :
+ cbdata.meeting_type = MEETING_REQUEST;
+ cbdata.resp = olResponseNotResponded;
+ if (e_cal_component_has_attendees (comp))
+ exchange_mapi_cal_util_fetch_recipients (comp, &recipients);
+ break;
+ case ICAL_METHOD_CANCEL :
+ cbdata.meeting_type = MEETING_CANCEL;
+ cbdata.resp = olResponseNotResponded;
+ if (e_cal_component_has_attendees (comp))
+ exchange_mapi_cal_util_fetch_recipients (comp, &recipients);
+ break;
+ case ICAL_METHOD_RESPONSE :
+ cbdata.meeting_type = MEETING_RESPONSE;
+ cbdata.resp = find_my_response (cbmapi, comp);
+ if (e_cal_component_has_organizer (comp))
+ exchange_mapi_cal_util_fetch_organizer (comp, &recipients);
+ break;
+ default :
+ cbdata.meeting_type = NOT_A_MEETING;
+ cbdata.resp = olResponseNone;
+ if (e_cal_component_has_attendees (comp))
+ exchange_mapi_cal_util_fetch_recipients (comp, &recipients);
+ break;
+ }
+
+ get_server_data (cbmapi, subcomp, &cbdata);
+ cbdata.username = e_cal_backend_mapi_get_user_name (cbmapi);
+ cbdata.useridtype = "SMTP";
+ cbdata.userid = e_cal_backend_mapi_get_user_email (cbmapi);
+ cbdata.ownername = e_cal_backend_mapi_get_owner_name (cbmapi);
+ cbdata.owneridtype = "SMTP";
+ cbdata.ownerid = e_cal_backend_mapi_get_owner_email (cbmapi);
+
+ mid = exchange_mapi_create_item (olFolderOutbox, 0,
+ exchange_mapi_cal_util_build_name_id, GINT_TO_POINTER(kind),
+ exchange_mapi_cal_util_build_props, &cbdata,
+ recipients, attachments, NULL, 0);
+ g_free (cbdata.props);
+ if (!mid) {
+ g_object_unref (comp);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_attachment_list (&attachments);
+ return GNOME_Evolution_Calendar_OtherError;
+ } else
+ status = GNOME_Evolution_Calendar_Success;
+
+ g_object_unref (comp);
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_attachment_list (&attachments);
+
+ subcomp = icalcomponent_get_next_component (icalcomp,
+ e_cal_backend_get_kind (E_CAL_BACKEND (backend)));
+ }
+ }
+
+ if (status == GNOME_Evolution_Calendar_Success)
+ *modified_calobj = g_strdup (calobj);
+
+ icalcomponent_free (icalcomp);
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj)
+{
+ ECalBackendSyncStatus status = GNOME_Evolution_Calendar_OtherError;
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ icalcomponent_kind kind;
+ icalcomponent *icalcomp;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+ kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
+
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GNOME_Evolution_Calendar_InvalidObject);
+ g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);
+
+ if (priv->mode == CAL_MODE_LOCAL)
+ return GNOME_Evolution_Calendar_RepositoryOffline;
+
+ /* check the component for validity */
+ icalcomp = icalparser_parse_string (calobj);
+ if (!icalcomp)
+ return GNOME_Evolution_Calendar_InvalidObject;
+
+ if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
+ gboolean stop = FALSE;
+ icalproperty_method method = icalcomponent_get_method (icalcomp);
+ icalcomponent *subcomp = icalcomponent_get_first_component (icalcomp, kind);
+ while (subcomp && !stop) {
+ ECalComponent *comp = e_cal_component_new ();
+ gchar *rid = NULL;
+ const char *uid;
+ gchar *old_object = NULL, *new_object = NULL, *comp_str;
+
+ e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp));
+
+ /* FIXME: Add support for recurrences */
+ if (e_cal_component_has_recurrences (comp)) {
+ g_object_unref (comp);
+ return GNOME_Evolution_Calendar_OtherError;
+ }
+
+ e_cal_component_get_uid (comp, &uid);
+ rid = e_cal_component_get_recurid_as_string (comp);
+
+ switch (method) {
+ case ICAL_METHOD_REQUEST :
+ comp_str = e_cal_component_get_as_string (comp);
+ status = e_cal_backend_mapi_modify_object (backend, cal, comp_str, CALOBJ_MOD_THIS, &old_object, &new_object);
+ g_free (comp_str);
+ g_free (old_object);
+ g_free (new_object);
+ if (status == GNOME_Evolution_Calendar_Success) {
+ GList *users = NULL, *l;
+ icalcomponent *resp_comp = e_cal_util_new_top_level ();
+ icalcomponent_set_method (resp_comp, ICAL_METHOD_RESPONSE);
+ icalcomponent_add_component (resp_comp,
+ icalcomponent_new_clone(e_cal_component_get_icalcomponent(comp)));
+ comp_str = icalcomponent_as_ical_string (resp_comp);
+ status = e_cal_backend_mapi_send_objects (backend, cal, comp_str, &users, &new_object);
+ g_free (comp_str);
+ g_free (new_object);
+ for (l = users; l; l = l->next)
+ g_free (l->data);
+ g_list_free (users);
+ icalcomponent_free (resp_comp);
+ }
+
+ if (status != GNOME_Evolution_Calendar_Success)
+ stop = TRUE;
+ break;
+ case ICAL_METHOD_CANCEL :
+ status = e_cal_backend_mapi_remove_object (backend, cal, uid, rid, CALOBJ_MOD_THIS, &old_object, &new_object);
+ if (status != GNOME_Evolution_Calendar_Success)
+ stop = TRUE;
+ g_free (old_object);
+ g_free (new_object);
+ break;
+ case ICAL_METHOD_REPLY :
+ /* responses are automatically updated even as they are rendered (just like in Outlook) */
+ status = GNOME_Evolution_Calendar_Success;
+ break;
+ default :
+ break;
+ }
+
+ g_free (rid);
+ g_object_unref (comp);
+
+ subcomp = icalcomponent_get_next_component (icalcomp,
+ e_cal_backend_get_kind (E_CAL_BACKEND (backend)));
+ }
+ }
+
+ return status;
+}
+
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ icaltimezone *zone;
+ icalcomponent *icalcomp;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
+
+ if (!strcmp (tzid, "UTC")) {
+ zone = icaltimezone_get_utc_timezone ();
+ } else {
+ zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
+ if (!zone)
+ return GNOME_Evolution_Calendar_ObjectNotFound;
+ }
+
+ icalcomp = icaltimezone_get_component (zone);
+ if (!icalcomp)
+ return GNOME_Evolution_Calendar_InvalidObject;
+
+ *object = icalcomponent_as_ical_string (icalcomp);
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ icalcomponent *tz_comp;
+
+ cbmapi = (ECalBackendMAPI *) backend;
+
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GNOME_Evolution_Calendar_OtherError);
+ g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
+
+ priv = cbmapi->priv;
+
+ tz_comp = icalparser_parse_string (tzobj);
+ if (!tz_comp)
+ return GNOME_Evolution_Calendar_InvalidObject;
+
+ if (icalcomponent_isa (tz_comp) == ICAL_VTIMEZONE_COMPONENT) {
+ icaltimezone *zone;
+ zone = icaltimezone_new ();
+ icaltimezone_set_component (zone, tz_comp);
+
+ if (e_cal_backend_cache_put_timezone (priv->cache, zone) == FALSE) {
+ icaltimezone_free (zone, 1);
+ return GNOME_Evolution_Calendar_OtherError;
+ }
+ icaltimezone_free (zone, 1);
+ }
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_set_default_zone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
+{
+ icalcomponent *tz_comp;
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ icaltimezone *zone;
+
+ cbmapi = (ECalBackendMAPI *) backend;
+
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GNOME_Evolution_Calendar_OtherError);
+ g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
+
+ priv = cbmapi->priv;
+
+ tz_comp = icalparser_parse_string (tzobj);
+ if (!tz_comp)
+ return GNOME_Evolution_Calendar_InvalidObject;
+
+ zone = icaltimezone_new ();
+ icaltimezone_set_component (zone, tz_comp);
+
+ if (priv->default_zone)
+ icaltimezone_free (priv->default_zone, 1);
+
+ /* Set the default timezone to it. */
+ priv->default_zone = zone;
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_get_free_busy (ECalBackendSync *backend, EDataCal *cal,
+ GList *users, time_t start, time_t end, GList **freebusy)
+{
+
+ return GNOME_Evolution_Calendar_Success;
+
+}
+
+typedef struct {
+ ECalBackendMAPI *backend;
+ icalcomponent_kind kind;
+ GList *deletes;
+ EXmlHash *ehash;
+} ECalBackendMAPIComputeChangesData;
+
+static void
+e_cal_backend_mapi_compute_changes_foreach_key (const char *key, const char *value, gpointer data)
+{
+ ECalBackendMAPIComputeChangesData *be_data = data;
+
+ if (!e_cal_backend_cache_get_component (be_data->backend->priv->cache, key, NULL)) {
+ ECalComponent *comp;
+
+ comp = e_cal_component_new ();
+ if (be_data->kind == ICAL_VTODO_COMPONENT)
+ e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
+ else
+ e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
+
+ e_cal_component_set_uid (comp, key);
+ be_data->deletes = g_list_prepend (be_data->deletes, e_cal_component_get_as_string (comp));
+
+ e_xmlhash_remove (be_data->ehash, key);
+ g_object_unref (comp);
+ }
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_compute_changes (ECalBackendMAPI *cbmapi, const char *change_id,
+ GList **adds, GList **modifies, GList **deletes)
+{
+ ECalBackendSyncStatus status;
+ ECalBackendCache *cache;
+ gchar *filename;
+ EXmlHash *ehash;
+ ECalBackendMAPIComputeChangesData be_data;
+ GList *i, *list = NULL;
+ gchar *unescaped_uri;
+
+ cache = cbmapi->priv->cache;
+
+ /* FIXME Will this always work? */
+ unescaped_uri = g_uri_unescape_string (cbmapi->priv->uri, "");
+ filename = g_strdup_printf ("%s-%s.db", unescaped_uri, change_id);
+ ehash = e_xmlhash_new (filename);
+ g_free (filename);
+ g_free (unescaped_uri);
+
+ status = e_cal_backend_mapi_get_object_list (E_CAL_BACKEND_SYNC (cbmapi), NULL, "#t", &list);
+ if (status != GNOME_Evolution_Calendar_Success)
+ return status;
+
+ /* Calculate adds and modifies */
+ for (i = list; i != NULL; i = g_list_next (i)) {
+ const char *uid;
+ char *calobj;
+ ECalComponent *comp;
+
+ comp = e_cal_component_new_from_string (i->data);
+ e_cal_component_get_uid (comp, &uid);
+ calobj = i->data;
+
+ g_assert (calobj != NULL);
+
+ /* check what type of change has occurred, if any */
+ switch (e_xmlhash_compare (ehash, uid, calobj)) {
+ case E_XMLHASH_STATUS_SAME:
+ break;
+ case E_XMLHASH_STATUS_NOT_FOUND:
+ *adds = g_list_prepend (*adds, g_strdup (calobj));
+ e_xmlhash_add (ehash, uid, calobj);
+ break;
+ case E_XMLHASH_STATUS_DIFFERENT:
+ *modifies = g_list_prepend (*modifies, g_strdup (calobj));
+ e_xmlhash_add (ehash, uid, calobj);
+ break;
+ }
+
+ g_free (calobj);
+ g_object_unref (comp);
+ }
+ g_list_free (list);
+
+ /* Calculate deletions */
+ be_data.backend = cbmapi;
+ be_data.kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
+ be_data.deletes = NULL;
+ be_data.ehash = ehash;
+ e_xmlhash_foreach_key (ehash, (EXmlHashFunc)e_cal_backend_mapi_compute_changes_foreach_key, &be_data);
+
+ *deletes = be_data.deletes;
+
+ e_xmlhash_write (ehash);
+ e_xmlhash_destroy (ehash);
+
+ return GNOME_Evolution_Calendar_Success;
+}
+
+static ECalBackendSyncStatus
+e_cal_backend_mapi_get_changes (ECalBackendSync *backend, EDataCal *cal, const char *change_id,
+ GList **adds, GList **modifies, GList **deletes)
+{
+ ECalBackendMAPI *cbmapi;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+
+ g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), GNOME_Evolution_Calendar_InvalidObject);
+ g_return_val_if_fail (change_id != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
+
+ return e_cal_backend_mapi_compute_changes (cbmapi, change_id, adds, modifies, deletes);
+
+}
+
+
+/***** BACKEND CLASS FUNCTIONS *****/
+static gboolean
+e_cal_backend_mapi_is_loaded (ECalBackend *backend)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ return priv->cache ? TRUE : FALSE;
+}
+
+static void
+e_cal_backend_mapi_start_query (ECalBackend *backend, EDataCalView *query)
+{
+ ECalBackendSyncStatus status;
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+ GList *objects = NULL;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ status = e_cal_backend_mapi_get_object_list (E_CAL_BACKEND_SYNC (backend), NULL,
+ e_data_cal_view_get_text (query), &objects);
+ if (status != GNOME_Evolution_Calendar_Success) {
+ e_data_cal_view_notify_done (query, status);
+ return;
+ }
+
+ /* notify listeners of all objects */
+ if (objects) {
+ e_data_cal_view_notify_objects_added (query, (const GList *) objects);
+ /* free memory */
+ g_list_foreach (objects, (GFunc) g_free, NULL);
+ g_list_free (objects);
+ }
+
+ e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_Success);
+}
+
+static CalMode
+e_cal_backend_mapi_get_mode (ECalBackend *backend)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ return priv->mode;
+}
+
+static void
+e_cal_backend_mapi_set_mode (ECalBackend *backend, CalMode mode)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ if (!priv->mode && priv->mode == mode) {
+ e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_SET,
+ cal_mode_to_corba (mode));
+ return;
+ }
+
+ g_mutex_lock (priv->mutex);
+
+ priv->mode_changed = TRUE;
+ switch (mode) {
+ case CAL_MODE_REMOTE:
+ priv->mode = CAL_MODE_REMOTE;
+ priv->read_only = FALSE;
+ e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_SET,
+ GNOME_Evolution_Calendar_MODE_REMOTE);
+ e_cal_backend_notify_readonly (backend, priv->read_only);
+ if (e_cal_backend_mapi_is_loaded (backend))
+ e_cal_backend_notify_auth_required(backend);
+ break;
+ case CAL_MODE_LOCAL:
+ priv->mode = CAL_MODE_LOCAL;
+ priv->read_only = TRUE;
+ /* do we have to close the connection here ? */
+ e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_SET,
+ GNOME_Evolution_Calendar_MODE_REMOTE);
+ e_cal_backend_notify_readonly (backend, priv->read_only);
+ break;
+ default:
+ e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED,
+ cal_mode_to_corba (mode));
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+static icaltimezone *
+e_cal_backend_mapi_internal_get_default_timezone (ECalBackend *backend)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ cbmapi = E_CAL_BACKEND_MAPI (backend);
+ priv = cbmapi->priv;
+
+ return priv->default_zone;
+}
+
+static icaltimezone *
+e_cal_backend_mapi_internal_get_timezone (ECalBackend *backend, const char *tzid)
+{
+ icaltimezone *zone;
+
+ zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
+
+ if (!zone && E_CAL_BACKEND_CLASS (parent_class)->internal_get_timezone)
+ zone = E_CAL_BACKEND_CLASS (parent_class)->internal_get_timezone (backend, tzid);
+
+ if (!zone)
+ return icaltimezone_get_utc_timezone ();
+
+ return zone;
+}
+
+
+/* MAPI CLASS INIT */
+static void
+e_cal_backend_mapi_class_init (ECalBackendMAPIClass *class)
+{
+ GObjectClass *object_class;
+ ECalBackendSyncClass *sync_class;
+ ECalBackendClass *backend_class;
+
+ object_class = (GObjectClass *) class;
+ sync_class = (ECalBackendSyncClass *) class;
+ backend_class = (ECalBackendClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = e_cal_backend_mapi_dispose;
+ object_class->finalize = e_cal_backend_mapi_finalize;
+
+ sync_class->is_read_only_sync = e_cal_backend_mapi_is_read_only;
+ sync_class->get_cal_address_sync = e_cal_backend_mapi_get_cal_address;
+ sync_class->get_alarm_email_address_sync = e_cal_backend_mapi_get_alarm_email_address;
+ sync_class->get_ldap_attribute_sync = e_cal_backend_mapi_get_ldap_attribute;
+ sync_class->get_static_capabilities_sync = e_cal_backend_mapi_get_static_capabilities;
+ sync_class->open_sync = e_cal_backend_mapi_open;
+ sync_class->remove_sync = e_cal_backend_mapi_remove;
+ sync_class->get_default_object_sync = e_cal_backend_mapi_get_default_object;
+ sync_class->get_object_sync = e_cal_backend_mapi_get_object;
+ sync_class->get_object_list_sync = e_cal_backend_mapi_get_object_list;
+ sync_class->get_attachment_list_sync = e_cal_backend_mapi_get_attachment_list;
+ sync_class->create_object_sync = e_cal_backend_mapi_create_object;
+ sync_class->modify_object_sync = e_cal_backend_mapi_modify_object;
+ sync_class->remove_object_sync = e_cal_backend_mapi_remove_object;
+ sync_class->discard_alarm_sync = e_cal_backend_mapi_discard_alarm;
+ sync_class->receive_objects_sync = e_cal_backend_mapi_receive_objects;
+ sync_class->send_objects_sync = e_cal_backend_mapi_send_objects;
+ sync_class->get_timezone_sync = e_cal_backend_mapi_get_timezone;
+ sync_class->add_timezone_sync = e_cal_backend_mapi_add_timezone;
+ sync_class->set_default_zone_sync = e_cal_backend_mapi_set_default_zone;
+ sync_class->get_freebusy_sync = e_cal_backend_mapi_get_free_busy;
+ sync_class->get_changes_sync = e_cal_backend_mapi_get_changes;
+
+ backend_class->is_loaded = e_cal_backend_mapi_is_loaded;
+ backend_class->start_query = e_cal_backend_mapi_start_query;
+ backend_class->get_mode = e_cal_backend_mapi_get_mode;
+ backend_class->set_mode = e_cal_backend_mapi_set_mode;
+ backend_class->internal_get_default_timezone = e_cal_backend_mapi_internal_get_default_timezone;
+ backend_class->internal_get_timezone = e_cal_backend_mapi_internal_get_timezone;
+}
+
+
+static void
+e_cal_backend_mapi_init (ECalBackendMAPI *cbmapi, ECalBackendMAPIClass *class)
+{
+ ECalBackendMAPIPrivate *priv;
+
+ priv = g_new0 (ECalBackendMAPIPrivate, 1);
+
+ priv->timeout_id = 0;
+ priv->sendoptions_sync_timeout = 0;
+
+ /* create the mutex for thread safety */
+ priv->mutex = g_mutex_new ();
+
+ cbmapi->priv = priv;
+
+ e_cal_backend_sync_set_lock (E_CAL_BACKEND_SYNC (cbmapi), TRUE);
+}
+
+GType
+e_cal_backend_mapi_get_type (void)
+{
+ static GType e_cal_backend_mapi_type = 0;
+
+ if (!e_cal_backend_mapi_type) {
+ static GTypeInfo info = {
+ sizeof (ECalBackendMAPIClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) e_cal_backend_mapi_class_init,
+ NULL, NULL,
+ sizeof (ECalBackendMAPI),
+ 0,
+ (GInstanceInitFunc) e_cal_backend_mapi_init
+ };
+ e_cal_backend_mapi_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC,
+ "ECalBackendMAPI", &info, 0);
+ }
+
+ return e_cal_backend_mapi_type;
+}
+
+
+/***** UTILITY FUNCTIONS *****/
+const char *
+e_cal_backend_mapi_get_local_attachments_store (ECalBackendMAPI *cbmapi)
+{
+ ECalBackendMAPIPrivate *priv;
+
+ priv = cbmapi->priv;
+
+ return priv->local_attachments_store;
+}
+
+const char *
+e_cal_backend_mapi_get_owner_name (ECalBackendMAPI *cbmapi)
+{
+ ECalBackendMAPIPrivate *priv;
+
+ priv = cbmapi->priv;
+
+ return priv->owner_name;
+}
+
+const char *
+e_cal_backend_mapi_get_owner_email (ECalBackendMAPI *cbmapi)
+{
+ ECalBackendMAPIPrivate *priv;
+
+ priv = cbmapi->priv;
+
+ return priv->owner_email;
+}
+
+const char *
+e_cal_backend_mapi_get_user_name (ECalBackendMAPI *cbmapi)
+{
+ ECalBackendMAPIPrivate *priv;
+
+ priv = cbmapi->priv;
+
+ return priv->user_name;
+}
+
+const char *
+e_cal_backend_mapi_get_user_email (ECalBackendMAPI *cbmapi)
+{
+ ECalBackendMAPIPrivate *priv;
+
+ priv = cbmapi->priv;
+
+ return priv->user_email;
+}
Added: trunk/src/calendar/e-cal-backend-mapi.h
==============================================================================
--- (empty file)
+++ trunk/src/calendar/e-cal-backend-mapi.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_CAL_BACKEND_MAPI_H
+#define E_CAL_BACKEND_MAPI_H
+
+#include <glib.h>
+
+#include <libedata-cal/e-cal-backend-sync.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_CAL_BACKEND_MAPI (e_cal_backend_mapi_get_type ())
+#define E_CAL_BACKEND_MAPI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CAL_BACKEND_MAPI, ECalBackendMAPI))
+#define E_CAL_BACKEND_MAPI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CAL_BACKEND_MAPI, ECalBackendMAPIClass))
+#define E_IS_CAL_BACKEND_MAPI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CAL_BACKEND_MAPI))
+#define E_IS_CAL_BACKEND_MAPI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_CAL_BACKEND_MAPI))
+
+typedef struct _ECalBackendMAPI ECalBackendMAPI;
+typedef struct _ECalBackendMAPIClass ECalBackendMAPIClass;
+typedef struct _ECalBackendMAPIPrivate ECalBackendMAPIPrivate;
+
+struct _ECalBackendMAPI {
+ ECalBackendSync backend;
+
+ /* Private data */
+ ECalBackendMAPIPrivate *priv;
+};
+
+struct _ECalBackendMAPIClass {
+ ECalBackendSyncClass parent_class;
+};
+
+GType e_cal_backend_mapi_get_type(void);
+
+const char *
+e_cal_backend_mapi_get_local_attachments_store (ECalBackendMAPI *cbmapi);
+
+const char *
+e_cal_backend_mapi_get_owner_name (ECalBackendMAPI *cbmapi);
+const char *
+e_cal_backend_mapi_get_owner_email (ECalBackendMAPI *cbmapi);
+
+const char *
+e_cal_backend_mapi_get_user_name (ECalBackendMAPI *cbmapi);
+const char *
+e_cal_backend_mapi_get_user_email (ECalBackendMAPI *cbmapi);
+
+G_END_DECLS
+
+#endif /* E_CAL_BACKEND_MAPI_H */
+
Added: trunk/src/camel/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/camel/Makefile.am Wed Nov 19 04:28:20 2008
@@ -0,0 +1,35 @@
+## Process this file with automake to produce Makefile.in
+
+camel_provider_LTLIBRARIES = libcamelmapi.la
+camel_provider_DATA = libcamelmapi.urls
+
+INCLUDES = -I.. \
+ -I$(top_srcdir)/src/camel \
+ -I$(top_srcdir)/src/libexchangemapi \
+ $(CAMEL_CFLAGS) \
+ $(LIBMAPI_CFLAGS) \
+ -DG_LOG_DOMAIN=\"camel-mapi-provider\"
+
+libcamelmapi_la_SOURCES = \
+ camel-mapi-provider.c \
+ camel-mapi-folder.c \
+ camel-mapi-store.c \
+ camel-mapi-store-summary.c \
+ camel-mapi-summary.c \
+ camel-mapi-transport.c
+
+noinst_HEADERS = \
+ camel-mapi-folder.h \
+ camel-mapi-store.h \
+ camel-mapi-summary.h \
+ camel-mapi-transport.h \
+ camel-mapi-private.h \
+ camel-private.h
+
+libcamelmapi_la_LDFLAGS = -avoid-version -module $(NO_UNDEFINED)
+libcamelmapi_la_LIBADD = \
+ $(top_builddir)/src/libexchangemapi/libexchangemapi-1.0.la \
+ $(CAMEL_LIBS) \
+ $(LIBMAPI_LIBS)
+
+EXTRA_DIST = libcamelmapi.urls
Added: trunk/src/camel/camel-mapi-folder.c
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-folder.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,1590 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include <pthread.h>
+#include <string.h>
+#include <time.h>
+
+#include <camel/camel-folder-search.h>
+#include <camel/camel-mime-part.h>
+#include <camel/camel-mime-utils.h>
+#include <camel/camel-string-utils.h>
+#include <camel/camel-object.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-data-wrapper.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-private.h>
+#include <camel/camel-stream-buffer.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-debug.h>
+
+#include <libmapi/libmapi.h>
+#include <exchange-mapi-defs.h>
+#include <exchange-mapi-utils.h>
+#include <exchange-mapi-folder.h>
+
+#include "camel-mapi-store.h"
+#include "camel-mapi-folder.h"
+#include "camel-mapi-private.h"
+#include "camel-mapi-summary.h"
+
+#define DEBUG_FN( ) printf("----%u %s\n", (unsigned int)pthread_self(), __FUNCTION__);
+#define d(x)
+
+static CamelOfflineFolderClass *parent_class = NULL;
+
+struct _CamelMapiFolderPrivate {
+
+//#ifdef ENABLE_THREADS
+ GStaticMutex search_lock; /* for locking the search object */
+ GStaticRecMutex cache_lock; /* for locking the cache object */
+//#endif
+
+};
+
+/*for syncing flags back to server*/
+typedef struct {
+ guint32 changed;
+ guint32 bits;
+} flags_diff_t;
+
+/*For collecting summary info from server*/
+typedef struct {
+ GSList *items_list;
+ const struct timeval *last_modification_time;
+}fetch_items_data;
+
+static CamelMimeMessage *mapi_folder_item_to_msg( CamelFolder *folder, MapiItem *item, CamelException *ex );
+
+static GPtrArray *
+mapi_folder_search_by_expression (CamelFolder *folder, const char *expression, CamelException *ex)
+{
+ CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER(folder);
+ GPtrArray *matches;
+
+ CAMEL_MAPI_FOLDER_LOCK(mapi_folder, search_lock);
+ camel_folder_search_set_folder (mapi_folder->search, folder);
+ matches = camel_folder_search_search(mapi_folder->search, expression, NULL, ex);
+ CAMEL_MAPI_FOLDER_UNLOCK(mapi_folder, search_lock);
+
+ return matches;
+}
+
+
+static int
+mapi_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args)
+{
+ CamelFolder *folder = (CamelFolder *)object;
+ int i, count = 0;
+ guint32 tag;
+
+ for (i=0 ; i<args->argc ; i++) {
+ CamelArgGet *arg = &args->argv[i];
+
+ tag = arg->tag;
+
+ switch (tag & CAMEL_ARG_TAG) {
+
+ case CAMEL_OBJECT_ARG_DESCRIPTION:
+ if (folder->description == NULL) {
+ CamelURL *uri = ((CamelService *)folder->parent_store)->url;
+
+ folder->description = g_strdup_printf("%s %s:%s", uri->user, uri->host, folder->full_name);
+ }
+ *arg->ca_str = folder->description;
+ break;
+ default:
+ count++;
+ continue;
+ }
+
+ arg->tag = (tag & CAMEL_ARG_TYPE) | CAMEL_ARG_IGNORE;
+ }
+
+ if (count)
+ return ((CamelObjectClass *)parent_class)->getv(object, ex, args);
+
+ return 0;
+
+}
+
+static void
+mapi_refresh_info(CamelFolder *folder, CamelException *ex)
+{
+ CamelStoreInfo *si;
+ /*
+ * Checking for the summary->time_string here since the first the a
+ * user views a folder, the read cursor is in progress, and the getQM
+ * should not interfere with the process
+ */
+ // if (summary->time_string && (strlen (summary->time_string) > 0)) {
+ if(1){
+ mapi_refresh_folder(folder, ex);
+ si = camel_store_summary_path ((CamelStoreSummary *)((CamelMapiStore *)folder->parent_store)->summary, folder->full_name);
+
+ if (si) {
+ guint32 unread, total;
+ camel_object_get (folder, NULL, CAMEL_FOLDER_TOTAL, &total, CAMEL_FOLDER_UNREAD, &unread, NULL);
+ if (si->total != total || si->unread != unread) {
+ si->total = total;
+ si->unread = unread;
+ camel_store_summary_touch ((CamelStoreSummary *)((CamelMapiStore *)folder->parent_store)->summary);
+ }
+ camel_store_summary_info_free ((CamelStoreSummary *)((CamelMapiStore *)folder->parent_store)->summary, si);
+ }
+ camel_folder_summary_save_to_db (folder->summary, ex);
+ camel_store_summary_save ((CamelStoreSummary *)((CamelMapiStore *)folder->parent_store)->summary);
+ } else {
+ /* We probably could not get the messages the first time. (get_folder) failed???!
+ * so do a get_folder again. And hope that it works
+ */
+ g_print("Reloading folder...something wrong with the summary....\n");
+ }
+ //#endif
+
+}
+
+static void
+mapi_item_free (MapiItem *item)
+{
+ g_free (item->header.subject);
+ g_free (item->header.from);
+ g_free (item->header.to);
+ g_free (item->header.cc);
+ g_free (item->header.bcc);
+
+ exchange_mapi_util_free_attachment_list (&item->attachments);
+ exchange_mapi_util_free_stream_list (&item->generic_streams);
+}
+static gboolean
+fetch_items_cb (FetchItemsCallbackData *item_data, gpointer data)
+{
+ fetch_items_data *fi_data = (fetch_items_data *)data;
+
+ GSList **slist = &(fi_data->items_list);
+
+ long *flags;
+ struct FILETIME *delivery_date = NULL;
+ struct FILETIME *last_modification_time = NULL;
+ struct timeval *item_modification_time = NULL;
+ guint32 j = 0;
+ NTTIME ntdate;
+
+ MapiItem *item = g_new0(MapiItem , 1);
+
+ if (camel_debug_start("mapi:folder")) {
+ exchange_mapi_debug_property_dump (item_data->properties);
+ camel_debug_end();
+ }
+
+ item->fid = item_data->fid;
+ item->mid = item_data->mid;
+
+ for (j = 0; j < item_data->properties->cValues; j++) {
+
+ gpointer prop_data = get_mapi_SPropValue_data(&item_data->properties->lpProps[j]);
+
+ switch (item_data->properties->lpProps[j].ulPropTag) {
+ /* FIXME : Instead of duping. Use talloc_steal to reuse the memory */
+ case PR_NORMALIZED_SUBJECT:
+ case PR_NORMALIZED_SUBJECT_UNICODE :
+ item->header.subject = g_strdup (prop_data);
+ break;
+ case PR_DISPLAY_TO :
+ case PR_DISPLAY_TO_UNICODE :
+ item->header.to = g_strdup (prop_data);
+ break;
+ case PR_DISPLAY_CC:
+ case PR_DISPLAY_CC_UNICODE:
+ item->header.cc = g_strdup (prop_data);
+ break;
+ case PR_DISPLAY_BCC:
+ case PR_DISPLAY_BCC_UNICODE:
+ item->header.bcc = g_strdup (prop_data);
+ break;
+ case PR_SENT_REPRESENTING_NAME:
+ case PR_SENT_REPRESENTING_NAME_UNICODE:
+ item->header.from = g_strdup (prop_data);
+ break;
+ case PR_MESSAGE_SIZE:
+ item->header.size = *(glong *)prop_data;
+ break;
+ case PR_MESSAGE_DELIVERY_TIME:
+ delivery_date = (struct FILETIME *) prop_data;
+ break;
+ case PR_LAST_MODIFICATION_TIME:
+ last_modification_time = (struct FILETIME *) prop_data;
+ break;
+ case PR_MESSAGE_FLAGS:
+ flags = (long *) prop_data;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (delivery_date) {
+ ntdate = delivery_date->dwHighDateTime;
+ ntdate = ntdate << 32;
+ ntdate |= delivery_date->dwLowDateTime;
+ item->header.recieved_time = nt_time_to_unix(ntdate);
+ }
+
+ if (last_modification_time) {
+ ntdate = last_modification_time->dwHighDateTime;
+ ntdate = ntdate << 32;
+ ntdate |= last_modification_time->dwLowDateTime;
+ item_modification_time = g_new0 (struct timeval, 1);
+ nttime_to_timeval(item_modification_time, ntdate);
+ }
+
+ if (timeval_compare (item_modification_time, fi_data->last_modification_time) == 1)
+ fi_data->last_modification_time = item_modification_time;
+
+ if ((*flags & MSGFLAG_READ) != 0)
+ item->header.flags |= CAMEL_MESSAGE_SEEN;
+ if ((*flags & MSGFLAG_HASATTACH) != 0)
+ item->header.flags |= CAMEL_MESSAGE_ATTACHMENTS;
+
+ *slist = g_slist_prepend (*slist, item);
+
+ if (item_data->total > 0)
+ camel_operation_progress (NULL, (item_data->index * 100)/item_data->total);
+
+ return TRUE;
+}
+
+static void
+mapi_update_cache (CamelFolder *folder, GSList *list, CamelException *ex, gboolean uid_flag)
+{
+ CamelMapiMessageInfo *mi = NULL;
+ CamelMessageInfo *pmi = NULL;
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (folder->parent_store);
+
+ guint32 status_flags = 0;
+ CamelFolderChangeInfo *changes = NULL;
+ gboolean exists = FALSE;
+ GString *str = g_string_new (NULL);
+ const gchar *folder_id = NULL;
+ GSList *item_list = list;
+ int total_items = g_slist_length (item_list), i=0;
+
+ changes = camel_folder_change_info_new ();
+ folder_id = camel_mapi_store_folder_id_lookup (mapi_store, folder->full_name);
+
+ if (!folder_id) {
+ d(printf("\nERROR - Folder id not present. Cannot refresh info\n"));
+ camel_folder_change_info_free (changes);
+ return;
+ }
+
+ camel_operation_start (NULL, _("Fetching summary information for new messages in %s"), folder->name);
+
+ for ( ; item_list != NULL ; item_list = g_slist_next (item_list) ) {
+ MapiItem *temp_item ;
+ MapiItem *item;
+ guint64 id;
+
+ exists = FALSE;
+ status_flags = 0;
+
+ if (uid_flag == FALSE) {
+ temp_item = (MapiItem *)item_list->data;
+ id = temp_item->mid;
+ item = temp_item;
+ }
+
+ camel_operation_progress (NULL, (100*i)/total_items);
+
+ /************************ First populate summary *************************/
+ mi = NULL;
+ pmi = NULL;
+ char *msg_uid = exchange_mapi_util_mapi_ids_to_uid (item->fid, item->mid);
+ pmi = camel_folder_summary_uid (folder->summary, msg_uid);
+
+ if (pmi) {
+ exists = TRUE;
+ camel_message_info_ref (pmi);
+ mi = (CamelMapiMessageInfo *)pmi;
+ }
+
+ if (!exists) {
+ mi = (CamelMapiMessageInfo *)camel_message_info_new (folder->summary);
+ if (mi->info.content == NULL) {
+ mi->info.content = camel_folder_summary_content_info_new (folder->summary);
+ mi->info.content->type = camel_content_type_new ("multipart", "mixed");
+ }
+ }
+
+ mi->info.flags = item->header.flags;
+
+ if (!exists) {
+ mi->info.uid = g_strdup (exchange_mapi_util_mapi_ids_to_uid(item->fid, item->mid));
+ mi->info.subject = camel_pstring_strdup(item->header.subject);
+ mi->info.date_sent = mi->info.date_received = item->header.recieved_time;
+ mi->info.from = camel_pstring_strdup (item->header.from);
+ mi->info.to = camel_pstring_strdup (item->header.to);
+ mi->info.size = (guint32) item->header.size;
+ }
+
+ if (exists) {
+ camel_folder_change_info_change_uid (changes, mi->info.uid);
+ camel_message_info_free (pmi);
+ } else {
+ camel_folder_summary_add (folder->summary,(CamelMessageInfo *)mi);
+ camel_folder_change_info_add_uid (changes, mi->info.uid);
+ camel_folder_change_info_recent_uid (changes, mi->info.uid);
+ }
+
+ /********************* Summary ends *************************/
+ if (!strcmp (folder->full_name, "Junk Mail"))
+ continue;
+
+ g_free (msg_uid);
+ i++;
+ }
+ camel_operation_end (NULL);
+
+ g_string_free (str, TRUE);
+ camel_object_trigger_event (folder, "folder_changed", changes);
+
+ camel_folder_change_info_free (changes);
+}
+
+static void
+mapi_sync_summary (CamelFolder *folder, CamelException *ex)
+{
+ camel_folder_summary_save_to_db (folder->summary, ex);
+ camel_store_summary_touch ((CamelStoreSummary *)((CamelMapiStore *)folder->parent_store)->summary);
+ camel_store_summary_save ((CamelStoreSummary *)((CamelMapiStore *)folder->parent_store)->summary);
+}
+
+static void
+mapi_utils_do_flags_diff (flags_diff_t *diff, guint32 old, guint32 _new)
+{
+ diff->changed = old ^ _new;
+ diff->bits = _new & diff->changed;
+}
+
+static void
+mapi_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
+{
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (folder->parent_store);
+ CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER (folder);
+ CamelMessageInfo *info = NULL;
+ CamelMapiMessageInfo *mapi_info = NULL;
+
+ GSList *read_items = NULL, *unread_items = NULL;
+ flags_diff_t diff, unset_flags;
+ const char *folder_id;
+ mapi_id_t fid, deleted_items_fid;
+ int count, i;
+ guint32 options =0;
+
+ GSList *deleted_items, *deleted_head;
+ deleted_items = deleted_head = NULL;
+
+ if (((CamelOfflineStore *) mapi_store)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL ||
+ ((CamelService *)mapi_store)->status == CAMEL_SERVICE_DISCONNECTED) {
+ mapi_sync_summary (folder, ex);
+ return;
+ }
+
+ if (((CamelMapiFolder *)folder)->type == MAPI_FAVOURITE_FOLDER){
+ options |= MAPI_OPTIONS_USE_PFSTORE;
+ }
+
+ folder_id = camel_mapi_store_folder_id_lookup (mapi_store, folder->full_name) ;
+ exchange_mapi_util_mapi_id_from_string (folder_id, &fid);
+
+ CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
+ if (!camel_mapi_store_connected (mapi_store, ex)) {
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+ camel_exception_clear (ex);
+ return;
+ }
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+
+ count = camel_folder_summary_count (folder->summary);
+ CAMEL_MAPI_FOLDER_REC_LOCK (folder, cache_lock);
+ for (i=0 ; i < count ; i++) {
+ info = camel_folder_summary_index (folder->summary, i);
+ mapi_info = (CamelMapiMessageInfo *) info;
+
+ if (mapi_info && (mapi_info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) {
+ const char *uid;
+ mapi_id_t *mid = g_new0 (mapi_id_t, 1); /* FIXME : */
+ mapi_id_t temp_fid;
+
+ uid = camel_message_info_uid (info);
+ guint32 flags= camel_message_info_flags (info);
+
+ /* Why are we getting so much noise here :-/ */
+ if (!exchange_mapi_util_mapi_ids_from_uid (uid, &temp_fid, mid))
+ continue;
+
+ mapi_utils_do_flags_diff (&diff, mapi_info->server_flags, mapi_info->info.flags);
+ mapi_utils_do_flags_diff (&unset_flags, flags, mapi_info->server_flags);
+
+ diff.changed &= folder->permanent_flags;
+ if (!diff.changed) {
+ camel_message_info_free(info);
+ continue;
+ } else {
+ if (diff.bits & CAMEL_MESSAGE_DELETED) {
+ if (diff.bits & CAMEL_MESSAGE_SEEN)
+ read_items = g_slist_prepend (read_items, mid);
+ if (deleted_items)
+ deleted_items = g_slist_prepend (deleted_items, mid);
+ else {
+ g_slist_free (deleted_head);
+ deleted_head = NULL;
+ deleted_head = deleted_items = g_slist_prepend (deleted_items, mid);
+ }
+
+ CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
+
+ }
+ }
+
+ if (diff.bits & CAMEL_MESSAGE_SEEN) {
+ read_items = g_slist_prepend (read_items, mid);
+ } else if (unset_flags.bits & CAMEL_MESSAGE_SEEN) {
+ unread_items = g_slist_prepend (unread_items, mid);
+ }
+ }
+ camel_message_info_free (info);
+ }
+
+ CAMEL_MAPI_FOLDER_REC_UNLOCK (folder, cache_lock);
+
+ /*
+ Sync up the READ changes before deleting the message.
+ Note that if a message is marked as unread and then deleted,
+ Evo doesnt not take care of it, as I find that scenario to be impractical.
+ */
+
+ if (read_items) {
+ CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
+ exchange_mapi_set_flags (0, fid, read_items, 0, options);
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+ g_slist_free (read_items);
+ }
+
+ if (deleted_items) {
+ CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
+ if (mapi_folder->type & CAMEL_FOLDER_TYPE_TRASH) {
+ exchange_mapi_remove_items (0, fid, deleted_items);
+ } else {
+ exchange_mapi_util_mapi_id_from_string (camel_mapi_store_system_folder_fid (mapi_store, olFolderDeletedItems), &deleted_items_fid);
+ exchange_mapi_move_items(fid, deleted_items_fid, deleted_items);
+ }
+
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+ }
+ /*Remove them from cache*/
+ while (deleted_items) {
+ char* deleted_msg_uid = g_strdup_printf ("%016llX%016llX", fid, *(mapi_id_t *)deleted_items->data);
+
+ CAMEL_MAPI_FOLDER_REC_LOCK (folder, cache_lock);
+ camel_folder_summary_remove_uid (folder->summary, deleted_msg_uid);
+ camel_data_cache_remove(mapi_folder->cache, "cache", deleted_msg_uid, NULL);
+ CAMEL_MAPI_FOLDER_REC_UNLOCK (folder, cache_lock);
+
+ deleted_items = g_slist_next (deleted_items);
+ }
+
+
+ if (unread_items) {
+ CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
+ /* TODO */
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+ g_slist_free (unread_items);
+ }
+
+ if (expunge) {
+ CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
+ /* TODO */
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+ }
+
+ CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
+ mapi_sync_summary (folder, ex);
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+}
+
+
+void
+mapi_refresh_folder(CamelFolder *folder, CamelException *ex)
+{
+
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (folder->parent_store);
+ CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER (folder);
+ CamelMapiSummary *mapi_summary = CAMEL_MAPI_SUMMARY (folder->summary);
+ gboolean is_proxy = folder->parent_store->flags & CAMEL_STORE_PROXY;
+ gboolean is_locked = TRUE;
+ gboolean status;
+
+ struct mapi_SRestriction *res = NULL;
+ fetch_items_data *fetch_data = g_new0 (fetch_items_data, 1);
+
+ const gchar *folder_id = NULL;
+
+ const guint32 summary_prop_list[] = {
+ PR_NORMALIZED_SUBJECT,
+ PR_MESSAGE_SIZE,
+ PR_MESSAGE_DELIVERY_TIME,
+ PR_MESSAGE_FLAGS,
+ PR_SENT_REPRESENTING_NAME,
+ PR_LAST_MODIFICATION_TIME,
+ PR_DISPLAY_TO,
+ PR_DISPLAY_CC,
+ PR_DISPLAY_BCC
+ };
+
+ if (((CamelOfflineStore *) mapi_store)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL)
+ return;
+
+ /* Sync-up the (un)read changes before getting updates,
+ so that the getFolderList will reflect the most recent changes too */
+ mapi_sync (folder, FALSE, ex);
+
+ //creating a copy
+ folder_id = camel_mapi_store_folder_id_lookup (mapi_store, folder->full_name);
+ if (!folder_id) {
+ d(printf ("\nERROR - Folder id not present. Cannot refresh info for %s\n", folder->full_name));
+ return;
+ }
+
+ if (camel_folder_is_frozen (folder) ) {
+ mapi_folder->need_refresh = TRUE;
+ }
+
+ CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
+
+ if (!camel_mapi_store_connected (mapi_store, ex))
+ goto end1;
+
+ /*Get the New Items*/
+ if (!is_proxy) {
+ mapi_id_t temp_folder_id;
+ guint32 options = 0;
+
+ fetch_data->last_modification_time = g_new0 (struct timeval, 1); /*First Sync*/
+
+ if (mapi_summary->sync_time_stamp && *mapi_summary->sync_time_stamp &&
+ g_time_val_from_iso8601 (mapi_summary->sync_time_stamp, fetch_data->last_modification_time)) {
+ struct SPropValue sprop;
+ struct timeval t;
+
+ res = g_new0 (struct mapi_SRestriction, 1);
+ res->rt = RES_PROPERTY;
+ /*RELOP_GE acts more like >=. Few extra items are being fetched.*/
+ res->res.resProperty.relop = RELOP_GE;
+ res->res.resProperty.ulPropTag = PR_LAST_MODIFICATION_TIME;
+
+ t.tv_sec = fetch_data->last_modification_time->tv_sec;
+ t.tv_usec = fetch_data->last_modification_time->tv_usec;
+
+ //Creation time ?
+ set_SPropValue_proptag_date_timeval (&sprop, PR_LAST_MODIFICATION_TIME, &t);
+ cast_mapi_SPropValue (&(res->res.resProperty.lpProp), &sprop);
+ }
+
+ exchange_mapi_util_mapi_id_from_string (folder_id, &temp_folder_id);
+
+ if (!camel_mapi_store_connected (mapi_store, ex)) {
+ /*BUG : Fix exception string.*/
+ camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+ _("This message is not available in offline mode."));
+ goto end2;
+ }
+
+ if (((CamelMapiFolder *)folder)->type == MAPI_FAVOURITE_FOLDER)
+ options |= MAPI_OPTIONS_USE_PFSTORE;
+
+ camel_operation_start (NULL, _("Fetching summary information for new messages in %s"), folder->name);
+
+ status = exchange_mapi_connection_fetch_items (temp_folder_id, res,
+ summary_prop_list, G_N_ELEMENTS (summary_prop_list),
+ NULL, NULL,
+ fetch_items_cb, fetch_data,
+ options);
+ camel_operation_end (NULL);
+
+ if (!status) {
+ camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_INVALID, _("Fetch items failed"));
+ goto end2;
+ }
+
+ /*Preserve last_modification_time from this fetch for later use with restrictions.*/
+ mapi_summary->sync_time_stamp = g_time_val_to_iso8601 (fetch_data->last_modification_time);
+
+ camel_folder_summary_touch (folder->summary);
+ mapi_sync_summary (folder, ex);
+
+ if (fetch_data->items_list)
+ mapi_update_cache (folder, fetch_data->items_list, ex, FALSE);
+ }
+
+
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+ is_locked = FALSE;
+
+ g_slist_foreach (fetch_data->items_list, (GFunc) mapi_item_free, NULL);
+ g_slist_free (fetch_data->items_list);
+end2:
+ //TODO:
+end1:
+ if (is_locked)
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+ return;
+
+}
+
+static const uint32_t camel_GetPropsList[] = {
+ PR_FID,
+ PR_MID,
+
+ PR_MESSAGE_CLASS,
+ PR_MESSAGE_CLASS_UNICODE,
+ PR_MESSAGE_SIZE,
+ PR_MESSAGE_FLAGS,
+ PR_MESSAGE_DELIVERY_TIME,
+ PR_MSG_EDITOR_FORMAT,
+
+ PR_SUBJECT,
+ PR_SUBJECT_UNICODE,
+ PR_NORMALIZED_SUBJECT,
+ PR_NORMALIZED_SUBJECT_UNICODE,
+ PR_CONVERSATION_TOPIC,
+ PR_CONVERSATION_TOPIC_UNICODE,
+
+ PR_BODY,
+ PR_BODY_UNICODE,
+ PR_HTML,
+ /*Fixme : If this property is fetched, it garbles everything else. */
+ /*PR_BODY_HTML, */
+ /*PR_BODY_HTML_UNICODE, */
+
+ PR_DISPLAY_TO,
+ PR_DISPLAY_TO_UNICODE,
+ PR_DISPLAY_CC,
+ PR_DISPLAY_CC_UNICODE,
+ PR_DISPLAY_BCC,
+ PR_DISPLAY_BCC_UNICODE,
+
+ PR_CREATION_TIME,
+ PR_LAST_MODIFICATION_TIME,
+ PR_PRIORITY,
+ PR_SENSITIVITY,
+ PR_START_DATE,
+ PR_END_DATE,
+ PR_RESPONSE_REQUESTED,
+ PR_OWNER_APPT_ID,
+ PR_PROCESSED,
+
+ PR_SENT_REPRESENTING_NAME,
+ PR_SENT_REPRESENTING_NAME_UNICODE,
+ PR_SENT_REPRESENTING_ADDRTYPE,
+ PR_SENT_REPRESENTING_ADDRTYPE_UNICODE,
+ PR_SENT_REPRESENTING_EMAIL_ADDRESS,
+ PR_SENT_REPRESENTING_EMAIL_ADDRESS_UNICODE,
+
+ PR_SENDER_NAME,
+ PR_SENDER_NAME_UNICODE,
+ PR_SENDER_ADDRTYPE,
+ PR_SENDER_ADDRTYPE_UNICODE,
+ PR_SENDER_EMAIL_ADDRESS,
+ PR_SENDER_EMAIL_ADDRESS_UNICODE,
+
+ PR_RCVD_REPRESENTING_NAME,
+ PR_RCVD_REPRESENTING_NAME_UNICODE,
+ PR_RCVD_REPRESENTING_ADDRTYPE,
+ PR_RCVD_REPRESENTING_ADDRTYPE_UNICODE,
+ PR_RCVD_REPRESENTING_EMAIL_ADDRESS,
+ PR_RCVD_REPRESENTING_EMAIL_ADDRESS_UNICODE
+};
+
+static gboolean
+camel_build_name_id (struct mapi_nameid *nameid, gpointer data)
+{
+ mapi_nameid_lid_add(nameid, 0x8501, PSETID_Common); // PT_LONG - ReminderMinutesBeforeStart
+ mapi_nameid_lid_add(nameid, 0x8502, PSETID_Common); // PT_SYSTIME - ReminderTime
+ mapi_nameid_lid_add(nameid, 0x8503, PSETID_Common); // PT_BOOLEAN - ReminderSet
+ mapi_nameid_lid_add(nameid, 0x8506, PSETID_Common); // PT_BOOLEAN - Private
+ mapi_nameid_lid_add(nameid, 0x8510, PSETID_Common); // PT_LONG - (context menu flags)
+ mapi_nameid_lid_add(nameid, 0x8516, PSETID_Common); // PT_SYSTIME - CommonStart
+ mapi_nameid_lid_add(nameid, 0x8517, PSETID_Common); // PT_SYSTIME - CommonEnd
+ mapi_nameid_lid_add(nameid, 0x8560, PSETID_Common); // PT_SYSTIME - ReminderNextTime
+
+ mapi_nameid_lid_add(nameid, 0x8201, PSETID_Appointment); // PT_LONG - ApptSequence
+ mapi_nameid_lid_add(nameid, 0x8205, PSETID_Appointment); // PT_LONG - BusyStatus
+ mapi_nameid_lid_add(nameid, 0x8208, PSETID_Appointment); // PT_UNICODE - Location
+ mapi_nameid_lid_add(nameid, 0x820D, PSETID_Appointment); // PT_SYSTIME - Start/ApptStartWhole
+ mapi_nameid_lid_add(nameid, 0x820E, PSETID_Appointment); // PT_SYSTIME - End/ApptEndWhole
+ mapi_nameid_lid_add(nameid, 0x8213, PSETID_Appointment); // PT_LONG - Duration/ApptDuration
+ mapi_nameid_lid_add(nameid, 0x8215, PSETID_Appointment); // PT_BOOLEAN - AllDayEvent (also called ApptSubType)
+ mapi_nameid_lid_add(nameid, 0x8216, PSETID_Appointment); // PT_BINARY - (recurrence blob)
+ mapi_nameid_lid_add(nameid, 0x8217, PSETID_Appointment); // PT_LONG - MeetingStatus
+ mapi_nameid_lid_add(nameid, 0x8218, PSETID_Appointment); // PT_LONG - ResponseStatus
+ mapi_nameid_lid_add(nameid, 0x8223, PSETID_Appointment); // PT_BOOLEAN - Recurring
+ mapi_nameid_lid_add(nameid, 0x8224, PSETID_Appointment); // PT_LONG - IntendedBusyStatus
+ mapi_nameid_lid_add(nameid, 0x8228, PSETID_Appointment); // PT_SYSTIME - RecurrenceBase
+ mapi_nameid_lid_add(nameid, 0x8229, PSETID_Appointment); // PT_BOOLEAN - FInvited
+ mapi_nameid_lid_add(nameid, 0x8231, PSETID_Appointment); // PT_LONG - RecurrenceType
+ mapi_nameid_lid_add(nameid, 0x8232, PSETID_Appointment); // PT_STRING8 - RecurrencePattern
+ mapi_nameid_lid_add(nameid, 0x8235, PSETID_Appointment); // PT_SYSTIME - (dtstart)(for recurring events UTC 12 AM of day of start)
+ mapi_nameid_lid_add(nameid, 0x8236, PSETID_Appointment); // PT_SYSTIME - (dtend)(for recurring events UTC 12 AM of day of end)
+ mapi_nameid_lid_add(nameid, 0x823A, PSETID_Appointment); // PT_BOOLEAN - AutoFillLocation
+ mapi_nameid_lid_add(nameid, 0x8240, PSETID_Appointment); // PT_BOOLEAN - IsOnlineMeeting
+ mapi_nameid_lid_add(nameid, 0x8257, PSETID_Appointment); // PT_BOOLEAN - ApptCounterProposal
+ mapi_nameid_lid_add(nameid, 0x825E, PSETID_Appointment); // PT_BINARY - (timezone for dtstart)
+ mapi_nameid_lid_add(nameid, 0x825F, PSETID_Appointment); // PT_BINARY - (timezone for dtend)
+
+ mapi_nameid_lid_add(nameid, 0x0002, PSETID_Meeting); // PT_UNICODE - Where
+ mapi_nameid_lid_add(nameid, 0x0003, PSETID_Meeting); // PT_BINARY - GlobalObjectId
+ mapi_nameid_lid_add(nameid, 0x0005, PSETID_Meeting); // PT_BOOLEAN - IsRecurring
+ mapi_nameid_lid_add(nameid, 0x000a, PSETID_Meeting); // PT_BOOLEAN - IsException
+ mapi_nameid_lid_add(nameid, 0x0023, PSETID_Meeting); // PT_BINARY - CleanGlobalObjectId
+ mapi_nameid_lid_add(nameid, 0x0024, PSETID_Meeting); // PT_STRING8 - AppointmentMessageClass
+ mapi_nameid_lid_add(nameid, 0x0026, PSETID_Meeting); // PT_LONG - MeetingType
+
+ /* These probably would never be used from Evolution */
+// mapi_nameid_lid_add(nameid, 0x8200, PSETID_Appointment); // PT_BOOLEAN - SendAsICAL
+// mapi_nameid_lid_add(nameid, 0x8202, PSETID_Appointment); // PT_SYSTIME - ApptSequenceTime
+// mapi_nameid_lid_add(nameid, 0x8214, PSETID_Appointment); // PT_LONG - Label
+// mapi_nameid_lid_add(nameid, 0x8234, PSETID_Appointment); // PT_STRING8 - display TimeZone
+// mapi_nameid_lid_add(nameid, 0x8238, PSETID_Appointment); // PT_STRING8 - AllAttendees
+// mapi_nameid_lid_add(nameid, 0x823B, PSETID_Appointment); // PT_STRING8 - ToAttendeesString (dupe PR_DISPLAY_TO)
+// mapi_nameid_lid_add(nameid, 0x823C, PSETID_Appointment); // PT_STRING8 - CCAttendeesString (dupe PR_DISPLAY_CC)
+
+ mapi_nameid_lid_add(nameid, 0x8101, PSETID_Task); // PT_LONG - Status
+ mapi_nameid_lid_add(nameid, 0x8102, PSETID_Task); // PT_DOUBLE - PercentComplete
+ mapi_nameid_lid_add(nameid, 0x8103, PSETID_Task); // PT_BOOLEAN - TeamTask
+ mapi_nameid_lid_add(nameid, 0x8104, PSETID_Task); // PT_SYSTIME - StartDate/TaskStartDate
+ mapi_nameid_lid_add(nameid, 0x8105, PSETID_Task); // PT_SYSTIME - DueDate/TaskDueDate
+ mapi_nameid_lid_add(nameid, 0x810F, PSETID_Task); // PT_SYSTIME - DateCompleted
+// mapi_nameid_lid_add(nameid, 0x8116, PSETID_Task); // PT_BINARY - (recurrence blob)
+ mapi_nameid_lid_add(nameid, 0x811C, PSETID_Task); // PT_BOOLEAN - Complete
+ mapi_nameid_lid_add(nameid, 0x811F, PSETID_Task); // PT_STRING8 - Owner
+ mapi_nameid_lid_add(nameid, 0x8121, PSETID_Task); // PT_STRING8 - Delegator
+ mapi_nameid_lid_add(nameid, 0x8126, PSETID_Task); // PT_BOOLEAN - IsRecurring/TaskFRecur
+ mapi_nameid_lid_add(nameid, 0x8127, PSETID_Task); // PT_STRING8 - Role
+ mapi_nameid_lid_add(nameid, 0x8129, PSETID_Task); // PT_LONG - Ownership
+ mapi_nameid_lid_add(nameid, 0x812A, PSETID_Task); // PT_LONG - DelegationState
+
+ /* These probably would never be used from Evolution */
+// mapi_nameid_lid_add(nameid, 0x8110, PSETID_Task); // PT_LONG - ActualWork/TaskActualEffort
+// mapi_nameid_lid_add(nameid, 0x8111, PSETID_Task); // PT_LONG - TotalWork/TaskEstimatedEffort
+
+ /* These probably would never be used from Evolution */
+// mapi_nameid_lid_add(nameid, 0x8B00, PSETID_Note); // PT_LONG - Color
+
+ return TRUE;
+}
+
+static gboolean
+fetch_item_cb (FetchItemsCallbackData *item_data, gpointer data)
+{
+ long *flags;
+ struct FILETIME *delivery_date;
+ const char *msg_class;
+ NTTIME ntdate;
+ ExchangeMAPIStream *body;
+
+ MapiItem *item = g_new0(MapiItem , 1);
+ MapiItem **i = (MapiItem **)data;
+ guint32 j = 0;
+
+ if (camel_debug_start("mapi:folder")) {
+ exchange_mapi_debug_property_dump (item_data->properties);
+ camel_debug_end();
+ }
+
+ item->fid = item_data->fid;
+ item->mid = item_data->mid;
+
+
+ for (j = 0; j < item_data->properties->cValues; j++) {
+
+ gpointer prop_data = get_mapi_SPropValue_data(&item_data->properties->lpProps[j]);
+
+ switch (item_data->properties->lpProps[j].ulPropTag) {
+ /*FIXME : Instead of duping. Use talloc_steal to reuse the memory*/
+ case PR_NORMALIZED_SUBJECT:
+ case PR_NORMALIZED_SUBJECT_UNICODE :
+ item->header.subject = g_strdup (prop_data);
+ break;
+ case PR_DISPLAY_TO :
+ case PR_DISPLAY_TO_UNICODE :
+ item->header.to = g_strdup (prop_data);
+ break;
+ case PR_DISPLAY_CC:
+ case PR_DISPLAY_CC_UNICODE:
+ item->header.cc = g_strdup (prop_data);
+ break;
+ case PR_DISPLAY_BCC:
+ case PR_DISPLAY_BCC_UNICODE:
+ item->header.bcc = g_strdup (prop_data);
+ break;
+ case PR_SENT_REPRESENTING_NAME:
+ case PR_SENT_REPRESENTING_NAME_UNICODE:
+ item->header.from = g_strdup (prop_data);
+ break;
+ case PR_MESSAGE_SIZE:
+ item->header.size = *(glong *)prop_data;
+ break;
+ case PR_MESSAGE_CLASS:
+ case PR_MESSAGE_CLASS_UNICODE:
+ msg_class = (const char *) prop_data;
+ break;
+ case PR_MESSAGE_DELIVERY_TIME:
+ delivery_date = (struct FILETIME *) prop_data;
+ break;
+ case PR_MESSAGE_FLAGS:
+ flags = (long *) prop_data;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (g_str_has_prefix (msg_class, IPM_SCHEDULE_MEETING_PREFIX)) {
+ gchar *appointment_body_str = (gchar *) exchange_mapi_cal_util_camel_helper (item_data->properties,
+ item_data->streams,
+ item_data->recipients, item_data->attachments);
+
+ body = g_new0(ExchangeMAPIStream, 1);
+ body->proptag = PR_BODY;
+ body->value = g_byte_array_new ();
+ body->value = g_byte_array_append (body->value, appointment_body_str, g_utf8_strlen (appointment_body_str, -1));
+
+ item->msg.body_parts = g_slist_append (item->msg.body_parts, body);
+
+ item->is_cal = TRUE;
+ } else {
+ if (!((body = exchange_mapi_util_find_stream (item_data->streams, PR_HTML)) ||
+ (body = exchange_mapi_util_find_stream (item_data->streams, PR_BODY))))
+ body = exchange_mapi_util_find_stream (item_data->streams, PR_BODY_UNICODE);
+
+ item->msg.body_parts = g_slist_append (item->msg.body_parts, body);
+
+ item->is_cal = FALSE;
+ }
+
+ if (delivery_date) {
+ ntdate = delivery_date->dwHighDateTime;
+ ntdate = ntdate << 32;
+ ntdate |= delivery_date->dwLowDateTime;
+ item->header.recieved_time = nt_time_to_unix(ntdate);
+ }
+
+ if ((*flags & MSGFLAG_READ) != 0)
+ item->header.flags |= CAMEL_MESSAGE_SEEN;
+ if ((*flags & MSGFLAG_HASATTACH) != 0)
+ item->header.flags |= CAMEL_MESSAGE_ATTACHMENTS;
+
+ item->attachments = item_data->attachments;
+
+ *i = item;
+
+ return TRUE;
+}
+
+
+static void
+mapi_msg_set_recipient_list (CamelMimeMessage *msg, MapiItem *item)
+{
+ CamelInternetAddress *addr = NULL;
+ {
+ char *tmp_addr = NULL;
+ int index, len;
+
+ addr = camel_internet_address_new();
+ for (index = 0; item->header.to[index]; index += len){
+ if (item->header.to[index] == ';')
+ index++;
+ for (len = 0; item->header.to[index + len] &&
+ item->header.to[index + len] != ';'; len++)
+ ;
+ tmp_addr = malloc(/* tmp_addr, */ len + 1);
+ memcpy(tmp_addr, item->header.to + index, len);
+ tmp_addr[len] = 0;
+ if (len) camel_internet_address_add(addr, tmp_addr, tmp_addr);
+ }
+ if (index != 0)
+ camel_mime_message_set_recipients(msg, "To", addr);
+ }
+ /* modifing cc */
+ {
+ char *tmp_addr = NULL;
+ int index, len;
+
+ addr = camel_internet_address_new();
+ for (index = 0; item->header.cc[index]; index += len){
+ if (item->header.cc[index] == ';')
+ index++;
+ for (len = 0; item->header.cc[index + len] &&
+ item->header.cc[index + len] != ';'; len++)
+ ;
+ tmp_addr = malloc(/* tmp_addr, */ len + 1);
+ memcpy(tmp_addr, item->header.cc + index, len);
+ tmp_addr[len] = 0;
+ if (len) camel_internet_address_add(addr, tmp_addr, tmp_addr);
+ }
+ if (index != 0)
+ camel_mime_message_set_recipients(msg, "Cc", addr);
+ }
+}
+
+
+static void
+mapi_populate_details_from_item (CamelMimeMessage *msg, MapiItem *item)
+{
+ char *temp_str = NULL;
+ time_t recieved_time;
+ CamelInternetAddress *addr = NULL;
+
+ temp_str = item->header.subject;
+ if(temp_str)
+ camel_mime_message_set_subject (msg, temp_str);
+
+ recieved_time = item->header.recieved_time;
+
+ int offset = 0;
+ time_t actual_time = camel_header_decode_date (ctime(&recieved_time), &offset);
+ camel_mime_message_set_date (msg, actual_time, offset);
+
+ if (item->header.from) {
+ /* add reply to */
+ addr = camel_internet_address_new();
+ camel_internet_address_add(addr, item->header.from, item->header.from);
+ camel_mime_message_set_reply_to(msg, addr);
+
+ /* add from */
+ addr = camel_internet_address_new();
+ camel_internet_address_add(addr, item->header.from, item->header.from);
+ camel_mime_message_set_from(msg, addr);
+ }
+}
+
+
+static void
+mapi_populate_msg_body_from_item (CamelMultipart *multipart, MapiItem *item, ExchangeMAPIStream *body)
+{
+ CamelMimePart *part;
+
+ part = camel_mime_part_new ();
+ camel_mime_part_set_encoding(part, CAMEL_TRANSFER_ENCODING_8BIT);
+ const char* type = NULL;
+
+ if (body) {
+ if (item->is_cal)
+ camel_mime_part_set_content(part, body->value->data, body->value->len, "text/calendar");
+ else {
+ type = (body->proptag == PR_BODY || body->proptag == PR_BODY_UNICODE) ?
+ "text/plain" : "text/html";
+
+ camel_mime_part_set_content(part, body->value->data, body->value->len, type );
+ }
+ } else
+ camel_mime_part_set_content(part, " ", strlen(" "), "text/html");
+
+ camel_multipart_set_boundary (multipart, NULL);
+ camel_multipart_add_part (multipart, part);
+ camel_object_unref (part);
+}
+
+
+static CamelMimeMessage *
+mapi_folder_item_to_msg( CamelFolder *folder,
+ MapiItem *item,
+ CamelException *ex )
+{
+ CamelMimeMessage *msg = NULL;
+ CamelMultipart *multipart = NULL;
+
+ GSList *attach_list = NULL;
+ int errno;
+ /* char *body = NULL; */
+ ExchangeMAPIStream *body = NULL;
+ GSList *body_part_list = NULL;
+ const char *uid = NULL;
+
+ attach_list = item->attachments;
+
+ msg = camel_mime_message_new ();
+
+ multipart = camel_multipart_new ();
+
+ camel_mime_message_set_message_id (msg, uid);
+ body_part_list = item->msg.body_parts;
+ while (body_part_list){
+ body = body_part_list->data;
+ mapi_populate_msg_body_from_item (multipart, item, body);
+ body_part_list = g_slist_next (body_part_list);
+ }
+
+
+ /*Set recipient details*/
+ mapi_msg_set_recipient_list (msg, item);
+ mapi_populate_details_from_item (msg, item);
+
+ if (attach_list) {
+ GSList *al = attach_list;
+ for (al = attach_list; al != NULL; al = al->next) {
+ ExchangeMAPIAttachment *attach = (ExchangeMAPIAttachment *)al->data;
+ ExchangeMAPIStream *stream = NULL;
+ const char *filename, *mime_type;
+ CamelMimePart *part;
+
+ filename = (const char *) exchange_mapi_util_find_SPropVal_array_propval(attach->lpProps, PR_ATTACH_LONG_FILENAME);
+ if (!(filename && *filename))
+ filename = (const char *) exchange_mapi_util_find_SPropVal_array_propval(attach->lpProps, PR_ATTACH_FILENAME);
+
+ mime_type = (const char *) exchange_mapi_util_find_SPropVal_array_propval(attach->lpProps, PR_ATTACH_MIME_TAG);
+
+ stream = exchange_mapi_util_find_stream (attach->streams, PR_ATTACH_DATA_BIN);
+
+ if (!stream || stream->value->len <= 0) {
+ continue;
+ }
+ part = camel_mime_part_new ();
+
+ camel_mime_part_set_filename(part, g_strdup(filename));
+ //Auto generate content-id
+ camel_mime_part_set_content_id (part, NULL);
+ camel_mime_part_set_content(part, stream->value->data, stream->value->len, mime_type);
+ camel_content_type_set_param (((CamelDataWrapper *) part)->mime_type, "name", filename);
+
+ camel_multipart_set_boundary(multipart, NULL);
+ camel_multipart_add_part (multipart, part);
+ camel_object_unref (part);
+
+ }
+ exchange_mapi_util_free_attachment_list (&attach_list);
+ }
+
+ camel_medium_set_content_object(CAMEL_MEDIUM (msg), CAMEL_DATA_WRAPPER(multipart));
+ camel_object_unref (multipart);
+
+ if (body)
+ g_free (body);
+
+ return msg;
+}
+
+
+static CamelMimeMessage *
+mapi_folder_get_message( CamelFolder *folder, const char *uid, CamelException *ex )
+{
+ CamelMimeMessage *msg = NULL;
+ CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER(folder);
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE(folder->parent_store);
+ CamelMapiMessageInfo *mi = NULL;
+
+ CamelStream *stream, *cache_stream;
+ int errno;
+
+ /* see if it is there in cache */
+
+ mi = (CamelMapiMessageInfo *) camel_folder_summary_uid (folder->summary, uid);
+ if (mi == NULL) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
+ _("Cannot get message: %s\n %s"), uid, _("No such message"));
+ return NULL;
+ }
+ cache_stream = camel_data_cache_get (mapi_folder->cache, "cache", uid, ex);
+ stream = camel_stream_mem_new ();
+ if (cache_stream) {
+ msg = camel_mime_message_new ();
+ camel_stream_reset (stream);
+ camel_stream_write_to_stream (cache_stream, stream);
+ camel_stream_reset (stream);
+ if (camel_data_wrapper_construct_from_stream ((CamelDataWrapper *) msg, stream) == -1) {
+ if (errno == EINTR) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
+ camel_object_unref (msg);
+ camel_object_unref (cache_stream);
+ camel_object_unref (stream);
+ camel_message_info_free (&mi->info);
+ return NULL;
+ } else {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get message %s: %s"),
+ uid, g_strerror (errno));
+ camel_object_unref (msg);
+ msg = NULL;
+ }
+ }
+ camel_object_unref (cache_stream);
+ }
+ camel_object_unref (stream);
+
+ if (msg != NULL) {
+ camel_message_info_free (&mi->info);
+ return msg;
+ }
+
+ if (((CamelOfflineStore *) mapi_store)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL) {
+ camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+ _("This message is not available in offline mode."));
+ camel_message_info_free (&mi->info);
+ return NULL;
+ }
+
+ /* Check if we are really offline */
+ if (!camel_mapi_store_connected (mapi_store, ex)) {
+ camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+ _("This message is not available in offline mode."));
+ camel_message_info_free (&mi->info);
+ return NULL;
+ }
+
+ mapi_id_t id_folder;
+ mapi_id_t id_message;
+ MapiItem *item = NULL;
+ guint32 options = 0;
+
+ options = MAPI_OPTIONS_FETCH_ALL | MAPI_OPTIONS_FETCH_BODY_STREAM | MAPI_OPTIONS_GETBESTBODY ;
+ exchange_mapi_util_mapi_ids_from_uid (uid, &id_folder, &id_message);
+
+ if (((CamelMapiFolder *)folder)->type == MAPI_FAVOURITE_FOLDER){
+ options |= MAPI_OPTIONS_USE_PFSTORE;
+ }
+
+ exchange_mapi_connection_fetch_item (id_folder, id_message,
+ camel_GetPropsList, G_N_ELEMENTS (camel_GetPropsList),
+ camel_build_name_id, NULL,
+ fetch_item_cb, &item,
+ options);
+
+ if (item == NULL) {
+ camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_INVALID, _("Could not get message"));
+ camel_message_info_free (&mi->info);
+ return NULL;
+ }
+
+ msg = mapi_folder_item_to_msg (folder, item, ex);
+
+ g_free (item);
+
+ if (!msg) {
+ camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_INVALID, _("Could not get message"));
+ camel_message_info_free (&mi->info);
+
+ return NULL;
+ }
+
+ /* add to cache */
+ CAMEL_MAPI_FOLDER_REC_LOCK (folder, cache_lock);
+ if ((cache_stream = camel_data_cache_add (mapi_folder->cache, "cache", uid, NULL))) {
+ if (camel_data_wrapper_write_to_stream ((CamelDataWrapper *) msg, cache_stream) == -1
+ || camel_stream_flush (cache_stream) == -1)
+ camel_data_cache_remove (mapi_folder->cache, "cache", uid, NULL);
+ camel_object_unref (cache_stream);
+ }
+
+ CAMEL_MAPI_FOLDER_REC_UNLOCK (folder, cache_lock);
+
+ camel_message_info_free (&mi->info);
+
+ return msg;
+}
+
+static void
+mapi_folder_search_free (CamelFolder *folder, GPtrArray *uids)
+{
+ CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER(folder);
+
+ g_return_if_fail (mapi_folder->search);
+
+ CAMEL_MAPI_FOLDER_LOCK(mapi_folder, search_lock);
+
+ camel_folder_search_free_result (mapi_folder->search, uids);
+
+ CAMEL_MAPI_FOLDER_UNLOCK(mapi_folder, search_lock);
+
+}
+
+static void
+camel_mapi_folder_finalize (CamelObject *object)
+{
+ CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER (object);
+
+ if (mapi_folder->priv)
+ g_free(mapi_folder->priv);
+ if (mapi_folder->cache)
+ camel_object_unref (mapi_folder->cache);
+}
+
+
+static CamelMessageInfo*
+mapi_get_message_info(CamelFolder *folder, const char *uid)
+{
+#if 0
+ CamelMessageInfo *msg_info = NULL;
+ CamelMessageInfoBase *mi = (CamelMessageInfoBase *)msg ;
+ int status = 0;
+ oc_message_headers_t headers;
+
+ if (folder->summary) {
+ msg_info = camel_folder_summary_uid(folder->summary, uid);
+ }
+ if (msg_info != NULL) {
+ mi = (CamelMessageInfoBase *)msg_info ;
+ return (msg_info);
+ }
+ msg_info = camel_message_info_new(folder->summary);
+ mi = (CamelMessageInfoBase *)msg_info ;
+ //TODO :
+/* oc_message_headers_init(&headers); */
+/* oc_thread_connect_lock(); */
+/* status = oc_message_headers_get_by_id(&headers, uid); */
+/* oc_thread_connect_unlock(); */
+
+ if (headers.subject) mi->subject = (char *)camel_pstring_strdup(headers.subject);
+ if (headers.from) mi->from = (char *)camel_pstring_strdup(headers.from);
+ if (headers.to) mi->to = (char *)camel_pstring_strdup(headers.to);
+ if (headers.cc) mi->cc = (char *)camel_pstring_strdup(headers.cc);
+ mi->flags = headers.flags;
+
+
+ mi->user_flags = NULL;
+ mi->user_tags = NULL;
+ mi->date_received = 0;
+ mi->date_sent = headers.send;
+ mi->content = NULL;
+ mi->summary = folder->summary;
+ if (uid) mi->uid = g_strdup(uid);
+ oc_message_headers_release(&headers);
+ return (msg);
+#endif
+ return NULL;
+}
+
+static void
+mapi_expunge (CamelFolder *folder, CamelException *ex)
+{
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE(folder->parent_store);
+ CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER (folder);
+ CamelMapiMessageInfo *minfo;
+ CamelMessageInfo *info;
+ CamelFolderChangeInfo *changes;
+
+ mapi_id_t fid;
+
+ int i, count;
+ gboolean delete = FALSE, status = FALSE;
+ gchar *folder_id;
+ GSList *deleted_items, *deleted_head;
+ GSList *deleted_items_uid, *deleted_items_uid_head;
+
+ deleted_items = deleted_head = NULL;
+ deleted_items_uid = deleted_items_uid_head = NULL;
+
+ folder_id = g_strdup (camel_mapi_store_folder_id_lookup (mapi_store, folder->full_name)) ;
+ exchange_mapi_util_mapi_id_from_string (folder_id, &fid);
+
+ if (mapi_folder->type & CAMEL_FOLDER_TYPE_TRASH) {
+ CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
+ status = exchange_mapi_empty_folder (fid);
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+
+ if (status) {
+ camel_folder_freeze (folder);
+ mapi_summary_clear (folder->summary, TRUE);
+ camel_folder_thaw (folder);
+ } else
+ g_warning ("Could not Empty Trash\n");
+
+ return;
+ }
+
+ changes = camel_folder_change_info_new ();
+ count = camel_folder_summary_count (folder->summary);
+
+ /*Collect UIDs of deleted messages.*/
+ for (i = 0; i < count; i++) {
+ info = camel_folder_summary_index (folder->summary, i);
+ minfo = (CamelMapiMessageInfo *) info;
+ if (minfo && (minfo->info.flags & CAMEL_MESSAGE_DELETED)) {
+ const gchar *uid = camel_message_info_uid (info);
+ mapi_id_t *mid = g_new0 (mapi_id_t, 1);
+
+ if (!exchange_mapi_util_mapi_ids_from_uid (uid, &fid, mid))
+ continue;
+
+ if (deleted_items)
+ deleted_items = g_slist_prepend (deleted_items, mid);
+ else {
+ g_slist_free (deleted_head);
+ deleted_head = NULL;
+ deleted_head = deleted_items = g_slist_prepend (deleted_items, mid);
+ }
+ deleted_items_uid = g_slist_prepend (deleted_items_uid, uid);
+ }
+ camel_message_info_free (info);
+ }
+
+ deleted_items_uid_head = deleted_items_uid;
+
+ if (deleted_items) {
+ CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
+
+ status = exchange_mapi_remove_items(0, fid, deleted_items);
+
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+
+ if (status) {
+ while (deleted_items_uid) {
+ const gchar *uid = (gchar *)deleted_items_uid->data;
+ CAMEL_MAPI_FOLDER_REC_LOCK (folder, cache_lock);
+ camel_folder_change_info_remove_uid (changes, uid);
+ camel_folder_summary_remove_uid (folder->summary, uid);
+ camel_data_cache_remove(mapi_folder->cache, "cache", uid, NULL);
+ CAMEL_MAPI_FOLDER_REC_UNLOCK (folder, cache_lock);
+ deleted_items_uid = g_slist_next (deleted_items_uid);
+ }
+ }
+ delete = TRUE;
+
+ g_slist_foreach (deleted_head, (GFunc)g_free, NULL);
+ g_slist_free (deleted_head);
+ g_slist_free (deleted_items_uid_head);
+ }
+
+ if (delete)
+ camel_object_trigger_event (CAMEL_OBJECT (folder), "folder_changed", changes);
+
+ g_free (folder_id);
+ camel_folder_change_info_free (changes);
+}
+
+static void
+mapi_transfer_messages_to (CamelFolder *source, GPtrArray *uids,
+ CamelFolder *destination, GPtrArray **transferred_uids,
+ gboolean delete_originals, CamelException *ex)
+{
+ mapi_id_t src_fid, dest_fid;
+
+ CamelOfflineStore *offline = (CamelOfflineStore *) destination->parent_store;
+ CamelMapiStore *mapi_store= CAMEL_MAPI_STORE(source->parent_store);
+ CamelFolderChangeInfo *changes = NULL;
+
+ char *folder_id = NULL;
+ int i = 0;
+
+ GSList *src_msg_ids = NULL;
+
+
+ /* check for offline operation */
+ if (offline->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL)
+ return;
+
+ folder_id = camel_mapi_store_folder_id_lookup (mapi_store, source->full_name) ;
+ exchange_mapi_util_mapi_id_from_string (folder_id, &src_fid);
+
+ folder_id = camel_mapi_store_folder_id_lookup (mapi_store, destination->full_name) ;
+ exchange_mapi_util_mapi_id_from_string (folder_id, &dest_fid);
+
+ for (i=0; i < uids->len; i++) {
+ mapi_id_t *mid = g_new0 (mapi_id_t, 1); /* FIXME : */
+ if (!exchange_mapi_util_mapi_ids_from_uid (g_ptr_array_index (uids, i), &src_fid, mid))
+ continue;
+
+ src_msg_ids = g_slist_prepend (src_msg_ids, mid);
+ }
+
+ if (delete_originals) {
+ if (!exchange_mapi_move_items (src_fid, dest_fid, src_msg_ids)) {
+ //TODO : Set exception.
+ } else {
+ changes = camel_folder_change_info_new ();
+
+ for (i=0; i < uids->len; i++) {
+ camel_folder_summary_remove_uid (source->summary, uids->pdata[i]);
+ camel_folder_change_info_remove_uid (changes, uids->pdata[i]);
+ }
+ camel_object_trigger_event (source, "folder_changed", changes);
+ camel_folder_change_info_free (changes);
+
+ }
+ } else {
+ if (!exchange_mapi_copy_items (src_fid, dest_fid, src_msg_ids)) {
+ //TODO : Set exception.
+ }
+ }
+
+ g_slist_foreach (src_msg_ids, (GFunc) g_free, NULL);
+ g_slist_free (src_msg_ids);
+
+ return;
+}
+
+static void
+camel_mapi_folder_class_init (CamelMapiFolderClass *camel_mapi_folder_class)
+{
+ CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS (camel_mapi_folder_class);
+
+ parent_class = CAMEL_OFFLINE_FOLDER_CLASS (camel_type_get_global_classfuncs (camel_offline_folder_get_type ()));
+
+ ((CamelObjectClass *) camel_mapi_folder_class)->getv = mapi_getv;
+
+ camel_folder_class->get_message = mapi_folder_get_message;
+/* camel_folder_class->rename = mapi_folder_rename; */
+ camel_folder_class->search_by_expression = mapi_folder_search_by_expression;
+/* camel_folder_class->get_message_info = mapi_get_message_info; */
+/* camel_folder_class->search_by_uids = mapi_folder_search_by_uids; */
+ camel_folder_class->search_free = mapi_folder_search_free;
+/* camel_folder_class->append_message = mapi_append_message; */
+ camel_folder_class->refresh_info = mapi_refresh_info;
+ camel_folder_class->sync = mapi_sync;
+ camel_folder_class->expunge = mapi_expunge;
+ camel_folder_class->transfer_messages_to = mapi_transfer_messages_to;
+}
+
+static void
+camel_mapi_folder_init (gpointer object, gpointer klass)
+{
+ CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER (object);
+ CamelFolder *folder = CAMEL_FOLDER (object);
+
+
+ folder->permanent_flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_DELETED |
+ CAMEL_MESSAGE_DRAFT | CAMEL_MESSAGE_FLAGGED | CAMEL_MESSAGE_SEEN;
+
+ folder->folder_flags = CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY | CAMEL_FOLDER_HAS_SEARCH_CAPABILITY;
+
+ mapi_folder->priv = g_malloc0 (sizeof(*mapi_folder->priv));
+
+#ifdef ENABLE_THREADS
+ g_static_mutex_init(&mapi_folder->priv->search_lock);
+ g_static_rec_mutex_init(&mapi_folder->priv->cache_lock);
+#endif
+
+ mapi_folder->need_rescan = TRUE;
+}
+
+CamelType
+camel_mapi_folder_get_type (void)
+{
+ static CamelType camel_mapi_folder_type = CAMEL_INVALID_TYPE;
+
+
+ if (camel_mapi_folder_type == CAMEL_INVALID_TYPE) {
+ camel_mapi_folder_type =
+ camel_type_register (camel_offline_folder_get_type (),
+ "CamelMapiFolder",
+ sizeof (CamelMapiFolder),
+ sizeof (CamelMapiFolderClass),
+ (CamelObjectClassInitFunc) camel_mapi_folder_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_mapi_folder_init,
+ (CamelObjectFinalizeFunc) camel_mapi_folder_finalize);
+ }
+
+ return camel_mapi_folder_type;
+}
+
+CamelFolder *
+camel_mapi_folder_new(CamelStore *store, const char *folder_name, const char *folder_dir, guint32 flags, CamelException *ex)
+{
+
+ CamelFolder *folder = NULL;
+ CamelMapiFolder *mapi_folder;
+ CamelMapiStore *mapi_store = (CamelMapiStore *) store;
+
+ char *summary_file, *state_file;
+ char *short_name;
+ guint32 i = 0;
+
+ folder = CAMEL_FOLDER (camel_object_new(camel_mapi_folder_get_type ()) );
+
+ mapi_folder = CAMEL_MAPI_FOLDER(folder);
+ short_name = strrchr (folder_name, '/');
+ if (short_name)
+ short_name++;
+ else
+ short_name = (char *) folder_name;
+ camel_folder_construct (folder, store, folder_name, short_name);
+
+ summary_file = g_strdup_printf ("%s/%s/summary",folder_dir, folder_name);
+
+ folder->summary = camel_mapi_summary_new(folder, summary_file);
+ g_free(summary_file);
+
+ if (!folder->summary) {
+ camel_object_unref (CAMEL_OBJECT (folder));
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not load summary for %s"),
+ folder_name);
+ return NULL;
+ }
+
+ /* set/load persistent state */
+ state_file = g_strdup_printf ("%s/cmeta", g_strdup_printf ("%s/%s",folder_dir, folder_name));
+ camel_object_set(folder, NULL, CAMEL_OBJECT_STATE_FILE, state_file, NULL);
+ g_free(state_file);
+ camel_object_state_read(folder);
+
+ mapi_folder->cache = camel_data_cache_new (g_strdup_printf ("%s/%s",folder_dir, folder_name),0 ,ex);
+ if (!mapi_folder->cache) {
+ camel_object_unref (folder);
+ return NULL;
+ }
+
+/* journal_file = g_strdup_printf ("%s/journal", g_strdup_printf ("%s-%s",folder_name, "dir")); */
+/* mapi_folder->journal = camel_mapi_journal_new (mapi_folder, journal_file); */
+/* g_free (journal_file); */
+/* if (!mapi_folder->journal) { */
+/* camel_object_unref (folder); */
+/* return NULL; */
+/* } */
+
+ if (!strcmp (folder_name, "Mailbox")) {
+ if (camel_url_get_param (((CamelService *) store)->url, "filter"))
+ folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
+ }
+
+ mapi_folder->search = camel_folder_search_new ();
+ if (!mapi_folder->search) {
+ camel_object_unref (folder);
+ return NULL;
+ }
+
+ for (i=0;i<camel_store_summary_count((CamelStoreSummary *)mapi_store->summary);i++) {
+ CamelStoreInfo *si = camel_store_summary_index((CamelStoreSummary *)mapi_store->summary, i);
+ if (si == NULL)
+ continue;
+
+ if (!strcmp(folder_name, camel_mapi_store_info_full_name (mapi_store->summary, si))) {
+ mapi_folder->type = si->flags;
+ }
+
+ camel_store_summary_info_free((CamelStoreSummary *)mapi_store->summary, si);
+ }
+ return folder;
+}
+
+
Added: trunk/src/camel/camel-mapi-folder.h
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-folder.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,133 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef __MAPI_FOLDER_H__
+#define __MAPI_FOLDER_H__
+
+
+#include <camel/camel-folder.h>
+#include <camel/camel-offline-folder.h>
+#include <camel/camel-data-cache.h>
+#include <camel/camel-offline-folder.h>
+#include <camel/camel-offline-journal.h>
+#include <libmapi/libmapi.h>
+#include <exchange-mapi-connection.h>
+
+#define PATH_FOLDER ".evolution/mail/mapi"
+
+#define CAMEL_MAPI_FOLDER_TYPE (camel_mapi_folder_get_type ())
+#define CAMEL_MAPI_FOLDER(obj) (CAMEL_CHECK_CAST((obj), CAMEL_MAPI_FOLDER_TYPE, CamelMapiFolder))
+#define CAMEL_MAPI_FOLDER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_MAPI_FOLDER_TYPE, CamelMapiFolderClass))
+#define CAMEL_IS_MAPI_FOLDER(o) (CAMEL_CHECK_TYPE((o), CAMEL_MAPI_FOLDER_TYPE))
+
+/**
+ * DATA STRUCTURES
+ */
+
+G_BEGIN_DECLS
+
+typedef enum {
+ MAPI_ITEM_TYPE_MAIL=1,
+ MAPI_ITEM_TYPE_APPOINTMENT,
+ MAPI_ITEM_TYPE_CONTACT,
+ MAPI_ITEM_TYPE_JOURNAL,
+ MAPI_ITEM_TYPE_TASK
+} MapiItemType;
+
+typedef enum {
+ PART_TYPE_PLAIN_TEXT=1,
+ PART_TYPE_TEXT_HTML
+} MapiItemPartType;
+
+typedef struct {
+ gchar *subject;
+ gchar *from;
+ gchar *to;
+ gchar *cc;
+ gchar *bcc;
+
+ int flags;
+ glong size;
+ time_t recieved_time;
+ time_t send_time;
+} MapiItemHeader;
+
+typedef struct {
+ GSList *body_parts;
+} MapiItemMessage;
+
+typedef struct {
+ mapi_id_t fid;
+ mapi_id_t mid;
+
+ MapiItemHeader header;
+ MapiItemMessage msg;
+
+ gboolean is_cal;
+
+ GSList *attachments;
+ GSList *generic_streams;
+}MapiItem;
+
+
+typedef struct _CamelMapiFolder CamelMapiFolder;
+typedef struct _CamelMapiFolderClass CamelMapiFolderClass;
+
+struct _CamelMapiFolder {
+ CamelOfflineFolder parent_object;
+
+ struct _CamelMapiFolderPrivate *priv;
+
+ CamelFolderSearch *search;
+
+ CamelOfflineJournal *journal;
+ CamelDataCache *cache;
+
+ guint32 type;
+
+ unsigned int need_rescan:1;
+ unsigned int need_refresh:1;
+ unsigned int read_only:1;
+};
+
+struct _CamelMapiFolderClass {
+ CamelOfflineFolderClass parent_class;
+
+ /* Virtual methods */
+
+} ;
+
+
+/* Standard Camel function */
+CamelType camel_mapi_folder_get_type (void);
+
+/* implemented */
+CamelFolder *
+camel_mapi_folder_new(CamelStore *store, const char *folder_name, const char *folder_dir, guint32 flags, CamelException *ex);
+
+void mapi_update_summary ( CamelFolder *folder, GList *item_list,CamelException *ex) ;
+void mapi_refresh_folder(CamelFolder *folder, CamelException *ex);
+
+G_END_DECLS
+
+#endif /* CAMEL_GROUPWISE_FOLDER_H */
Added: trunk/src/camel/camel-mapi-private.h
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-private.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef CAMEL_MAPI_PRIVATE_H
+#define CAMEL_MAPI_PRIVATE_H 1
+
+/* need a way to configure and save this data, if this header is to
+ be installed. For now, dont install it */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+//#ifdef ENABLE_THREADS
+#define CAMEL_MAPI_FOLDER_LOCK(f, l) \
+ (g_static_mutex_lock(&((CamelMapiFolder *)f)->priv->l))
+#define CAMEL_MAPI_FOLDER_UNLOCK(f, l) \
+ (g_static_mutex_unlock(&((CamelMapiFolder *)f)->priv->l))
+#define CAMEL_MAPI_FOLDER_REC_LOCK(f, l) \
+ (g_static_rec_mutex_lock(&((CamelMapiFolder *)f)->priv->l))
+#define CAMEL_MAPI_FOLDER_REC_UNLOCK(f, l) \
+ (g_static_rec_mutex_unlock(&((CamelMapiFolder *)f)->priv->l))
+//#else
+#define MAPI_FOLDER_LOCK(f, l)
+#define MAPI_FOLDER_UNLOCK(f, l)
+#define MAPI_FOLDER_REC_LOCK(f, l)
+#define MAPI_FOLDER_REC_UNLOCK(f, l)
+//#endif
+
+#endif /* CAMEL_IMAP_PRIVATE_H */
Added: trunk/src/camel/camel-mapi-provider.c
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-provider.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,191 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <string.h>
+
+#include <gmodule.h>
+
+#include <camel/camel-provider.h>
+#include <camel/camel-session.h>
+#include <camel/camel-url.h>
+#include <camel/camel-sasl.h>
+#include <camel/camel-i18n.h>
+
+#include "camel-mapi-store.h"
+#include "camel-mapi-transport.h"
+
+#define d(x) x
+
+static void add_hash (guint *, char *);
+static guint mapi_url_hash (gconstpointer);
+static gint check_equal (char *, char *);
+static gint mapi_url_equal (gconstpointer, gconstpointer);
+
+static CamelProviderConfEntry mapi_conf_entries[] = {
+ { CAMEL_PROVIDER_CONF_SECTION_START, "mailcheck", NULL,
+ N_("Checking for New Mail") },
+ { CAMEL_PROVIDER_CONF_CHECKBOX, "check_all", NULL,
+ N_("C_heck for new messages in all folders"), "1" },
+ { CAMEL_PROVIDER_CONF_SECTION_END },
+
+/* /\* override the labels/defaults of the standard settings *\/ */
+/* { CAMEL_PROVIDER_CONF_LABEL, "username", NULL, */
+/* /\* i18n: the '_' should appear before the same letter it */
+/* does in the evolution:mail-config.glade "User_name" */
+/* translation (or not at all) *\/ */
+/* N_("Windows User_name:") }, */
+
+ /* extra Exchange configuration settings */
+ { CAMEL_PROVIDER_CONF_SECTION_START, "activedirectory", NULL,
+ /* i18n: GAL is an Outlookism, AD is a Windowsism */
+ N_("Global Address List / Active Directory") },
+ { CAMEL_PROVIDER_CONF_ENTRY, "ad_server", NULL,
+ /* i18n: "Global Catalog" is a Windowsism, but it's a
+ technical term and may not have translations? */
+ N_("_Global Catalog server name:") },
+ { CAMEL_PROVIDER_CONF_CHECKSPIN, "ad_limit", NULL,
+ N_("_Limit number of GAL responses: %s"), "y:1:500:10000" },
+ { CAMEL_PROVIDER_CONF_SECTION_END },
+ { CAMEL_PROVIDER_CONF_SECTION_START, "generals", NULL,
+ N_("Options") },
+ { CAMEL_PROVIDER_CONF_CHECKBOX, "sync_offline", NULL,
+ N_("Automatically synchroni_ze account locally"), "0" },
+ { CAMEL_PROVIDER_CONF_CHECKBOX, "filter", NULL,
+ /* i18n: copy from evolution:camel-imap-provider.c */
+ N_("_Apply filters to new messages in Inbox on this server"), "0" },
+ { CAMEL_PROVIDER_CONF_CHECKBOX, "filter_junk", NULL,
+ N_("Check new messages for _Junk contents"), "0" },
+ { CAMEL_PROVIDER_CONF_CHECKBOX, "filter_junk_inbox", "filter_junk",
+ N_("Only check for Junk messag_es in the Inbox folder"), "0" },
+
+
+ { CAMEL_PROVIDER_CONF_SECTION_END },
+ { CAMEL_PROVIDER_CONF_END }
+};
+
+static CamelProvider mapi_provider = {
+ "mapi",
+
+ "Exchange MAPI",
+
+ N_("For accessing Microsoft Exchange / OpenChange servers using MAPI"),
+
+ "mail",
+
+ CAMEL_PROVIDER_IS_REMOTE | CAMEL_PROVIDER_IS_SOURCE |
+ CAMEL_PROVIDER_IS_STORAGE | CAMEL_PROVIDER_DISABLE_SENT_FOLDER | CAMEL_PROVIDER_IS_EXTERNAL,
+
+ CAMEL_URL_NEED_USER | CAMEL_URL_NEED_HOST,
+
+ mapi_conf_entries,
+
+ /* ... */
+};
+
+CamelServiceAuthType camel_mapi_password_authtype = {
+ N_("Password"),
+ N_("This option will connect to the Openchange server using a plaintext password."),
+ "",
+ TRUE
+};
+
+static int
+mapi_auto_detect_cb(CamelURL *url, GHashTable **auto_detected, CamelException *ex)
+{
+ d (printf("mapi_auto_detect_cb\n"));
+ *auto_detected = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (*auto_detected, g_strdup ("poa"), g_strdup (url->host));
+
+ return 0;
+}
+
+void
+camel_provider_module_init(void)
+{
+ mapi_provider.name = "Exchange MAPI";
+ mapi_provider.auto_detect = mapi_auto_detect_cb;
+ mapi_provider.authtypes = g_list_prepend (mapi_provider.authtypes, &camel_mapi_password_authtype);
+ mapi_provider.url_hash = mapi_url_hash;
+ mapi_provider.url_equal = mapi_url_equal;
+ mapi_provider.license = "LGPL";
+ mapi_provider.object_types[CAMEL_PROVIDER_STORE] = camel_mapi_store_get_type();
+ mapi_provider.object_types[CAMEL_PROVIDER_TRANSPORT] = camel_mapi_transport_get_type();
+ camel_provider_register (&mapi_provider);
+}
+
+static void
+add_hash(guint *hash, char *s)
+{
+ if (s) {
+ *hash ^= g_str_hash(s);
+ }
+}
+
+static guint
+mapi_url_hash(gconstpointer key)
+{
+ const CamelURL *u = (CamelURL *)key;
+ guint hash = 0;
+
+ add_hash (&hash, u->user);
+ add_hash (&hash, u->authmech);
+ add_hash (&hash, u->host);
+ hash ^= u->port;
+
+ return hash;
+}
+
+static gint
+check_equal(char *s1, char *s2)
+{
+ if (s1 == NULL) {
+ if (s2 == NULL) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ }
+ if (s2 == NULL) {
+ return FALSE;
+ }
+
+ return strcmp (s1, s2) == 0;
+}
+
+static gint
+mapi_url_equal (gconstpointer a, gconstpointer b)
+{
+ const CamelURL *u1 = a;
+ const CamelURL *u2 = b;
+
+ return check_equal (u1->protocol, u2->protocol)
+ && check_equal (u1->user, u2->user)
+ && check_equal (u1->authmech, u2->authmech)
+ && check_equal (u1->host, u2->host)
+ && u1->port == u2->port;
+}
Added: trunk/src/camel/camel-mapi-store-summary.c
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-store-summary.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,315 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include <libedataserver/e-memory.h>
+#include <libedataserver/md5-utils.h>
+
+#include <camel/camel-file-utils.h>
+#include <camel/camel-utf8.h>
+
+#include "camel-private.h"
+#include "camel-mapi-store.h"
+#include "camel-mapi-store-summary.h"
+
+#define d(x)
+
+static int summary_header_load(CamelStoreSummary *, FILE *);
+static int summary_header_save(CamelStoreSummary *, FILE *);
+
+static CamelStoreInfo *store_info_load(CamelStoreSummary *s, FILE *in) ;
+static int store_info_save(CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi) ;
+static void store_info_free(CamelStoreSummary *s, CamelStoreInfo *mi) ;
+static void store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str) ;
+
+static const char *store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type) ;
+
+static void camel_mapi_store_summary_class_init (CamelMapiStoreSummaryClass *klass);
+static void camel_mapi_store_summary_init (CamelMapiStoreSummary *obj);
+static void camel_mapi_store_summary_finalise (CamelObject *obj);
+
+static CamelStoreSummaryClass *camel_mapi_store_summary_parent;
+
+
+static void
+camel_mapi_store_summary_class_init (CamelMapiStoreSummaryClass *klass)
+{
+ CamelStoreSummaryClass *ssklass = (CamelStoreSummaryClass *)klass;
+
+ ssklass->summary_header_load = summary_header_load;
+ ssklass->summary_header_save = summary_header_save;
+
+ ssklass->store_info_load = store_info_load;
+ ssklass->store_info_save = store_info_save;
+ ssklass->store_info_free = store_info_free;
+
+ ssklass->store_info_string = store_info_string;
+ ssklass->store_info_set_string = store_info_set_string;
+
+
+}
+
+static void
+camel_mapi_store_summary_init (CamelMapiStoreSummary *s)
+{
+
+ ((CamelStoreSummary *)s)->store_info_size = sizeof(CamelMapiStoreInfo);
+ s->version = CAMEL_MAPI_STORE_SUMMARY_VERSION;
+}
+
+
+static void
+camel_mapi_store_summary_finalise (CamelObject *obj)
+{
+}
+
+
+CamelType
+camel_mapi_store_summary_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ camel_mapi_store_summary_parent = (CamelStoreSummaryClass *)camel_store_summary_get_type();
+ type = camel_type_register((CamelType)camel_mapi_store_summary_parent, "CamelMapiStoreSummary",
+ sizeof (CamelMapiStoreSummary),
+ sizeof (CamelMapiStoreSummaryClass),
+ (CamelObjectClassInitFunc) camel_mapi_store_summary_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_mapi_store_summary_init,
+ (CamelObjectFinalizeFunc) camel_mapi_store_summary_finalise);
+ }
+
+ return type;
+}
+
+
+CamelMapiStoreSummary *
+camel_mapi_store_summary_new (void)
+{
+ CamelMapiStoreSummary *new = CAMEL_MAPI_STORE_SUMMARY ( camel_object_new (camel_mapi_store_summary_get_type ()));
+
+ return new;
+}
+
+
+static int
+summary_header_load(CamelStoreSummary *s, FILE *in)
+{
+ CamelMapiStoreSummary *summary = (CamelMapiStoreSummary *)s ;
+
+ /* TODO */
+ if (camel_mapi_store_summary_parent->summary_header_load ((CamelStoreSummary *)s, in) == -1)
+ /* || camel_file_util_decode_fixed_int32(in, &version) == -1) */
+ return -1 ;
+
+ summary->version = 0 ;
+
+ return 0 ;
+}
+
+
+static int
+summary_header_save(CamelStoreSummary *s, FILE *out)
+{
+
+ if (camel_mapi_store_summary_parent->summary_header_save((CamelStoreSummary *)s, out) == -1)
+ return -1;
+
+ return 0 ;
+}
+
+static CamelStoreInfo *
+store_info_load(CamelStoreSummary *s, FILE *in)
+{
+ CamelMapiStoreInfo *si;
+
+ si = (CamelMapiStoreInfo *)camel_mapi_store_summary_parent->store_info_load(s, in);
+ if (si) {
+ if (camel_file_util_decode_string(in, &si->full_name) == -1) {
+ camel_store_summary_info_free(s, (CamelStoreInfo *)si);
+ si = NULL;
+ }
+ }
+ return (CamelStoreInfo *)si;
+}
+
+static int
+store_info_save(CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi)
+{
+ CamelMapiStoreInfo *summary = (CamelMapiStoreInfo *)mi;
+ if (camel_mapi_store_summary_parent->store_info_save(s, out, mi) == -1
+ || camel_file_util_encode_string(out, summary->full_name) == -1)
+ return -1;
+
+ return 0;
+}
+
+
+static void
+store_info_free(CamelStoreSummary *s, CamelStoreInfo *mi)
+{
+ CamelMapiStoreInfo *si = (CamelMapiStoreInfo *)mi;
+
+ g_free(si->full_name);
+ camel_mapi_store_summary_parent->store_info_free(s, mi);
+}
+
+
+
+
+
+static const char *
+store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type)
+{
+ CamelMapiStoreInfo *isi = (CamelMapiStoreInfo *)mi;
+
+ /* FIXME: Locks? */
+
+ g_assert (mi != NULL);
+
+ switch (type) {
+ case CAMEL_STORE_INFO_LAST:
+ return isi->full_name;
+ default:
+ return camel_mapi_store_summary_parent->store_info_string(s, mi, type);
+ }
+}
+
+static void
+store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str)
+{
+ CamelMapiStoreInfo *isi = (CamelMapiStoreInfo *)mi;
+
+ g_assert(mi != NULL);
+
+ switch(type) {
+ case CAMEL_STORE_INFO_LAST:
+ d(printf("Set full name %s -> %s\n", isi->full_name, str));
+ CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
+ g_free(isi->full_name);
+ isi->full_name = g_strdup(str);
+ CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
+ break;
+ default:
+ camel_mapi_store_summary_parent->store_info_set_string(s, mi, type, str);
+ break;
+ }
+}
+
+CamelMapiStoreInfo *
+camel_mapi_store_summary_full_name(CamelMapiStoreSummary *s, const char *full_name)
+{
+ int count, i;
+ CamelMapiStoreInfo *info;
+
+ count = camel_store_summary_count((CamelStoreSummary *)s);
+ for (i=0;i<count;i++) {
+ info = (CamelMapiStoreInfo *)camel_store_summary_index((CamelStoreSummary *)s, i);
+ if (info) {
+ if (strcmp(info->full_name, full_name) == 0)
+ return info;
+ camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info);
+ }
+ }
+
+ return NULL;
+
+}
+
+char *
+camel_mapi_store_summary_full_to_path(CamelMapiStoreSummary *s, const char *full_name, char dir_sep)
+{
+ char *path, *p;
+ int c;
+ const char *f;
+
+ if (dir_sep != '/') {
+ p = path = alloca(strlen(full_name)*3+1);
+ f = full_name;
+ while ( (c = *f++ & 0xff) ) {
+ if (c == dir_sep)
+ *p++ = '/';
+//FIXME : why ?? :(
+/* else if (c == '/' || c == '%') */
+/* p += sprintf(p, "%%%02X", c); */
+ else
+ *p++ = c;
+ }
+ *p = 0;
+ } else
+ path = (char *)full_name;
+
+ return g_strdup (path);
+}
+
+
+CamelMapiStoreInfo *
+camel_mapi_store_summary_add_from_full(CamelMapiStoreSummary *s, const char *full, char dir_sep)
+{
+ CamelMapiStoreInfo *info;
+ char *pathu8;
+ int len;
+ char *full_name;
+
+ d(printf("adding full name '%s' '%c'\n", full, dir_sep));
+ len = strlen(full);
+ full_name = alloca(len+1);
+ strcpy(full_name, full);
+
+ if (full_name[len-1] == dir_sep)
+ full_name[len-1] = 0;
+
+ info = camel_mapi_store_summary_full_name(s, full_name);
+ if (info) {
+ camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info);
+ d(printf(" already there\n"));
+ return info;
+ }
+ pathu8 = camel_mapi_store_summary_full_to_path(s, full_name, '/');
+ info = (CamelMapiStoreInfo *)camel_store_summary_add_from_path((CamelStoreSummary *)s, pathu8);
+ if (info)
+ camel_store_info_set_string((CamelStoreSummary *)s, (CamelStoreInfo *)info, CAMEL_STORE_INFO_LAST, full_name);
+
+ return info;
+}
+
+char *
+camel_mapi_store_summary_full_from_path(CamelMapiStoreSummary *s, const char *path)
+{
+ char *name = NULL;
+
+/* ns = camel_mapi_store_summary_namespace_find_path(s, path); */
+/* if (ns) */
+/* name = camel_mapi_store_summary_path_to_full(s, path, ns->sep); */
+
+ d(printf("looking up path %s -> %s\n", path, name?name:"not found"));
+
+ return name;
+}
Added: trunk/src/camel/camel-mapi-store-summary.h
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-store-summary.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,80 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef _CAMEL_MAPI_STORE_SUMMARY_H
+#define _CAMEL_MAPI_STORE_SUMMARY_H
+
+#include <camel/camel-object.h>
+#include <camel/camel-store-summary.h>
+
+#define CAMEL_MAPI_STORE_SUMMARY_VERSION (0)
+
+#define CAMEL_MAPI_STORE_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_mapi_store_summary_get_type (), CamelMapiStoreSummary)
+#define CAMEL_MAPI_STORE_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_mapi_store_summary_get_type (), CamelMapiStoreSummaryClass)
+#define CAMEL_IS_MAPI_STORE_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_mapi_store_summary_get_type ())
+
+G_BEGIN_DECLS
+
+typedef struct _CamelMapiStoreSummary CamelMapiStoreSummary;
+typedef struct _CamelMapiStoreSummaryClass CamelMapiStoreSummaryClass;
+
+typedef struct _CamelMapiStoreInfo CamelMapiStoreInfo;
+
+enum {
+ CAMEL_MAPI_STORE_INFO_FULL_NAME = CAMEL_STORE_INFO_LAST,
+ CAMEL_MAPI_STORE_INFO_LAST,
+};
+
+struct _CamelMapiStoreInfo {
+ CamelStoreInfo info;
+ char *full_name;
+};
+
+struct _CamelMapiStoreSummary {
+ CamelStoreSummary summary;
+
+ struct _CamelMapiStoreSummaryPrivate *priv;
+
+ /* header info */
+ guint32 version; /* version of base part of file */
+ guint32 capabilities;
+};
+
+struct _CamelMapiStoreSummaryClass {
+ CamelStoreSummaryClass summary_class;
+};
+
+CamelType camel_mapi_store_summary_get_type (void);
+CamelMapiStoreSummary *camel_mapi_store_summary_new (void);
+CamelMapiStoreInfo *camel_mapi_store_summary_full_name(CamelMapiStoreSummary *s, const char *full_name) ;
+CamelMapiStoreInfo *camel_mapi_store_summary_add_from_full(CamelMapiStoreSummary *s, const char *full, char dir_sep) ;
+
+char *camel_mapi_store_summary_full_to_path(CamelMapiStoreSummary *s, const char *full_name, char dir_sep) ;
+char *camel_mapi_store_summary_path_to_full(CamelMapiStoreSummary *s, const char *path, char dir_sep) ;
+char *camel_mapi_store_summary_full_from_path(CamelMapiStoreSummary *s, const char *path) ;
+
+#define camel_mapi_store_info_full_name(s, i) (camel_store_info_string((CamelStoreSummary *)s, (const CamelStoreInfo *)i, CAMEL_STORE_INFO_LAST))
+
+G_END_DECLS
+
+#endif /* ! _CAMEL_MAPI_STORE_SUMMARY_H */
Added: trunk/src/camel/camel-mapi-store.c
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-store.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,1351 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <camel/camel-sasl.h>
+#include <camel/camel-utf8.h>
+#include <camel/camel-tcp-stream-raw.h>
+
+#ifdef HAVE_SSL
+#include <camel/camel-tcp-stream-ssl.h>
+#endif
+
+#include <camel/camel-private.h>
+#include <camel/camel-session.h>
+#include <camel/camel-service.h>
+#include <camel/camel-store-summary.h>
+#include <camel/camel-i18n.h>
+#include <camel/camel-net-utils.h>
+
+#include "camel-mapi-store.h"
+#include "camel-mapi-folder.h"
+#include "camel-mapi-store-summary.h"
+#include "camel-mapi-summary.h"
+
+#include <exchange-mapi-utils.h>
+//#define d(x) x
+
+/* This definition should be in-sync with those in exchange-mapi-account-setup.c and exchange-account-listener.c */
+#define E_PASSWORD_COMPONENT "ExchangeMAPI"
+
+#define DISPLAY_NAME_FAVOURITES _("Favourites")
+#define DISPLAY_NAME_ALL_PUBLIC_FOLDERS _("All Public Folders")
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <libmapi/libmapi.h>
+#include <param.h>
+
+#define d(x) printf("%s(%d):%s:%s \n", __FILE__, __LINE__, __PRETTY_FUNCTION__, x)
+
+struct _CamelMapiStorePrivate {
+ char *user;
+ const char *profile;
+ char *base_url;
+ char *storage_path;
+
+ GHashTable *id_hash; /*get names from ids*/
+ GHashTable *name_hash;/*get ids from names*/
+ GHashTable *parent_hash;
+ GHashTable *default_folders; /*Default Type : Folder ID*/
+};
+
+static CamelOfflineStoreClass *parent_class = NULL;
+
+static void camel_mapi_store_class_init(CamelMapiStoreClass *);
+CamelType camel_mapi_store_get_type(void);
+static void camel_mapi_store_init(CamelMapiStore *, CamelMapiStoreClass *);
+static void camel_mapi_store_finalize(CamelObject *);
+
+/* service methods */
+static void mapi_construct(CamelService *, CamelSession *,
+ CamelProvider *, CamelURL *,
+ CamelException *);
+static char *mapi_get_name(CamelService *, gboolean );
+static gboolean mapi_connect(CamelService *, CamelException *);
+static gboolean mapi_disconnect(CamelService *, gboolean , CamelException *);
+static GList *mapi_query_auth_types(CamelService *, CamelException *);
+
+/* store methods */
+static CamelFolder *mapi_get_folder(CamelStore *, const char *, guint32, CamelException *);
+static CamelFolderInfo *mapi_create_folder(CamelStore *, const char *, const char *, CamelException *);
+static void mapi_delete_folder(CamelStore *, const char *, CamelException *);
+static void mapi_rename_folder(CamelStore *, const char *, const char *, CamelException *);
+static CamelFolderInfo *mapi_get_folder_info(CamelStore *, const char *, guint32, CamelException *);
+static void mapi_subscribe_folder(CamelStore *, const char *, CamelException *);
+static gboolean mapi_folder_subscribed (CamelStore *store, const char *folder_name);
+static void mapi_unsubscribe_folder(CamelStore *, const char *, CamelException *);
+static void mapi_noop(CamelStore *, CamelException *);
+static CamelFolderInfo * mapi_build_folder_info(CamelMapiStore *mapi_store, const char *parent_name, const char *folder_name);
+static void mapi_folders_sync (CamelMapiStore *store, CamelException *ex);
+static gboolean mapi_fid_is_system_folder (CamelMapiStore *mapi_store, const char *fid);
+
+static guint
+mapi_hash_folder_name(gconstpointer key)
+{
+ return g_str_hash(key);
+}
+
+static gint
+mapi_compare_folder_name(gconstpointer a, gconstpointer b)
+{
+ gconstpointer aname = a;
+ gconstpointer bname = b;
+
+ return g_str_equal(aname, bname);
+}
+
+static void
+camel_mapi_store_class_init(CamelMapiStoreClass *klass)
+{
+ CamelServiceClass *service_class =
+ CAMEL_SERVICE_CLASS (klass);
+ CamelStoreClass *store_class = (CamelStoreClass *) klass;
+
+ parent_class = (CamelOfflineStoreClass *) camel_type_get_global_classfuncs(CAMEL_TYPE_OFFLINE_STORE);
+
+ service_class->construct = mapi_construct;
+ service_class->get_name = mapi_get_name;
+ service_class->connect = mapi_connect;
+ service_class->disconnect = mapi_disconnect;
+ service_class->query_auth_types = mapi_query_auth_types;
+
+ store_class->hash_folder_name = mapi_hash_folder_name;
+ store_class->compare_folder_name = mapi_compare_folder_name;
+ /* store_class->get_inbox = mapi_get_inbox; */
+ store_class->get_folder = mapi_get_folder;
+ store_class->create_folder = mapi_create_folder;
+ store_class->delete_folder = mapi_delete_folder;
+ store_class->rename_folder = mapi_rename_folder;
+ store_class->get_folder_info = mapi_get_folder_info;
+ store_class->subscribe_folder = mapi_subscribe_folder;
+ store_class->folder_subscribed = mapi_folder_subscribed;
+ store_class->unsubscribe_folder = mapi_unsubscribe_folder;
+ store_class->noop = mapi_noop;
+}
+
+CamelType
+camel_mapi_store_get_type(void)
+{
+ static CamelType camel_mapi_store_type = CAMEL_INVALID_TYPE;
+
+ if (camel_mapi_store_type == CAMEL_INVALID_TYPE) {
+ camel_mapi_store_type = camel_type_register(camel_offline_store_get_type (),
+ "CamelMapiStores",
+ sizeof (CamelMapiStore),
+ sizeof (CamelMapiStoreClass),
+ (CamelObjectClassInitFunc) camel_mapi_store_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_mapi_store_init,
+ (CamelObjectFinalizeFunc) camel_mapi_store_finalize);
+ }
+
+ return camel_mapi_store_type;
+}
+
+/*
+** store is already initilyse to NULL or 0 value
+** klass already have a parent_class
+** nothing must be doing here
+*/
+static void camel_mapi_store_init(CamelMapiStore *store, CamelMapiStoreClass *klass)
+{
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
+ CamelMapiStorePrivate *priv = g_new0 (CamelMapiStorePrivate, 1);
+
+ mapi_store->summary = NULL;
+
+ priv->storage_path = NULL;
+ priv->base_url = NULL;
+
+ mapi_store->priv = priv;
+
+}
+
+static void camel_mapi_store_finalize(CamelObject *object)
+{
+}
+
+/* service methods */
+static void mapi_construct(CamelService *service, CamelSession *session,
+ CamelProvider *provider, CamelURL *url,
+ CamelException *ex)
+{
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (service);
+ CamelStore *store = CAMEL_STORE (service);
+ CamelMapiStorePrivate *priv = mapi_store->priv;
+ char *path = NULL;
+
+ CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
+
+ if (camel_exception_is_set (ex))
+ return;
+
+/* if (!(url->host || url->user)) { */
+/* camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_INVALID, */
+/* _("Host or user not available in url")); */
+/* } */
+
+ /*storage path*/
+ priv->storage_path = camel_session_get_storage_path (session, service, ex);
+ if (!priv->storage_path)
+ return;
+
+ /*store summary*/
+ path = g_alloca (strlen (priv->storage_path) + 32);
+ sprintf (path, "%s/.summary", priv->storage_path);
+
+ mapi_store->summary = camel_mapi_store_summary_new ();
+ camel_store_summary_set_filename ((CamelStoreSummary *)mapi_store->summary, path);
+
+ camel_store_summary_touch ((CamelStoreSummary *)mapi_store->summary);
+ camel_store_summary_load ((CamelStoreSummary *) mapi_store->summary);
+
+ /*user and profile*/
+ priv->user = g_strdup (url->user);
+ priv->profile = g_strdup (camel_url_get_param(url, "profile"));
+
+ /*base url*/
+ priv->base_url = camel_url_to_string (service->url, (CAMEL_URL_HIDE_PASSWORD |
+ CAMEL_URL_HIDE_PARAMS |
+ CAMEL_URL_HIDE_AUTH) );
+
+ /*Hash Table*/
+ priv->id_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->name_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->parent_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->default_folders = g_hash_table_new_full (g_int_hash, g_str_equal, g_free, g_free);
+
+ store->flags &= ~CAMEL_STORE_VJUNK;
+ store->flags &= ~CAMEL_STORE_VTRASH;
+
+ store->flags |= CAMEL_STORE_SUBSCRIPTIONS;
+
+}
+
+static char
+*mapi_get_name(CamelService *service, gboolean brief)
+{
+ if (brief) {
+ return g_strdup_printf(_("Exchange MAPI server %s"), service->url->host);
+ } else {
+ return g_strdup_printf(_("Exchange MAPI for %s on %s"),
+ service->url->user, service->url->host);
+ }
+}
+
+static gboolean
+check_for_connection (CamelService *service, CamelException *ex)
+{
+ /*Fixme : What happens when the network connection drops.
+ will mapi subsystem handle that ?*/
+ return exchange_mapi_connection_exists ();
+}
+
+static gboolean
+mapi_auth_loop (CamelService *service, CamelException *ex)
+{
+ CamelSession *session = camel_service_get_session (service);
+
+ char *errbuf = NULL;
+ gboolean authenticated = FALSE;
+
+ service->url->passwd = NULL;
+
+ while (!authenticated) {
+ if (errbuf) {
+ /* We need to un-cache the password before prompting again */
+ camel_session_forget_password (session, service, E_PASSWORD_COMPONENT, "password", ex);
+ g_free (service->url->passwd);
+ service->url->passwd = NULL;
+ }
+
+ if (!service->url->passwd ){
+ char *prompt;
+
+ prompt = g_strdup_printf (_("%sPlease enter the MAPI "
+ "password for %s %s"),
+ errbuf ? errbuf : "",
+ service->url->user,
+ service->url->host);
+ service->url->passwd =
+ camel_session_get_password (session, service, E_PASSWORD_COMPONENT,
+ prompt, "password", CAMEL_SESSION_PASSWORD_SECRET, ex);
+ g_free (prompt);
+ g_free (errbuf);
+ errbuf = NULL;
+
+ if (!service->url->passwd) {
+ camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
+ _("You did not enter a password."));
+ return FALSE;
+ }
+ }
+
+ exchange_mapi_connection_new (NULL,service->url->passwd);
+
+ if (!exchange_mapi_connection_exists ()) {
+ errbuf = g_strdup_printf (_("Unable to authenticate "
+ "to Exchange MAPI server. "));
+
+ camel_exception_clear (ex);
+ } else
+ authenticated = TRUE;
+
+ }
+ return TRUE;
+}
+
+
+static gboolean
+mapi_connect(CamelService *service, CamelException *ex)
+{
+ CamelMapiStore *store = CAMEL_MAPI_STORE (service);
+ CamelMapiStorePrivate *priv = store->priv;
+
+ if (service->status == CAMEL_SERVICE_DISCONNECTED) {
+ return FALSE;
+ }
+
+ if (!priv) {
+ store->priv = g_new0 (CamelMapiStorePrivate, 1);
+ priv = store->priv;
+ camel_service_construct (service, service->session, service->provider, service->url, ex);
+ }
+
+ CAMEL_SERVICE_REC_LOCK (service, connect_lock);
+ if (check_for_connection (service, ex)) {
+ CAMEL_SERVICE_REC_UNLOCK (service, connect_lock);
+ return TRUE;
+ }
+
+ if (!mapi_auth_loop (service, ex)) {
+ CAMEL_SERVICE_REC_UNLOCK (service, connect_lock);
+ camel_service_disconnect (service, TRUE, NULL);
+ return FALSE;
+ }
+
+ service->status = CAMEL_SERVICE_CONNECTED;
+ ((CamelOfflineStore *) store)->state = CAMEL_OFFLINE_STORE_NETWORK_AVAIL;
+
+ camel_store_summary_save ((CamelStoreSummary *) store->summary);
+
+ CAMEL_SERVICE_REC_UNLOCK (service, connect_lock);
+
+ return TRUE;
+}
+
+static gboolean
+mapi_disconnect(CamelService *service, gboolean clean, CamelException *ex)
+{
+ CamelMapiStore *store = CAMEL_MAPI_STORE (service);
+ CamelMapiStorePrivate *priv = store->priv;
+
+ /* Close the mapi subsystem */
+ exchange_mapi_connection_close ();
+
+ ((CamelOfflineStore *) store)->state = CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL;
+ service->status = CAMEL_SERVICE_DISCONNECTED;
+
+ return TRUE;
+}
+
+static GList *mapi_query_auth_types(CamelService *service, CamelException *ex)
+{
+ return NULL;
+}
+
+
+static gboolean
+hash_check_fid_presence (gpointer key, gpointer value, gpointer folder_id)
+{
+ return (g_ascii_strcasecmp (value, folder_id) == 0);
+}
+
+static gboolean
+mapi_fid_is_system_folder (CamelMapiStore *mapi_store, const char *fid)
+{
+ CamelMapiStorePrivate *priv = mapi_store->priv;
+
+ if (!(fid && *fid))
+ return FALSE;
+
+ return (g_hash_table_find (priv->default_folders, hash_check_fid_presence, fid) != NULL);
+}
+
+static const gchar*
+mapi_system_folder_fid (CamelMapiStore *mapi_store, int folder_type)
+{
+ CamelMapiStorePrivate *priv = mapi_store->priv;
+
+ return g_hash_table_lookup (priv->default_folders, &folder_type);
+}
+
+static CamelFolder *
+mapi_get_folder(CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex)
+{
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
+ CamelMapiStorePrivate *priv = mapi_store->priv;
+ char *storage_path = NULL;
+
+ storage_path = g_strdup_printf("%s/folders", priv->storage_path);
+
+ return camel_mapi_folder_new(store, folder_name, storage_path, flags, ex);
+}
+
+static CamelFolderInfo*
+mapi_create_folder(CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex)
+{
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
+ CamelMapiStorePrivate *priv = mapi_store->priv;
+ CamelFolderInfo *root = NULL;
+ char *parent_id;
+ mapi_id_t parent_fid, new_folder_id;
+
+ if (((CamelOfflineStore *) store)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL) {
+ camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create MAPI folders in offline mode."));
+ return NULL;
+ }
+
+ if (mapi_fid_is_system_folder (mapi_store, camel_mapi_store_folder_id_lookup (mapi_store, folder_name))) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create new folder `%s'"),
+ folder_name);
+ return NULL;
+ }
+
+ if (parent_name && (strlen(parent_name) > 0) )
+ parent_id = g_hash_table_lookup (priv->name_hash, parent_name);
+ else
+ parent_id = "";
+
+ if (!mapi_connect (CAMEL_SERVICE(store), ex)) {
+ camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, _("Authentication failed"));
+ return NULL;
+ }
+
+ CAMEL_SERVICE_REC_LOCK (store, connect_lock);
+
+
+ exchange_mapi_util_mapi_id_from_string (parent_id, &parent_fid);
+ new_folder_id = exchange_mapi_create_folder(olFolderInbox, parent_fid, folder_name);
+ if (new_folder_id != 0) {
+ CamelMapiStoreInfo *si;
+
+ root = mapi_build_folder_info(mapi_store, parent_name, folder_name);
+
+ si = camel_mapi_store_summary_add_from_full(mapi_store->summary, root->full_name, '/');
+ camel_store_summary_save((CamelStoreSummary *)mapi_store->summary);
+
+ g_hash_table_insert (priv->id_hash, g_strdup_printf ("%016llX", new_folder_id), g_strdup(folder_name));
+ g_hash_table_insert (priv->name_hash, g_strdup(root->full_name), g_strdup_printf ("%016llX", new_folder_id));
+ g_hash_table_insert (priv->parent_hash, g_strdup_printf ("%016llX", new_folder_id), g_strdup(parent_id));
+
+ camel_object_trigger_event (CAMEL_OBJECT (store), "folder_created", root);
+ }
+
+ CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
+ return root;
+
+}
+
+static void
+mapi_forget_folder (CamelMapiStore *mapi_store, const char *folder_name, CamelException *ex)
+{
+ CamelFolderSummary *summary;
+ CamelMapiStorePrivate *priv = mapi_store->priv;
+ char *summary_file, *state_file;
+ char *folder_dir, *storage_path;
+ CamelFolderInfo *fi;
+ const char *name;
+
+ name = folder_name;
+
+ storage_path = g_strdup_printf ("%s/folders", priv->storage_path);
+
+ /* Fixme Path - e_*-to_path */
+ folder_dir = g_strdup(g_strconcat (storage_path, "/", folder_name, NULL));
+
+ if (g_access(folder_dir, F_OK) != 0) {
+ g_free(folder_dir);
+ return;
+ }
+
+ summary_file = g_strdup_printf ("%s/summary", folder_dir);
+ summary = camel_mapi_summary_new(NULL,summary_file);
+ if(!summary) {
+ g_free(summary_file);
+ g_free(folder_dir);
+ return;
+ }
+
+ camel_object_unref (summary);
+ g_unlink (summary_file);
+ g_free (summary_file);
+
+ state_file = g_strdup_printf ("%s/cmeta", folder_dir);
+ g_unlink (state_file);
+ g_free (state_file);
+
+ g_rmdir (folder_dir);
+ g_free (folder_dir);
+
+ camel_store_summary_remove_path ( (CamelStoreSummary *)mapi_store->summary, folder_name);
+ camel_store_summary_save ( (CamelStoreSummary *)mapi_store->summary);
+
+ fi = mapi_build_folder_info(mapi_store, NULL, folder_name);
+ camel_object_trigger_event (CAMEL_OBJECT (mapi_store), "folder_deleted", fi);
+ camel_folder_info_free (fi);
+}
+
+static void
+mapi_delete_folder(CamelStore *store, const char *folder_name, CamelException *ex)
+{
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
+ CamelMapiStorePrivate *priv = mapi_store->priv;
+
+ const char *folder_id;
+ mapi_id_t folder_fid;
+ gboolean status = FALSE;
+
+ CAMEL_SERVICE_REC_LOCK (store, connect_lock);
+
+ if (!camel_mapi_store_connected ((CamelMapiStore *)store, ex)) {
+ CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
+ return;
+ }
+
+ folder_id = g_hash_table_lookup (priv->name_hash, folder_name);
+ exchange_mapi_util_mapi_id_from_string (folder_id, &folder_fid);
+ status = exchange_mapi_remove_folder (0, folder_fid);
+
+ if (status) {
+ /* Fixme ?? */
+/* if (mapi_store->current_folder) */
+/* camel_object_unref (mapi_store->current_folder); */
+ mapi_forget_folder(mapi_store,folder_name,ex);
+
+ g_hash_table_remove (priv->id_hash, folder_id);
+ g_hash_table_remove (priv->name_hash, folder_name);
+
+ g_hash_table_remove (priv->parent_hash, folder_id);
+ }
+
+ CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
+
+}
+
+//FIXME : Moves this function to toplevel camel. same is used in GW.
+
+static char *
+mapi_path_to_physical (const char *prefix, const char *vpath)
+{
+ const char *p, *newp;
+ char *dp;
+ char *ppath;
+ int ppath_len;
+ int prefix_len;
+
+ while (*vpath == '/')
+ vpath++;
+ if (!prefix)
+ prefix = "";
+
+ /* Calculate the length of the real path. */
+ ppath_len = strlen (vpath);
+ ppath_len++; /* For the ending zero. */
+
+ prefix_len = strlen (prefix);
+ ppath_len += prefix_len;
+ ppath_len++; /* For the separating slash. */
+
+ p = vpath;
+ while (1) {
+ newp = strchr (p, '/');
+ if (newp == NULL)
+ break;
+
+ /* Skip consecutive slashes. */
+ while (*newp == '/')
+ newp++;
+
+ p = newp;
+ };
+
+ ppath = g_malloc (ppath_len);
+ dp = ppath;
+
+ memcpy (dp, prefix, prefix_len);
+ dp += prefix_len;
+ *(dp++) = '/';
+
+ /* Copy the mangled path. */
+ p = vpath;
+ while (1) {
+ newp = strchr (p, '/');
+ if (newp == NULL) {
+ strcpy (dp, p);
+ break;
+ }
+
+ memcpy (dp, p, newp - p + 1); /* `+ 1' to copy the slash too. */
+ dp += newp - p + 1;
+
+ *(dp++) = '/';
+
+ /* Skip consecutive slashes. */
+ while (*newp == '/')
+ newp++;
+
+ p = newp;
+ }
+
+ return ppath;
+}
+
+static void
+mapi_rename_folder(CamelStore *store, const char *old_name, const char *new_name, CamelException *ex)
+{
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
+ CamelMapiStorePrivate *priv = mapi_store->priv;
+ char *oldpath, *newpath, *storepath;
+ const char *folder_id;
+ char *temp = NULL;
+ mapi_id_t fid;
+
+
+ CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
+
+ if (!camel_mapi_store_connected ((CamelMapiStore *)store, ex)) {
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+ return;
+ }
+
+ temp = strrchr (old_name, '/');
+ if (temp)
+ temp++;
+ else
+ temp = (char *)old_name;
+
+ folder_id = camel_mapi_store_folder_id_lookup (mapi_store, temp);
+ if (!folder_id) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot rename MAPI folder `%s'. Folder doesn't exist"),
+ old_name);
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+ return;
+ }
+
+ /*Do not allow rename for system folders.*/
+ if (mapi_fid_is_system_folder (mapi_store, folder_id)) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot rename Mapi folder `%s' to `%s'. Default folder."),
+ old_name, new_name);
+ return;
+ }
+
+
+ exchange_mapi_util_mapi_id_from_string (folder_id, &fid);
+
+ temp = strrchr (new_name, '/');
+ if (temp)
+ temp++;
+ else
+ temp = (char *)new_name;
+
+ if (!exchange_mapi_rename_folder (fid , temp))
+ {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot rename MAPI folder `%s' to `%s'"),
+ old_name, new_name);
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+ return;
+ }
+
+ g_hash_table_replace (priv->id_hash, g_strdup(folder_id), g_strdup(temp));
+
+ g_hash_table_insert (priv->name_hash, g_strdup(new_name), g_strdup(folder_id));
+ g_hash_table_remove (priv->name_hash, old_name);
+
+ storepath = g_strdup_printf ("%s/folders", priv->storage_path);
+ oldpath = mapi_path_to_physical (storepath, old_name);
+ newpath = mapi_path_to_physical (storepath, new_name);
+ g_free (storepath);
+
+ /*XXX: make sure the summary is also renamed*/
+ if (g_rename (oldpath, newpath) == -1) {
+ g_warning ("Could not rename message cache '%s' to '%s': %s: cache reset",
+ oldpath, newpath, strerror (errno));
+ }
+
+ g_free (oldpath);
+ g_free (newpath);
+ CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+}
+
+static guint32 hexnib(guint32 c)
+{
+ if (c >= '0' && c <= '9')
+ return c-'0';
+ else if (c>='A' && c <= 'Z')
+ return c-'A'+10;
+ else
+ return 0;
+}
+
+char *
+camel_mapi_store_summary_path_to_full(CamelMapiStoreSummary *s, const char *path, char dir_sep)
+{
+ unsigned char *full, *f;
+ guint32 c, v = 0;
+ const char *p;
+ int state=0;
+ char *subpath, *last = NULL;
+ CamelStoreInfo *si;
+
+ /* check to see if we have a subpath of path already defined */
+ subpath = alloca(strlen(path)+1);
+ strcpy(subpath, path);
+ do {
+ si = camel_store_summary_path((CamelStoreSummary *)s, subpath);
+ if (si == NULL) {
+ last = strrchr(subpath, '/');
+ if (last)
+ *last = 0;
+ }
+ } while (si == NULL && last);
+
+ /* path is already present, use the raw version we have */
+ if (si && strlen(subpath) == strlen(path)) {
+ f = g_strdup(camel_mapi_store_info_full_name(s, si));
+ camel_store_summary_info_free((CamelStoreSummary *)s, si);
+ return f;
+ }
+
+ f = full = alloca(strlen(path)*2+1);
+ if (si)
+ p = path + strlen(subpath);
+ else
+ p = path;
+
+ while ( (c = camel_utf8_getc((const unsigned char **)&p)) ) {
+ switch(state) {
+ case 0:
+ if (c == '%')
+ state = 1;
+ else {
+ if (c == '/')
+ c = dir_sep;
+ camel_utf8_putc(&f, c);
+ }
+ break;
+ case 1:
+ state = 2;
+ v = hexnib(c)<<4;
+ break;
+ case 2:
+ state = 0;
+ v |= hexnib(c);
+ camel_utf8_putc(&f, v);
+ break;
+ }
+ }
+ camel_utf8_putc(&f, c);
+
+ /* merge old path part if required */
+ f = g_strdup (full);
+ if (si) {
+ full = g_strdup_printf("%s%s", camel_mapi_store_info_full_name(s, si), f);
+ g_free(f);
+ camel_store_summary_info_free((CamelStoreSummary *)s, si);
+ f = full;
+ }
+
+ return f ;
+}
+
+
+//do we realy need this. move to utils then !
+static int
+match_path(const char *path, const char *name)
+{
+ char p, n;
+
+ p = *path++;
+ n = *name++;
+ while (n && p) {
+ if (n == p) {
+ p = *path++;
+ n = *name++;
+ } else if (p == '%') {
+ if (n != '/') {
+ n = *name++;
+ } else {
+ p = *path++;
+ }
+ } else if (p == '*') {
+ return TRUE;
+ } else
+ return FALSE;
+ }
+
+ return n == 0 && (p == '%' || p == 0);
+}
+
+static char *
+mapi_concat ( const char *prefix, const char *suffix)
+{
+ size_t len;
+
+ len = strlen (prefix);
+ if (len == 0 || prefix[len - 1] == '/')
+ return g_strdup_printf ("%s%s", prefix, suffix);
+ else
+ return g_strdup_printf ("%s%c%s", prefix, '/', suffix);
+}
+
+static CamelFolderInfo *
+mapi_build_folder_info(CamelMapiStore *mapi_store, const char *parent_name, const char *folder_name)
+{
+ CamelURL *url;
+ const char *name;
+ CamelFolderInfo *fi;
+ CamelMapiStorePrivate *priv = mapi_store->priv;
+
+ fi = g_malloc0(sizeof(*fi));
+
+ fi->unread = -1;
+ fi->total = -1;
+
+ if (parent_name) {
+ if (strlen(parent_name) > 0)
+ fi->full_name = g_strconcat(parent_name, "/", folder_name, NULL);
+ else
+ fi->full_name = g_strdup (folder_name);
+ } else
+ fi->full_name = g_strdup(folder_name);
+
+ url = camel_url_new(priv->base_url,NULL);
+ //g_free(url->path);
+ url->path = g_strdup_printf("/%s", fi->full_name);
+ fi->uri = camel_url_to_string(url,CAMEL_URL_HIDE_ALL);
+ //camel_url_free(url);
+
+ name = strrchr(fi->full_name,'/');
+ if(name == NULL)
+ name = fi->full_name;
+ else
+ name++;
+
+ /*Fixme : Mark system folders.*/
+
+ fi->name = g_strdup(name);
+ return fi;
+}
+
+static CamelFolderInfo *
+mapi_get_folder_info_offline (CamelStore *store, const char *top,
+ guint32 flags, CamelException *ex)
+{
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
+ CamelFolderInfo *fi;
+ GPtrArray *folders;
+ char *path, *name;
+ int i;
+ gboolean recursive, subscribed, info_fast, favourites = false;
+
+ recursive = (flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE);
+ subscribed = (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED);
+ info_fast = (flags & CAMEL_STORE_FOLDER_INFO_FAST);
+
+ folders = g_ptr_array_new ();
+
+ if (top == NULL)
+ top = "";
+
+ /* get starting point */
+ if (top[0] == 0) {
+ name = g_strdup("");
+ } else {
+ name = camel_mapi_store_summary_full_from_path(mapi_store->summary, top);
+ if (name == NULL)
+ name = camel_mapi_store_summary_path_to_full(mapi_store->summary, top, '/');
+ }
+
+ path = mapi_concat (name, "*");
+
+
+ for (i=0;i<camel_store_summary_count((CamelStoreSummary *)mapi_store->summary);i++) {
+ CamelStoreInfo *si = camel_store_summary_index((CamelStoreSummary *)mapi_store->summary, i);
+
+ if (si == NULL)
+ continue;
+
+ /* Based on exchange connector. Allow only All Public Folders heirarchy */
+ if ((!subscribed) && info_fast)
+ if (!(si->flags & CAMEL_MAPI_FOLDER_PUBLIC)) continue;
+
+ /*Allow Mailbox and Favourites (Subscribed public folders)*/
+ if (subscribed)
+ if (!(si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) continue;
+
+ if ( !strcmp(name, camel_mapi_store_info_full_name (mapi_store->summary, si))
+ || match_path (path, camel_mapi_store_info_full_name (mapi_store->summary, si))) {
+
+ gchar *store_info_path = camel_store_info_path((CamelStoreSummary *)mapi_store->summary, si);
+ gchar *parent_name = NULL;
+ gchar *folder_name = NULL;
+
+ /* TODO : UTF8 / i18n*/
+ if (g_str_has_prefix (store_info_path, DISPLAY_NAME_ALL_PUBLIC_FOLDERS) && subscribed) {
+ parent_name = DISPLAY_NAME_FAVOURITES;
+
+ folder_name = strrchr(store_info_path,'/');
+ if(folder_name != NULL)
+ store_info_path = ++folder_name;
+
+ favourites = true;
+ }
+
+ fi = mapi_build_folder_info(mapi_store, parent_name, store_info_path);
+ if (favourites) {
+ CamelURL *url;
+ url = camel_url_new(mapi_store->priv->base_url,NULL);
+ url->path = g_strdup_printf("/%s", camel_store_info_path((CamelStoreSummary *)mapi_store->summary, si));
+ fi->uri = camel_url_to_string(url,CAMEL_URL_HIDE_ALL);
+ }
+
+ fi->unread = si->unread;
+ fi->total = si->total;
+ fi->flags = si->flags;
+
+ g_ptr_array_add (folders, fi);
+ }
+ camel_store_summary_info_free((CamelStoreSummary *)mapi_store->summary, si);
+ }
+
+ /*FIXME*/
+ if (!((!subscribed) && info_fast) ) {
+ fi = mapi_build_folder_info(mapi_store, NULL, DISPLAY_NAME_FAVOURITES);
+ fi->flags |= CAMEL_FOLDER_NOSELECT;
+ fi->flags |= CAMEL_FOLDER_SYSTEM;
+
+ g_ptr_array_add (folders, fi);
+ }
+
+ g_free(name);
+ g_free (path);
+ fi = camel_folder_info_build (folders, top, '/', TRUE);
+ g_ptr_array_free (folders, TRUE);
+ return fi;
+}
+
+static CamelFolderInfo *
+mapi_convert_to_folder_info (CamelMapiStore *store, ExchangeMAPIFolder *folder, const char *url, CamelException *ex)
+{
+ const char *name = NULL;
+ gchar *parent, *id = NULL;
+ mapi_id_t mapi_id_folder;
+
+ char *par_name = NULL;
+ CamelFolderInfo *fi;
+ CamelMapiStorePrivate *priv = store->priv;
+
+ name = exchange_mapi_folder_get_name (folder);
+
+ id = g_strdup_printf ("%016llX", exchange_mapi_folder_get_fid (folder));
+
+ fi = g_new0 (CamelFolderInfo, 1);
+
+ if (folder->is_default) {
+ switch (folder->default_type) {
+ case olFolderTopInformationStore:
+ fi->flags |= CAMEL_FOLDER_NOSELECT;
+ break;
+ case olFolderInbox:
+ fi->flags |= CAMEL_FOLDER_TYPE_INBOX;
+ break;
+ case olFolderSentMail:
+ fi->flags |= CAMEL_FOLDER_TYPE_SENT;
+ break;
+ case olFolderDeletedItems:
+ fi->flags |= CAMEL_FOLDER_TYPE_TRASH;
+ break;
+ /*TODO : case olFolderJunkMail */
+ }
+
+ fi->flags |= CAMEL_FOLDER_SYSTEM;
+ }
+
+ if (folder->category == MAPI_PERSONAL_FOLDER) {
+ fi->flags |= CAMEL_MAPI_FOLDER_PERSONAL;
+ fi->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED; /*Set this default for mailbox.*/
+ } else if (folder->category == MAPI_FAVOURITE_FOLDER)
+ fi->flags |= CAMEL_MAPI_FOLDER_PUBLIC;
+
+ if (folder->child_count <=0)
+ fi->flags |= CAMEL_FOLDER_NOCHILDREN;
+ /*
+ parent_hash contains the "parent id <-> folder id" combination. So we form
+ the path for the full name in camelfolder info by looking up the hash table until
+ NULL is found
+ */
+
+ mapi_id_folder = exchange_mapi_folder_get_parent_id (folder);
+ parent = g_strdup_printf ("%016llX", mapi_id_folder);
+ par_name = g_hash_table_lookup (priv->id_hash, parent);
+
+ if (par_name != NULL) {
+ gchar *temp_parent = NULL;
+ gchar *temp = NULL;
+ gchar *str = g_strconcat (par_name, "/", name, NULL);
+
+ fi->name = g_strdup (name);
+
+ temp_parent = g_hash_table_lookup (priv->parent_hash, parent);
+ while (temp_parent) {
+ temp = g_hash_table_lookup (priv->id_hash, temp_parent );
+ if (temp == NULL) {
+ break;
+ }
+ str = g_strconcat ( temp, "/", str, NULL);
+
+ temp_parent = g_hash_table_lookup (priv->parent_hash, temp_parent);
+
+ }
+ fi->full_name = g_strdup (str);
+ fi->uri = g_strconcat (url, str, NULL);
+ g_free (str);
+ }
+ else {
+ fi->name = g_strdup (name);
+ fi->full_name = g_strdup (name);
+ fi->uri = g_strconcat (url, "", name, NULL);
+ }
+
+ /*name_hash returns the container id given the name */
+ g_hash_table_insert (priv->name_hash, g_strdup(fi->full_name), id);
+
+ fi->total = folder->total;
+ fi->unread = folder->unread_count;
+
+ return fi;
+}
+
+gboolean
+camel_mapi_store_connected (CamelMapiStore *store, CamelException *ex)
+{
+ if (((CamelOfflineStore *) store)->state == CAMEL_OFFLINE_STORE_NETWORK_AVAIL
+ && camel_service_connect ((CamelService *)store, ex))
+
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+mapi_folders_sync (CamelMapiStore *store, CamelException *ex)
+{
+ CamelMapiStorePrivate *priv = store->priv;
+ gboolean status;
+ GSList *folder_list = NULL, *temp_list = NULL, *list = NULL;
+ char *url, *temp_url;
+ CamelFolderInfo *info = NULL;
+ CamelMapiStoreInfo *mapi_si = NULL;
+
+ if (((CamelOfflineStore *) store)->state == CAMEL_OFFLINE_STORE_NETWORK_AVAIL) {
+ if (((CamelService *)store)->status == CAMEL_SERVICE_DISCONNECTED){
+ ((CamelService *)store)->status = CAMEL_SERVICE_CONNECTING;
+ mapi_connect ((CamelService *)store, ex);
+ }
+ }
+
+ if (!camel_mapi_store_connected (store, ex)) {
+ camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+ _("Folder list not available in offline mode."));
+ return;
+ }
+
+ status = exchange_mapi_get_folders_list (&folder_list);
+ if (!status) {
+ g_warning ("Could not get folder list..\n");
+ return;
+ }
+
+ status = exchange_mapi_get_pf_folders_list (&folder_list);
+ if (!status) {
+ g_warning ("Could not get Public folder list..\n");
+// return;
+ }
+
+ temp_list = folder_list;
+ list = folder_list;
+
+ url = camel_url_to_string (CAMEL_SERVICE(store)->url,
+ (CAMEL_URL_HIDE_PASSWORD|
+ CAMEL_URL_HIDE_PARAMS|
+ CAMEL_URL_HIDE_AUTH) );
+
+ if ( url[strlen(url) - 1] != '/') {
+ temp_url = g_strconcat (url, "/", NULL);
+ g_free ((char *)url);
+ url = temp_url;
+ }
+
+ /*populate the hash table for finding the mapping from container id <-> folder name*/
+ for (;temp_list != NULL ; temp_list = g_slist_next (temp_list) ) {
+ const char *name;
+ gchar *fid = NULL, *parent_id = NULL;
+
+ name = exchange_mapi_folder_get_name ((ExchangeMAPIFolder *)(temp_list->data));
+ fid = g_strdup_printf ("%016llX", exchange_mapi_folder_get_fid((ExchangeMAPIFolder *)(temp_list->data)));
+ parent_id = g_strdup_printf ("%016llX", exchange_mapi_folder_get_parent_id ((ExchangeMAPIFolder *)(temp_list->data)));
+
+ /*id_hash returns the name for a given container id*/
+ g_hash_table_insert (priv->id_hash, g_strdup (fid), g_strdup(name));
+
+ /* name_hash : name <-> fid mapping */
+ g_hash_table_insert (priv->name_hash, g_strdup(name), g_strdup (fid));
+
+ /*parent_hash returns the parent container id, given an id*/
+ g_hash_table_insert (priv->parent_hash, g_strdup(fid), g_strdup(parent_id));
+
+ if (((ExchangeMAPIFolder *)(temp_list->data))->is_default) {
+ guint *type = g_new0 (guint, 1);
+ *type = ((ExchangeMAPIFolder *)(temp_list->data))->default_type;
+ g_hash_table_insert (priv->default_folders, type,
+ g_strdup(fid));
+ }
+ }
+
+ for (;folder_list != NULL; folder_list = g_slist_next (folder_list)) {
+ ExchangeMAPIFolder *folder = (ExchangeMAPIFolder *) folder_list->data;
+
+ if (exchange_mapi_folder_is_root ((ExchangeMAPIFolder *)(folder)))
+ continue;
+
+ if ( folder->container_class != MAPI_FOLDER_TYPE_MAIL)
+ continue;
+
+ info = mapi_convert_to_folder_info (store, folder, (const char *)url, ex);
+ if (!(mapi_si = camel_store_summary_path ((CamelStoreSummary *)store->summary, info->full_name))){
+ mapi_si = camel_mapi_store_summary_add_from_full (store->summary, info->full_name, '/');
+ if (mapi_si == NULL) {
+ continue;
+ }
+ }
+
+ mapi_si->info.flags |= info->flags;
+ mapi_si->info.total = info->total;
+ mapi_si->info.unread = info->unread;
+ }
+
+ camel_store_summary_touch ((CamelStoreSummary *)store->summary);
+ camel_store_summary_save ((CamelStoreSummary *)store->summary);
+
+ g_free ((char *)url);
+
+ // g_hash_table_foreach (present, get_folders_free, NULL);
+ // g_hash_table_destroy (present);
+
+}
+
+static CamelFolderInfo*
+mapi_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelException *ex)
+{
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
+ CamelFolderInfo *info = NULL;
+ int s_count = 0;
+
+ /*
+ * Thanks to Michael, for his cached folders implementation in IMAP
+ * is used as is here.
+ */
+
+ if (((CamelOfflineStore *) store)->state == CAMEL_OFFLINE_STORE_NETWORK_AVAIL) {
+ if (((CamelService *)store)->status == CAMEL_SERVICE_DISCONNECTED){
+ ((CamelService *)store)->status = CAMEL_SERVICE_CONNECTING;
+ mapi_connect ((CamelService *)store, ex);
+ }
+ }
+
+ if (check_for_connection((CamelService *)store, ex)) {
+ mapi_folders_sync (mapi_store, ex);
+ if (camel_exception_is_set (ex)) {
+ CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
+ return NULL;
+ }
+ camel_store_summary_touch ((CamelStoreSummary *)mapi_store->summary);
+ camel_store_summary_save ((CamelStoreSummary *)mapi_store->summary);
+ }
+
+ /*camel_exception_clear (ex);*/
+end_r:
+ s_count = camel_store_summary_count((CamelStoreSummary *)mapi_store->summary);
+ info = mapi_get_folder_info_offline (store, top, flags, ex);
+ return info;
+}
+
+const gchar *
+camel_mapi_store_folder_id_lookup (CamelMapiStore *mapi_store, const char *folder_name)
+{
+ CamelMapiStorePrivate *priv = mapi_store->priv;
+
+ return g_hash_table_lookup (priv->name_hash, folder_name);
+}
+
+const gchar *
+camel_mapi_store_system_folder_fid (CamelMapiStore *mapi_store, guint folder_type)
+{
+ return mapi_system_folder_fid (mapi_store, folder_type);
+}
+
+const gchar *
+camel_mapi_store_folder_lookup (CamelMapiStore *mapi_store, const char *folder_id)
+{
+ CamelMapiStorePrivate *priv = mapi_store->priv;
+
+ return g_hash_table_lookup (priv->id_hash, folder_id);
+}
+
+const gchar *
+camel_mapi_store_get_profile_name (CamelMapiStore *mapi_store)
+{
+ CamelMapiStorePrivate *priv;
+
+ g_return_val_if_fail (CAMEL_IS_MAPI_STORE (mapi_store), NULL);
+
+ priv = mapi_store->priv;
+
+ return priv->profile;
+}
+
+static void
+mapi_subscribe_folder(CamelStore *store, const char *folder_name, CamelException *ex)
+{
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
+
+ CamelFolderInfo *fi;
+ CamelStoreInfo *si;
+ gchar *parent_name = NULL;
+ gchar *f_name = NULL;
+ CamelURL *url;
+ /* TODO : exchange_mapi_add_to_favorites (); */
+
+ if (si = camel_store_summary_path((CamelStoreSummary *)mapi_store->summary, folder_name)) {
+ if ((si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) == 0) {
+ si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
+ si->flags |= CAMEL_FOLDER_SUBSCRIBED;
+ camel_store_summary_touch((CamelStoreSummary *)mapi_store->summary);
+ }
+ camel_store_summary_info_free((CamelStoreSummary *)mapi_store->summary, si);
+ }
+
+ if (g_str_has_prefix (folder_name, DISPLAY_NAME_ALL_PUBLIC_FOLDERS) ) {
+ parent_name = DISPLAY_NAME_FAVOURITES;
+
+ f_name = strrchr(folder_name,'/');
+ if(f_name != NULL)
+ folder_name = ++f_name;
+ }
+
+ fi = mapi_build_folder_info(mapi_store, parent_name, g_strdup (folder_name)); /* FIXME */
+
+ url = camel_url_new(mapi_store->priv->base_url,NULL);
+ url->path = g_strdup_printf("/%s", camel_store_info_path((CamelStoreSummary *)mapi_store->summary, si));
+ fi->uri = camel_url_to_string(url,CAMEL_URL_HIDE_ALL);
+
+ fi->flags |= CAMEL_FOLDER_SUBSCRIBED;
+ fi->flags |= CAMEL_FOLDER_NOCHILDREN;
+ fi->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
+
+ camel_object_trigger_event (CAMEL_OBJECT (store), "folder_subscribed", fi);
+}
+
+static gboolean
+mapi_folder_subscribed (CamelStore *store, const char *folder_name)
+{
+ CamelMapiStore *mapi_store = (CamelMapiStore *) store;
+ CamelStoreInfo *si;
+ int truth = FALSE;
+
+ if ((si = camel_store_summary_path ((CamelStoreSummary *) mapi_store->summary, folder_name))) {
+ truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
+ camel_store_summary_info_free ((CamelStoreSummary *) mapi_store->summary, si);
+ }
+
+ return truth;
+}
+
+static void
+mapi_unsubscribe_folder(CamelStore *store, const char *folder_name, CamelException *ex)
+{
+ CamelFolderInfo *fi;
+ CamelStoreInfo *si;
+ gchar *parent_name = NULL;
+ gchar *f_name = NULL;
+ CamelURL *url;
+
+ CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
+
+ si = camel_store_summary_path((CamelStoreSummary *)mapi_store->summary, folder_name);
+ if (si) {
+ if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) {
+ si->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
+ camel_store_summary_touch((CamelStoreSummary *)mapi_store->summary);
+ camel_store_summary_save((CamelStoreSummary *)mapi_store->summary);
+ }
+ camel_store_summary_info_free((CamelStoreSummary *)mapi_store->summary, si);
+ }
+
+ if (g_str_has_prefix (folder_name, DISPLAY_NAME_ALL_PUBLIC_FOLDERS) ) {
+ parent_name = DISPLAY_NAME_FAVOURITES;
+
+ f_name = strrchr(folder_name,'/');
+ if(f_name != NULL)
+ folder_name = ++f_name;
+ }
+
+ fi = mapi_build_folder_info(mapi_store, parent_name, g_strdup (folder_name)); /* FIXME */
+
+ url = camel_url_new(mapi_store->priv->base_url,NULL);
+ url->path = g_strdup_printf("/%s", camel_store_info_path((CamelStoreSummary *)mapi_store->summary, si));
+ fi->uri = camel_url_to_string(url,CAMEL_URL_HIDE_ALL);
+
+ camel_object_trigger_event (CAMEL_OBJECT (store), "folder_unsubscribed", fi);
+}
+
+static void
+mapi_noop(CamelStore *store, CamelException *ex)
+{
+
+}
+
Added: trunk/src/camel/camel-mapi-store.h
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-store.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,101 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef __CAMEL_MAPI_STORE_H__
+#define __CAMEL_MAPI_STORE_H__
+
+#include <camel/camel-store.h>
+#include <camel/camel-offline-store.h>
+#include <camel-mapi-store-summary.h>
+#include <camel/camel-net-utils.h>
+#include <camel/camel-i18n.h>
+
+#include <exchange-mapi-folder.h>
+
+#define CAMEL_MAPI_STORE_TYPE (camel_mapi_store_get_type ())
+#define CAMEL_MAPI_STORE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_MAPI_STORE_TYPE, CamelMapiStore))
+#define CAMEL_MAPI_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_MAPI_STORE_TYPE, CamelMapiStoreClass))
+#define CAMEL_IS_MAPI_STORE(o) (CAMEL_CHECK_TYPE((o), CAMEL_MAPI_STORE_TYPE))
+
+/* TODO : Move this to libcamel. Task when merging */
+#define CAMEL_FOLDER_FLAGS_LAST (1<<13)
+
+#define CAMEL_MAPI_FOLDER_PUBLIC (CAMEL_FOLDER_FLAGS_LAST << 1)
+#define CAMEL_MAPI_FOLDER_PERSONAL (CAMEL_FOLDER_FLAGS_LAST << 2)
+#define CAMEL_MAPI_FOLDER_FORIEGN (CAMEL_FOLDER_FLAGS_LAST << 3)
+
+/**
+ * definition of CamelMAPIStore
+ */
+typedef struct _CamelMapiStore CamelMapiStore;
+typedef struct _CamelMapiStoreClass CamelMapiStoreClass;
+typedef struct _CamelMapiStorePrivate CamelMapiStorePrivate;
+
+struct _CamelMapiStore{
+ CamelOfflineStore parent_object;
+
+ struct _CamelMapiStoreSummary *summary;
+ CamelMapiStorePrivate *priv;
+/* char *base_url; */
+/* CamelURL *camel_url; */
+/* CamelFolderInfo *fi; */
+/* GHashTable *folders; */
+/* GMutex *folders_lock; */
+/* GMutex *connect_lock; */
+};
+
+
+
+
+struct _CamelMapiStoreClass {
+ CamelOfflineStoreClass parent_class;
+};
+
+
+/**
+ * PROTOTYPES
+ */
+
+#ifndef __BEGIN_DECLS
+#ifdef __cplusplus
+#define __BEGIN_DECLS extern "C" {
+#define __END_DECLS }
+#else
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+#endif
+
+__BEGIN_DECLS
+/* Standard Camel function */
+CamelType camel_mapi_store_get_type(void);
+gboolean camel_mapi_store_connected(CamelMapiStore *, CamelException *);
+
+const gchar* camel_mapi_store_folder_id_lookup (CamelMapiStore *mapi_store, const char *folder_name);
+const gchar* camel_mapi_store_folder_lookup (CamelMapiStore *mapi_store, const char *folder_id);
+const gchar* camel_mapi_store_get_profile_name (CamelMapiStore *mapi_store);
+const gchar *camel_mapi_store_system_folder_fid (CamelMapiStore *mapi_store, guint folder_type);
+
+__END_DECLS
+
+#endif /* __CAMEL_OPENCHANGE_STORE_H__ */
Added: trunk/src/camel/camel-mapi-summary.c
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-summary.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,284 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <camel/camel-db.h>
+#include <camel/camel-data-cache.h>
+#include <camel/camel-file-utils.h>
+#include <camel/camel-folder.h>
+
+#include "camel-mapi-folder.h"
+#include "camel-mapi-summary.h"
+
+#define CAMEL_MAPI_SUMMARY_VERSION (1)
+
+/* Macros for DB Summary */
+#define MS_EXTRACT_FIRST_DIGIT(val) val=strtoul (part, &part, 10);
+
+/*Prototypes*/
+static CamelFIRecord* mapi_summary_header_to_db (CamelFolderSummary *, CamelException *ex);
+static int mapi_summary_header_from_db (CamelFolderSummary *, CamelFIRecord *fir);
+
+static CamelMessageInfo *mapi_message_info_from_db (CamelFolderSummary *s, CamelMIRecord *mir) ;
+static CamelMIRecord *mapi_message_info_to_db (CamelFolderSummary *s, CamelMessageInfo *info) ;
+
+static CamelMessageContentInfo * mapi_content_info_from_db (CamelFolderSummary *s, CamelMIRecord *mir) ;
+static int mapi_content_info_to_db (CamelFolderSummary *s, CamelMessageContentInfo *info, CamelMIRecord *mir) ;
+
+static void camel_mapi_summary_class_init (CamelMapiSummaryClass *klass);
+static void camel_mapi_summary_init (CamelMapiSummary *obj);
+
+
+/*End of Prototypes*/
+
+
+static CamelFolderSummaryClass *camel_mapi_summary_parent ;
+
+
+CamelType
+camel_mapi_summary_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register(
+ camel_folder_summary_get_type(), "CamelMapiSummary",
+ sizeof (CamelMapiSummary),
+ sizeof (CamelMapiSummaryClass),
+ (CamelObjectClassInitFunc) camel_mapi_summary_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_mapi_summary_init,
+ NULL);
+ }
+
+ return type;
+}
+
+static CamelMessageInfo *
+mapi_message_info_clone(CamelFolderSummary *s, const CamelMessageInfo *mi)
+{
+ CamelMapiMessageInfo *to;
+ const CamelMapiMessageInfo *from = (const CamelMapiMessageInfo *)mi;
+
+ to = (CamelMapiMessageInfo *)camel_mapi_summary_parent->message_info_clone(s, mi);
+ to->server_flags = from->server_flags;
+
+ /* FIXME: parent clone should do this */
+ to->info.content = camel_folder_summary_content_info_new(s);
+
+ return (CamelMessageInfo *)to;
+}
+
+static void
+camel_mapi_summary_class_init (CamelMapiSummaryClass *klass)
+{
+ CamelFolderSummaryClass *cfs_class = (CamelFolderSummaryClass *) klass;
+
+ camel_mapi_summary_parent = CAMEL_FOLDER_SUMMARY_CLASS (camel_type_get_global_classfuncs (camel_folder_summary_get_type()));
+
+ cfs_class->message_info_clone = mapi_message_info_clone ;
+
+ cfs_class->summary_header_to_db = mapi_summary_header_to_db;
+ cfs_class->summary_header_from_db = mapi_summary_header_from_db;
+ cfs_class->message_info_to_db = mapi_message_info_to_db;
+ cfs_class->message_info_from_db = mapi_message_info_from_db;
+ cfs_class->content_info_to_db = mapi_content_info_to_db;
+ cfs_class->content_info_from_db = mapi_content_info_from_db;
+}
+
+
+static void
+camel_mapi_summary_init (CamelMapiSummary *obj)
+{
+ CamelFolderSummary *s = (CamelFolderSummary *)obj;
+
+ /* subclasses need to set the right instance data sizes */
+ s->message_info_size = sizeof(CamelMapiMessageInfo);
+ s->content_info_size = sizeof(CamelMapiMessageContentInfo);
+
+ /* Meta-summary - Overriding UID len */
+ s->meta_summary->uid_len = 2048;
+}
+
+
+/**
+ * camel_mapi_summary_new:
+ * @filename: the file to store the summary in.
+ *
+ * This will create a new CamelMapiSummary object and read in the
+ * summary data from disk, if it exists.
+ *
+ * Return value: A new CamelMapiSummary object.
+ **/
+CamelFolderSummary *
+camel_mapi_summary_new (struct _CamelFolder *folder, const char *filename)
+{
+ CamelException ex;
+
+ CamelFolderSummary *summary = CAMEL_FOLDER_SUMMARY (
+ camel_object_new (camel_mapi_summary_get_type ()));
+
+ camel_exception_init (&ex);
+
+ summary->folder = folder ;
+ camel_folder_summary_set_build_content (summary, TRUE);
+ camel_folder_summary_set_filename (summary, filename);
+
+ if (camel_folder_summary_load_from_db (summary, &ex) == -1) {
+ /* FIXME: Isn't this dangerous ? We clear the summary
+ if it cannot be loaded, for some random reason.
+ We need to pass the ex and find out why it is not loaded etc. ? */
+ camel_folder_summary_clear_db (summary);
+ g_warning ("Unable to load summary %s\n", camel_exception_get_description (&ex));
+ camel_exception_clear (&ex);
+ }
+
+ return summary;
+}
+
+static int
+mapi_summary_header_from_db (CamelFolderSummary *summary, CamelFIRecord *fir)
+{
+ CamelMapiSummary *mapi_summary = CAMEL_MAPI_SUMMARY (summary);
+ gchar *part;
+
+ if (camel_mapi_summary_parent->summary_header_from_db (summary, fir) == -1)
+ return -1 ;
+
+ part = fir->bdata;
+
+ if (part)
+ MS_EXTRACT_FIRST_DIGIT(mapi_summary->version);
+
+ if (part && part++) {
+ mapi_summary->sync_time_stamp = g_strdup (part);
+ }
+
+ return 0;
+}
+static CamelFIRecord *
+mapi_summary_header_to_db (CamelFolderSummary *summary, CamelException *ex)
+{
+ CamelMapiSummary *mapi_summary = CAMEL_MAPI_SUMMARY(summary);
+ struct _CamelFIRecord *fir;
+
+ fir = camel_mapi_summary_parent->summary_header_to_db (summary, ex);
+
+ if(!fir)
+ return NULL;
+
+ fir->bdata = g_strdup_printf ("%d %s", CAMEL_MAPI_SUMMARY_VERSION, mapi_summary->sync_time_stamp);
+
+ return fir;
+}
+
+static CamelMessageInfo*
+mapi_message_info_from_db (CamelFolderSummary *s, CamelMIRecord *mir)
+{
+ CamelMessageInfo *info ;
+
+ info = camel_mapi_summary_parent->message_info_from_db (s, mir) ;
+
+ return info ;
+}
+
+static CamelMIRecord *
+mapi_message_info_to_db (CamelFolderSummary *s, CamelMessageInfo *info)
+{
+ struct _CamelMIRecord *mir;
+
+ mir = camel_mapi_summary_parent->message_info_to_db (s, info);
+
+ return mir;
+}
+
+static CamelMessageContentInfo*
+mapi_content_info_from_db (CamelFolderSummary *s, CamelMIRecord *mir)
+{
+ char *part = mir->cinfo;
+ guint32 type=0;
+
+ if (part)
+ MS_EXTRACT_FIRST_DIGIT (type);
+
+ mir->cinfo = part;
+
+ if (type)
+ return camel_mapi_summary_parent->content_info_from_db (s, mir);
+ else
+ return camel_folder_summary_content_info_new (s);
+}
+
+static int
+mapi_content_info_to_db (CamelFolderSummary *s, CamelMessageContentInfo *info, CamelMIRecord *mir)
+{
+ if (info->type) {
+ mir->cinfo = g_strdup ("1");
+ return camel_mapi_summary_parent->content_info_to_db (s, info, mir);
+ } else {
+ mir->cinfo = g_strdup ("0");
+ return 0;
+ }
+}
+
+void
+mapi_summary_clear (CamelFolderSummary *summary, gboolean uncache)
+{
+ CamelFolderChangeInfo *changes;
+ CamelMessageInfo *info;
+ CamelException ex;
+ int i, count;
+ const char *uid;
+
+ changes = camel_folder_change_info_new ();
+ count = camel_folder_summary_count (summary);
+ for (i = 0; i < count; i++) {
+ if (!(info = camel_folder_summary_index (summary, i)))
+ continue;
+
+ uid = camel_message_info_uid (info);
+ camel_folder_change_info_remove_uid (changes, uid);
+ camel_folder_summary_remove_uid (summary, uid);
+ camel_message_info_free(info);
+ }
+
+ camel_folder_summary_clear (summary);
+ camel_exception_init (&ex);
+ /*TODO : Test exception */
+ camel_folder_summary_save_to_db (summary, &ex);
+
+ if (uncache)
+ camel_data_cache_clear (((CamelMapiFolder *) summary->folder)->cache, "cache", NULL);
+
+ if (camel_folder_change_info_changed (changes))
+ camel_object_trigger_event (summary->folder, "folder_changed", changes);
+ camel_folder_change_info_free (changes);
+}
Added: trunk/src/camel/camel-mapi-summary.h
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-summary.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef _CAMEL_MAPI_SUMMARY_H
+#define _CAMEL_MAPI_SUMMARY_H
+
+#include <camel/camel-folder-summary.h>
+#include <camel/camel-exception.h>
+#include <camel/camel-store.h>
+
+#define CAMEL_MAPI_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_mapi_summary_get_type (), CamelMapiSummary)
+#define CAMEL_MAPI_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_mapi_summary_get_type (), CamelMapiSummaryClass)
+#define CAMEL_IS_MAPI_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_mapi_summary_get_type ())
+
+G_BEGIN_DECLS
+
+typedef struct _CamelMapiSummary CamelMapiSummary ;
+typedef struct _CamelMapiSummaryClass CamelMapiSummaryClass ;
+typedef struct _CamelMapiMessageInfo CamelMapiMessageInfo ;
+typedef struct _CamelMapiMessageContentInfo CamelMapiMessageContentInfo ;
+
+/* extra summary flags*/
+enum {
+ CAMEL_GW_MESSAGE_JUNK = 1<<17,
+ CAMEL_GW_MESSAGE_NOJUNK = 1<<18,
+};
+
+struct _CamelMapiMessageInfo {
+ CamelMessageInfoBase info;
+
+ guint32 server_flags;
+} ;
+
+
+struct _CamelMapiMessageContentInfo {
+ CamelMessageContentInfo info ;
+} ;
+
+
+struct _CamelMapiSummary {
+ CamelFolderSummary parent ;
+
+ gchar *sync_time_stamp;
+ guint32 version ;
+ guint32 validity ;
+} ;
+
+
+struct _CamelMapiSummaryClass {
+ CamelFolderSummaryClass parent_class ;
+} ;
+
+
+CamelType camel_mapi_summary_get_type (void) ;
+
+CamelFolderSummary *camel_mapi_summary_new (struct _CamelFolder *folder, const char *filename) ;
+
+void mapi_summary_clear (CamelFolderSummary *summary, gboolean uncache);
+
+G_END_DECLS
+
+#endif /*_CAMEL_GW_SUMMARY_H*/
Added: trunk/src/camel/camel-mapi-transport.c
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-transport.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,454 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <libmapi/libmapi.h>
+#include <gen_ndr/exchange.h>
+
+#include <camel/camel-data-wrapper.h>
+#include <camel/camel-exception.h>
+#include <camel/camel-mime-filter-crlf.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-session.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-stream-mem.h>
+
+
+#include "camel-mapi-transport.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <camel/camel-sasl.h>
+#include <camel/camel-utf8.h>
+#include <camel/camel-tcp-stream-raw.h>
+
+#ifdef HAVE_SSL
+#include <camel/camel-tcp-stream-ssl.h>
+#endif
+
+
+#include <camel/camel-private.h>
+#include <camel/camel-i18n.h>
+#include <camel/camel-net-utils.h>
+#include "camel-mapi-store.h"
+#include "camel-mapi-folder.h"
+#include "camel-mapi-store-summary.h"
+#include <camel/camel-session.h>
+#include <camel/camel-store-summary.h>
+#define d(x) x
+
+#include <camel/camel-seekable-stream.h>
+#include <exchange-mapi-defs.h>
+
+#define STREAM_SIZE 4000
+
+CamelStore *get_store(void);
+
+void set_store(CamelStore *);
+
+static void
+mapi_item_add_recipient (const char *recipients, OlMailRecipientType type, GSList **recipient_list);
+static int
+mapi_message_item_send (MapiItem *item, GSList *attachments, GSList *recipients);
+
+
+static void
+mapi_item_debug_dump (MapiItem *item)
+{
+ printf("-----------------\n\n");
+ printf("%s(%d):%s: \n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ printf("item->header.from : %s\n",item->header.from);
+ //Use Recipient List
+ printf("item->header.subject : %s\n",item->header.subject);
+ //printf("item->msg.body_stream : %s\n",item->msg.body_stream);
+ printf("-----------------\n\n");
+}
+
+static void
+mapi_item_set_from(MapiItem *item, const char *from)
+{
+ if (item->header.from) {
+ free(item->header.from);
+ }
+ item->header.from = strdup(from);
+}
+
+static void
+mapi_item_set_subject(MapiItem *item, const char *subject)
+{
+ if (item->header.subject)
+ free(item->header.subject);
+
+ item->header.subject = strdup(subject);
+}
+
+#define MAX_READ_SIZE 0x1000
+
+static void
+mapi_item_set_body_stream (MapiItem *item, CamelStream *body, MapiItemPartType part_type)
+{
+ guint8 *buf = g_new0 (guint8 , STREAM_SIZE);
+ guint32 read_size = 0;
+ ExchangeMAPIStream *stream = g_new0 (ExchangeMAPIStream, 1);
+
+ camel_seekable_stream_seek((CamelSeekableStream *)body, 0, CAMEL_STREAM_SET);
+
+ stream->value = g_byte_array_new ();
+
+ while((read_size = camel_stream_read(body, (char *)buf, STREAM_SIZE))){
+ if (read_size == -1)
+ return;
+
+ stream->value = g_byte_array_append (stream->value, (char *) buf, read_size);
+ }
+
+ switch (part_type) {
+ case PART_TYPE_TEXT_HTML :
+ stream->proptag = PR_HTML;
+ break;
+ case PART_TYPE_PLAIN_TEXT:
+ stream->proptag = PR_BODY;
+ break;
+ }
+
+ if (stream->value->len < MAX_READ_SIZE)
+ item->msg.body_parts = g_slist_append (item->msg.body_parts, stream);
+ else
+ item->generic_streams = g_slist_append (item->generic_streams, stream);
+
+}
+
+static gboolean
+mapi_item_add_attach(MapiItem *item, const gchar *filename, const char *description,
+ CamelStream *content_stream, int content_size)
+{
+ guint8 *buf = g_new0 (guint8 , STREAM_SIZE);
+ guint32 read_size, flag;
+ ExchangeMAPIAttachment *item_attach;
+ ExchangeMAPIStream *stream;
+
+ item_attach = g_new0 (ExchangeMAPIAttachment, 1);
+
+ item_attach->cValues = 4;
+ item_attach->lpProps = g_new0 (struct SPropValue, 4);
+
+ flag = ATTACH_BY_VALUE;
+ set_SPropValue_proptag(&(item_attach->lpProps[0]), PR_ATTACH_METHOD, (const void *) (&flag));
+
+ /* MSDN Documentation: When the supplied offset is -1 (0xFFFFFFFF), the
+ * attachment is not rendered using the PR_RENDERING_POSITION property.
+ * All values other than -1 indicate the position within PR_BODY at which
+ * the attachment is to be rendered.
+ */
+ flag = 0xFFFFFFFF;
+ set_SPropValue_proptag(&(item_attach->lpProps[1]), PR_RENDERING_POSITION, (const void *) (&flag));
+
+ if (filename) {
+ set_SPropValue_proptag(&(item_attach->lpProps[2]), PR_ATTACH_FILENAME, (const void *) g_strdup(filename));
+ set_SPropValue_proptag(&(item_attach->lpProps[3]), PR_ATTACH_LONG_FILENAME, (const void *) g_strdup(filename));
+ }
+
+ stream = g_new0 (ExchangeMAPIStream, 1);
+ stream->proptag = PR_ATTACH_DATA_BIN;
+ stream->value = g_byte_array_new ();
+ camel_seekable_stream_seek((CamelSeekableStream *)content_stream, 0, CAMEL_STREAM_SET);
+ while((read_size = camel_stream_read(content_stream, (char *)buf, STREAM_SIZE))){
+ stream->value = g_byte_array_append (stream->value, buf, read_size);
+ }
+ item_attach->streams = g_slist_append (item_attach->streams, stream);
+
+ item->attachments = g_slist_append(item->attachments, item_attach);
+
+ return TRUE;
+}
+
+static gboolean
+mapi_do_multipart(CamelMultipart *mp, MapiItem *item)
+{
+ CamelDataWrapper *dw;
+ CamelStream *content_stream;
+ CamelContentType *type;
+ CamelMimePart *part;
+ gint n_part, i_part;
+ const gchar *filename;
+ const gchar *description;
+ const gchar *content_id;
+ gint content_size;
+
+ n_part = camel_multipart_get_number(mp);
+ for (i_part = 0; i_part < n_part; i_part++) {
+ /* getting part */
+ part = camel_multipart_get_part(mp, i_part);
+ dw = camel_medium_get_content_object (CAMEL_MEDIUM (part));
+ if (CAMEL_IS_MULTIPART(dw)) {
+ /* recursive */
+ if (!mapi_do_multipart(CAMEL_MULTIPART(dw), item))
+ return FALSE;
+ continue ;
+ }
+ /* filename */
+ filename = camel_mime_part_get_filename(part);
+
+ dw = camel_medium_get_content_object(CAMEL_MEDIUM(part));
+ content_stream = camel_stream_mem_new();
+ content_size = camel_data_wrapper_decode_to_stream (dw, (CamelStream *) content_stream);
+ camel_seekable_stream_seek((CamelSeekableStream *)content_stream, 0, CAMEL_STREAM_SET);
+
+ description = camel_mime_part_get_description(part);
+ content_id = camel_mime_part_get_content_id(part);
+
+ type = camel_mime_part_get_content_type(part);
+
+ if (i_part == 0 && camel_content_type_is (type, "text", "plain")) {
+ mapi_item_set_body_stream (item, content_stream, PART_TYPE_PLAIN_TEXT);
+ } else if (camel_content_type_is (type, "text", "html")) {
+ mapi_item_set_body_stream (item, content_stream, PART_TYPE_TEXT_HTML);
+ } else {
+ mapi_item_add_attach(item, filename, description,
+ content_stream, content_size);
+ }
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+mapi_send_to (CamelTransport *transport, CamelMimeMessage *message,
+ CamelAddress *from, CamelAddress *recipients, CamelException *ex)
+{
+ CamelDataWrapper *dw = NULL;
+ CamelContentType *type;
+ CamelStream *content_stream;
+ CamelMultipart *multipart;
+ const CamelInternetAddress *to, *cc, *bcc;
+ MapiItem *item = g_new0 (MapiItem, 1);
+ const char *namep;
+ const char *addressp;
+ const char *content_type;
+ gint st;
+ ssize_t content_size;
+ GSList *recipient_list = NULL;
+ GSList *attach_list = NULL;
+ gint i = 0;
+ /* headers */
+
+ if (!camel_internet_address_get((const CamelInternetAddress *)from, 0, &namep, &addressp)) {
+ printf("index\n");
+ return (FALSE);
+ }
+ /** WARNING: double check **/
+ mapi_item_set_from (item, namep);
+
+ to = camel_mime_message_get_recipients(message, CAMEL_RECIPIENT_TYPE_TO);
+ for (i = 0; camel_internet_address_get(to, i, &namep, &addressp); i++){
+ mapi_item_add_recipient (addressp, olTo, &recipient_list);
+ }
+
+ cc = camel_mime_message_get_recipients(message, CAMEL_RECIPIENT_TYPE_CC);
+ for (i = 0; camel_internet_address_get(cc, i, &namep, &addressp); i++) {
+ mapi_item_add_recipient (addressp, olCC, &recipient_list);
+ }
+
+ bcc = camel_mime_message_get_recipients(message, CAMEL_RECIPIENT_TYPE_BCC);
+ for (i = 0; camel_internet_address_get(bcc, i, &namep, &addressp); i++) {
+ mapi_item_add_recipient (addressp, olBCC, &recipient_list);
+ }
+
+ if (camel_mime_message_get_subject(message)) {
+ mapi_item_set_subject(item, camel_mime_message_get_subject(message));
+ }
+
+ /* contents body */
+ multipart = (CamelMultipart *)camel_medium_get_content_object (CAMEL_MEDIUM (message));
+
+ if (CAMEL_IS_MULTIPART(multipart)) {
+ if (mapi_do_multipart(CAMEL_MULTIPART(multipart), item))
+ printf("camel message multi part error\n");
+ } else {
+ content_stream = (CamelStream *)camel_stream_mem_new();
+ dw = camel_medium_get_content_object (CAMEL_MEDIUM (message));
+ type = camel_mime_part_get_content_type((CamelMimePart *)message);
+ content_type = camel_content_type_simple (type);
+ content_size = camel_data_wrapper_write_to_stream(dw, (CamelStream *)content_stream);
+ mapi_item_set_body_stream (item, content_stream, PART_TYPE_PLAIN_TEXT);
+ }
+
+ mapi_item_debug_dump (item);
+
+ /* send */
+ st = mapi_message_item_send(item, attach_list, recipient_list);
+
+ if (st == -1) {
+ printf("[!] cannot send(%s)\n", item->header.to);
+ mapi_errstr("Cannot Send", GetLastError());
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+static char*
+mapi_transport_get_name(CamelService *service, gboolean brief)
+{
+ if (brief) {
+ return g_strdup_printf (_("MAPI server %s"), service->url->host);
+ } else {
+ return g_strdup_printf (_("MAPI service for %s on %s"),
+ service->url->user, service->url->host);
+ }
+}
+
+
+static void
+camel_mapi_transport_class_init(CamelMapiTransportClass *camel_mapi_transport_class)
+{
+ CamelTransportClass *camel_transport_class =
+ CAMEL_TRANSPORT_CLASS (camel_mapi_transport_class);
+ CamelServiceClass *camel_service_class =
+ CAMEL_SERVICE_CLASS (camel_mapi_transport_class);
+
+ camel_service_class->get_name = mapi_transport_get_name;
+ camel_transport_class->send_to = mapi_send_to;
+}
+
+static void
+camel_mapi_transport_init (CamelTransport *transport)
+{
+
+}
+
+CamelType
+camel_mapi_transport_get_type (void)
+{
+ static CamelType camel_mapi_transport_type = CAMEL_INVALID_TYPE;
+
+ if (camel_mapi_transport_type == CAMEL_INVALID_TYPE) {
+ camel_mapi_transport_type =
+ camel_type_register (CAMEL_TRANSPORT_TYPE,
+ "CamelMapiTransport",
+ sizeof (CamelMapiTransport),
+ sizeof (CamelMapiTransportClass),
+ (CamelObjectClassInitFunc) camel_mapi_transport_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_mapi_transport_init,
+ NULL);
+ }
+
+ return camel_mapi_transport_type;
+}
+
+static gint
+mail_build_props (struct SPropValue **value, struct SPropTagArray *SPropTagArray, gpointer data)
+{
+
+ MapiItem *item = (MapiItem *) data;
+ struct SPropValue *props;
+ GSList *l;
+
+ uint32_t *msgflag = g_new0 (uint32_t, 1);
+ int i=0;
+
+ props = g_new0 (struct SPropValue, 6);
+
+ set_SPropValue_proptag(&props[i++], PR_CONVERSATION_TOPIC, g_strdup (item->header.subject));
+ set_SPropValue_proptag(&props[i++], PR_NORMALIZED_SUBJECT, g_strdup (item->header.subject));
+
+ *msgflag = MSGFLAG_UNSENT;
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_FLAGS, (void *)msgflag);
+
+ for (l = item->msg.body_parts; l; l = l->next) {
+ ExchangeMAPIStream *stream = (ExchangeMAPIStream *) (l->data);
+ struct SBinary_short *bin = g_new0 (struct SBinary_short, 1);
+
+ bin->cb = stream->value->len;
+ bin->lpb = (uint8_t *)stream->value->data;
+ if (stream->proptag == PR_HTML)
+ set_SPropValue_proptag(&props[i++], stream->proptag, (void *)bin);
+ else if (stream->proptag == PR_BODY)
+ set_SPropValue_proptag(&props[i++], stream->proptag, (void *)stream->value->data);
+ }
+
+ /* FIXME : */
+ /* editor = EDITOR_FORMAT_PLAINTEXT; */
+ /* set_SPropValue_proptag(&props[i++], PR_MSG_EDITOR_FORMAT, (const void *)editor); */
+
+ *value = props;
+ return i;
+}
+
+static void
+mapi_item_add_recipient (const char *recipients, OlMailRecipientType type, GSList **recipient_list)
+{
+ uint32_t val = 0;
+ const char *str = NULL;
+
+ if (!recipients)
+ return ;
+
+ ExchangeMAPIRecipient *recipient = g_new0 (ExchangeMAPIRecipient, 1);
+
+ recipient->email_id = recipients;
+
+ /* this memory should be freed somewhere, perhaps in the existing
+ * exchange_mapi_util_free_recipient_list() */
+ recipient->in.req_lpProps = g_new0 (struct SPropValue, 1);
+ recipient->in.req_cValues = 1;
+ set_SPropValue_proptag (&(recipient->in.req_lpProps[0]), PR_RECIPIENT_TYPE, (const void *) &type);
+
+ /* External recipient properties - set them only when the recipient is unresolved */
+ recipient->in.ext_lpProps = g_new0 (struct SPropValue, 5);
+ recipient->in.ext_cValues = 5;
+
+ val = DT_MAILUSER;
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[0]), PR_DISPLAY_TYPE, (const void *)&val);
+ val = MAPI_MAILUSER;
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[1]), PR_OBJECT_TYPE, (const void *)&val);
+ str = "SMTP";
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[2]), PR_ADDRTYPE, (const void *)(str));
+ str = recipient->email_id;
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[3]), PR_SMTP_ADDRESS, (const void *)(str));
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[4]), PR_DISPLAY_NAME, (const void *)(str));
+
+ *recipient_list = g_slist_append (*recipient_list, recipient);
+}
+
+static int
+mapi_message_item_send (MapiItem *item, GSList *attachments, GSList *recipients)
+{
+ guint64 fid = 0;
+
+ exchange_mapi_create_item (olFolderOutbox, fid, NULL, NULL, mail_build_props, item, recipients, item->attachments, item->generic_streams, 0);
+
+ return 0;
+}
Added: trunk/src/camel/camel-mapi-transport.h
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-mapi-transport.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Johnny Jacob <jjohnny novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef CAMEL_MAPI_TRANSPORT_H
+#define CAMEL_MAPI_TRANSPORT_H 1
+
+#include <libmapi/libmapi.h>
+#include <camel/camel-transport.h>
+#include <exchange-mapi-connection.h>
+
+#define CAMEL_MAPI_TRANSPORT_TYPE (camel_mapi_transport_get_type ())
+#define CAMEL_MAPI_TRANSPORT(obj) (CAMEL_CHECK_CAST((obj), CAMEL_MAPI_TRANSPORT_TYPE, CamelMapiTransport))
+#define CAMEL_MAPI_TRANSPORT_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_MAPI_TRANSPORT_TYPE, CamelMapiTransportClass))
+#define CAMEL_IS_MAPI_TRANSPORT(o) (CAMEL_CHECK_TYPE((o), CAMEL_MAPI_TRANSPORT_TYPE))
+
+G_BEGIN_DECLS
+
+typedef struct {
+ CamelTransport parent_object;
+ gboolean connected ;
+
+} CamelMapiTransport;
+
+
+typedef struct {
+ CamelTransportClass parent_class;
+
+} CamelMapiTransportClass;
+
+
+/* Standard Camel function */
+CamelType camel_mapi_transport_get_type (void);
+
+G_END_DECLS
+
+#endif /* CAMEL_MAPI_TRANSPORT_H */
Added: trunk/src/camel/camel-private.h
==============================================================================
--- (empty file)
+++ trunk/src/camel/camel-private.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,194 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef CAMEL_PRIVATE_H
+#define CAMEL_PRIVATE_H 1
+
+/* need a way to configure and save this data, if this header is to
+ be installed. For now, dont install it */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <pthread.h>
+#include <libedataserver/e-msgport.h>
+
+G_BEGIN_DECLS
+
+struct _CamelFolderPrivate {
+ GStaticRecMutex lock;
+ GStaticMutex change_lock;
+ /* must require the 'change_lock' to access this */
+ int frozen;
+ struct _CamelFolderChangeInfo *changed_frozen; /* queues changed events */
+};
+
+#define CAMEL_FOLDER_LOCK(f, l) \
+ (g_static_mutex_lock(&((CamelFolder *)f)->priv->l))
+#define CAMEL_FOLDER_UNLOCK(f, l) \
+ (g_static_mutex_unlock(&((CamelFolder *)f)->priv->l))
+#define CAMEL_FOLDER_REC_LOCK(f, l) \
+ (g_static_rec_mutex_lock(&((CamelFolder *)f)->priv->l))
+#define CAMEL_FOLDER_REC_UNLOCK(f, l) \
+ (g_static_rec_mutex_unlock(&((CamelFolder *)f)->priv->l))
+
+
+struct _CamelStorePrivate {
+ GStaticRecMutex folder_lock; /* for locking folder operations */
+};
+
+#define CAMEL_STORE_LOCK(f, l) \
+ (g_static_rec_mutex_lock(&((CamelStore *)f)->priv->l))
+#define CAMEL_STORE_UNLOCK(f, l) \
+ (g_static_rec_mutex_unlock(&((CamelStore *)f)->priv->l))
+
+
+struct _CamelTransportPrivate {
+ GMutex *send_lock; /* for locking send operations */
+};
+
+#define CAMEL_TRANSPORT_LOCK(f, l) (g_mutex_lock(((CamelTransport *)f)->priv->l))
+#define CAMEL_TRANSPORT_UNLOCK(f, l) (g_mutex_unlock(((CamelTransport *)f)->priv->l))
+
+
+struct _CamelServicePrivate {
+ GStaticRecMutex connect_lock; /* for locking connection operations */
+ GStaticMutex connect_op_lock; /* for locking the connection_op */
+};
+
+#define CAMEL_SERVICE_LOCK(f, l) \
+ (g_static_mutex_lock(&((CamelService *)f)->priv->l))
+#define CAMEL_SERVICE_UNLOCK(f, l) \
+ (g_static_mutex_unlock(&((CamelService *)f)->priv->l))
+#define CAMEL_SERVICE_REC_LOCK(f, l) \
+ (g_static_rec_mutex_lock(&((CamelService *)f)->priv->l))
+#define CAMEL_SERVICE_REC_UNLOCK(f, l) \
+ (g_static_rec_mutex_unlock(&((CamelService *)f)->priv->l))
+
+
+struct _CamelSessionPrivate {
+ GMutex *lock; /* for locking everything basically */
+ GMutex *thread_lock; /* locking threads */
+
+ int thread_id;
+ GHashTable *thread_active;
+ GThreadPool *thread_pool;
+
+ GHashTable *thread_msg_op;
+};
+
+#define CAMEL_SESSION_LOCK(f, l) (g_mutex_lock(((CamelSession *)f)->priv->l))
+#define CAMEL_SESSION_UNLOCK(f, l) (g_mutex_unlock(((CamelSession *)f)->priv->l))
+
+
+/* most of this stuff really is private, but the lock can be used by subordinate classes */
+struct _CamelFolderSummaryPrivate {
+ GHashTable *filter_charset; /* CamelMimeFilterCharset's indexed by source charset */
+
+ struct _CamelMimeFilterIndex *filter_index;
+ struct _CamelMimeFilterBasic *filter_64;
+ struct _CamelMimeFilterBasic *filter_qp;
+ struct _CamelMimeFilterBasic *filter_uu;
+ struct _CamelMimeFilterSave *filter_save;
+ struct _CamelMimeFilterHTML *filter_html;
+
+ struct _CamelStreamFilter *filter_stream;
+
+ struct _CamelIndex *index;
+
+ GMutex *summary_lock; /* for the summary hashtable/array */
+ GMutex *io_lock; /* load/save lock, for access to saved_count, etc */
+ GMutex *filter_lock; /* for accessing any of the filtering/indexing stuff, since we share them */
+ GMutex *alloc_lock; /* for setting up and using allocators */
+ GMutex *ref_lock; /* for reffing/unreffing messageinfo's ALWAYS obtain before summary_lock */
+};
+
+#define CAMEL_SUMMARY_LOCK(f, l) (g_mutex_lock(((CamelFolderSummary *)f)->priv->l))
+#define CAMEL_SUMMARY_UNLOCK(f, l) (g_mutex_unlock(((CamelFolderSummary *)f)->priv->l))
+
+
+struct _CamelStoreSummaryPrivate {
+ GMutex *summary_lock; /* for the summary hashtable/array */
+ GMutex *io_lock; /* load/save lock, for access to saved_count, etc */
+ GMutex *alloc_lock; /* for setting up and using allocators */
+ GMutex *ref_lock; /* for reffing/unreffing messageinfo's ALWAYS obtain before summary_lock */
+};
+
+#define CAMEL_STORE_SUMMARY_LOCK(f, l) (g_mutex_lock(((CamelStoreSummary *)f)->priv->l))
+#define CAMEL_STORE_SUMMARY_UNLOCK(f, l) (g_mutex_unlock(((CamelStoreSummary *)f)->priv->l))
+
+
+struct _CamelVeeFolderPrivate {
+ GList *folders; /* lock using subfolder_lock before changing/accessing */
+ GList *folders_changed; /* for list of folders that have changed between updates */
+
+ GMutex *summary_lock; /* for locking vfolder summary */
+ GMutex *subfolder_lock; /* for locking the subfolder list */
+ GMutex *changed_lock; /* for locking the folders-changed list */
+};
+
+#define CAMEL_VEE_FOLDER_LOCK(f, l) (g_mutex_lock(((CamelVeeFolder *)f)->priv->l))
+#define CAMEL_VEE_FOLDER_UNLOCK(f, l) (g_mutex_unlock(((CamelVeeFolder *)f)->priv->l))
+
+
+struct _CamelDataWrapperPrivate {
+ pthread_mutex_t stream_lock;
+};
+
+#define CAMEL_DATA_WRAPPER_LOCK(dw, l) (pthread_mutex_lock(&((CamelDataWrapper *)dw)->priv->l))
+#define CAMEL_DATA_WRAPPER_UNLOCK(dw, l) (pthread_mutex_unlock(&((CamelDataWrapper *)dw)->priv->l))
+
+
+/* most of this stuff really is private, but the lock can be used by subordinate classes */
+struct _CamelCertDBPrivate {
+ GMutex *db_lock; /* for the db hashtable/array */
+ GMutex *io_lock; /* load/save lock, for access to saved_count, etc */
+ GMutex *alloc_lock; /* for setting up and using allocators */
+ GMutex *ref_lock; /* for reffing/unreffing certs */
+};
+
+#define CAMEL_CERTDB_LOCK(db, l) (g_mutex_lock (((CamelCertDB *) db)->priv->l))
+#define CAMEL_CERTDB_UNLOCK(db, l) (g_mutex_unlock (((CamelCertDB *) db)->priv->l))
+
+#ifdef G_OS_WIN32
+int fsync (int fd);
+
+const char *_camel_get_localedir (void) G_GNUC_CONST;
+const char *_camel_get_libexecdir (void) G_GNUC_CONST;
+const char *_camel_get_providerdir (void) G_GNUC_CONST;
+
+#undef EVOLUTION_LOCALEDIR
+#define EVOLUTION_LOCALEDIR _camel_get_localedir ()
+
+#undef CAMEL_LIBEXECDIR
+#define CAMEL_LIBEXECDIR _camel_get_libexecdir ()
+
+#undef CAMEL_PROVIDERDIR
+#define CAMEL_PROVIDERDIR _camel_get_providerdir ()
+
+#endif /* G_OS_WIN32 */
+
+G_END_DECLS
+
+#endif /* CAMEL_PRIVATE_H */
Added: trunk/src/camel/libcamelmapi.urls
==============================================================================
--- (empty file)
+++ trunk/src/camel/libcamelmapi.urls Wed Nov 19 04:28:20 2008
@@ -0,0 +1 @@
+mapi
Added: trunk/src/libexchangemapi/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/Makefile.am Wed Nov 19 04:28:20 2008
@@ -0,0 +1,60 @@
+INCLUDES = \
+ -DG_LOG_DOMAIN=\"libexchangemapi\" \
+ -DMAPI_DATADIR=\""$(mapidatadir)"\" \
+ $(EVOLUTION_DATA_SERVER_CFLAGS) \
+ $(LIBEDATASERVER_CFLAGS) \
+ -I$(top_srcdir) \
+ $(EVOLUTION_CALENDAR_CFLAGS) \
+ $(LIBMAPI_CFLAGS)
+
+
+lib_LTLIBRARIES = libexchangemapi-1.0.la
+
+mapidata_DATA = \
+ tz-mapi-to-ical \
+ tz-ical-to-mapi
+
+libexchangemapi_1_0_la_SOURCES = \
+ exchange-mapi-defs.h \
+ exchange-mapi-folder.c \
+ exchange-mapi-folder.h \
+ exchange-mapi-connection.c \
+ exchange-mapi-connection.h \
+ exchange-mapi-utils.c \
+ exchange-mapi-utils.h \
+ exchange-mapi-cal-utils.c \
+ exchange-mapi-cal-utils.h \
+ exchange-mapi-cal-tz-utils.c \
+ exchange-mapi-cal-tz-utils.h \
+ exchange-mapi-cal-recur-utils.c \
+ exchange-mapi-cal-recur-utils.h
+
+
+libexchangemapi_1_0_la_LIBADD = \
+ $(EVOLUTION_DATA_SERVER_LIBS) \
+ $(EVOLUTION_CALENDAR_LIBS) \
+ $(LIBMAPI_LIBS)
+
+libexchangemapiincludedir = $(edataserver_privincludedir)/mapi
+
+libexchangemapiinclude_HEADERS = \
+ exchange-mapi-defs.h \
+ exchange-mapi-folder.h \
+ exchange-mapi-connection.h \
+ exchange-mapi-utils.h \
+ exchange-mapi-cal-utils.h \
+ exchange-mapi-cal-tz-utils.h \
+ exchange-mapi-cal-recur-utils.h
+
+%-1.0.pc: %.pc.in
+ cp $< $@
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libexchangemapi-1.0.pc
+
+EXTRA_DIST = \
+ $(pkgconfig_DATA:-$(API_VERSION).pc=.pc.in) \
+ $(mapidata_DATA)
+
+DISTCLEANFILES = $(pkgconfig_DATA)
+
Added: trunk/src/libexchangemapi/exchange-mapi-cal-recur-utils.c
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-cal-recur-utils.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,1150 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "exchange-mapi-cal-recur-utils.h"
+
+/* Reader/Writer versions */
+#define READER_VERSION 0x3004
+#define WRITER_VERSION 0x3004
+#define READER_VERSION2 0x3006
+#define WRITER_VERSION2 0x3009
+
+#if 0
+struct ChangeHighlight {
+ uint32_t Size;
+ uint32_t Value;
+ uint32_t Reserved;
+};
+
+struct ExtendedException {
+ struct ChangeHighlight ch;
+ uint32_t ReservedEE1Size;
+ uint32_t ReservedEE1;
+ uint32_t StartDateTime;
+ uint32_t EndDateTime;
+ uint32_t OrigStartDate;
+ uint16_t WideCharSubjectLength;
+ gchar *WideCharSubject;
+ uint16_t WideCharLocationLength;
+ gchar *WideCharLocation;
+ uint32_t ReservedEE2Size;
+ uint32_t ReservedEE2;
+};
+
+struct ExceptionInfo {
+ uint32_t StartDateTime;
+ uint32_t EndDateTime;
+ uint32_t OrigStartDate;
+ uint16_t OverrideFlags;
+ uint16_t SubjectLength;
+ uint16_t SubjectLength2;
+ gchar *Subject;
+ uint32_t MeetingType;
+ uint32_t ReminderDelta;
+ uint32_t ReminderSet;
+ uint16_t LocationLength;
+ uint16_t LocationLength2;
+ gchar *Location;
+ uint32_t BusyStatus;
+ uint32_t Attachment;
+ uint32_t SubType;
+ uint32_t AppointmentColor;
+};
+#endif
+
+static icalrecurrencetype_weekday
+get_ical_weekstart (uint32_t fdow)
+{
+ switch (fdow) {
+ case FirstDOW_Sunday : return ICAL_SUNDAY_WEEKDAY;
+ case FirstDOW_Monday : return ICAL_MONDAY_WEEKDAY;
+ case FirstDOW_Tuesday : return ICAL_TUESDAY_WEEKDAY;
+ case FirstDOW_Wednesday : return ICAL_WEDNESDAY_WEEKDAY;
+ case FirstDOW_Thursday : return ICAL_THURSDAY_WEEKDAY;
+ case FirstDOW_Friday : return ICAL_FRIDAY_WEEKDAY;
+ case FirstDOW_Saturday : return ICAL_SATURDAY_WEEKDAY;
+ default : return ICAL_SUNDAY_WEEKDAY;
+ }
+}
+
+static uint32_t
+get_mapi_weekstart (icalrecurrencetype_weekday weekstart)
+{
+ switch (weekstart) {
+ case ICAL_SUNDAY_WEEKDAY : return FirstDOW_Sunday;
+ case ICAL_MONDAY_WEEKDAY : return FirstDOW_Monday;
+ case ICAL_TUESDAY_WEEKDAY : return FirstDOW_Tuesday;
+ case ICAL_WEDNESDAY_WEEKDAY: return FirstDOW_Wednesday;
+ case ICAL_THURSDAY_WEEKDAY : return FirstDOW_Thursday;
+ case ICAL_FRIDAY_WEEKDAY : return FirstDOW_Friday;
+ case ICAL_SATURDAY_WEEKDAY : return FirstDOW_Saturday;
+ default : return FirstDOW_Sunday;
+ }
+}
+
+static uint32_t
+get_mapi_day (icalrecurrencetype_weekday someday)
+{
+ switch (someday) {
+ case ICAL_SUNDAY_WEEKDAY : return olSunday;
+ case ICAL_MONDAY_WEEKDAY : return olMonday;
+ case ICAL_TUESDAY_WEEKDAY : return olTuesday;
+ case ICAL_WEDNESDAY_WEEKDAY: return olWednesday;
+ case ICAL_THURSDAY_WEEKDAY : return olThursday;
+ case ICAL_FRIDAY_WEEKDAY : return olFriday;
+ case ICAL_SATURDAY_WEEKDAY : return olSaturday;
+ default : return 0;
+ }
+}
+
+static int32_t
+get_ical_pos (uint32_t pos)
+{
+ switch (pos) {
+ case RecurrenceN_First : return 1;
+ case RecurrenceN_Second : return 2;
+ case RecurrenceN_Third : return 3;
+ case RecurrenceN_Fourth : return 4;
+ case RecurrenceN_Last : return (-1);
+ default : return 0;
+ }
+}
+
+static uint32_t
+get_mapi_pos (int32_t pos)
+{
+ switch (pos) {
+ case 1 : return RecurrenceN_First;
+ case 2 : return RecurrenceN_Second;
+ case 3 : return RecurrenceN_Third;
+ case 4 : return RecurrenceN_Fourth;
+ case -1 : return RecurrenceN_Last;
+ default : return 0;
+ }
+}
+
+#define cFileTimeUnitsPerSecond 10000000
+
+static void
+convert_recurrence_minutes_to_date (uint32_t minutes, struct FILETIME *ft)
+{
+ NTTIME nt;
+
+ nt = (NTTIME) minutes * (60 * cFileTimeUnitsPerSecond);
+
+ ft->dwLowDateTime = (uint32_t)((nt << 32) >> 32);
+ ft->dwHighDateTime = (uint32_t)(nt >> 32);
+}
+
+static uint32_t
+convert_date_to_recurrence_minutes (const struct FILETIME *ft)
+{
+ NTTIME minutes;
+
+ minutes = ft->dwHighDateTime;
+ minutes = minutes << 32;
+ minutes |= ft->dwLowDateTime;
+
+ minutes = minutes / (60 * cFileTimeUnitsPerSecond);
+
+ return (uint32_t)(minutes);
+}
+
+static time_t
+convert_filetime_to_timet (const struct FILETIME *ft)
+{
+ NTTIME time;
+
+ time = ft->dwHighDateTime;
+ time = time << 32;
+ time |= ft->dwLowDateTime;
+
+ return nt_time_to_unix (time);
+}
+
+static void
+convert_timet_to_filetime (time_t t, struct FILETIME *ft)
+{
+ NTTIME nt;
+
+ unix_to_nt_time (&nt, t);
+
+ ft->dwLowDateTime = (uint32_t)((nt << 32) >> 32);
+ ft->dwHighDateTime = (uint32_t)(nt >> 32);
+}
+
+static time_t
+convert_recurrence_minutes_to_timet (uint32_t minutes)
+{
+ NTTIME nt;
+
+ nt = (NTTIME) minutes * (60 * cFileTimeUnitsPerSecond);
+
+ return nt_time_to_unix (nt);
+}
+
+static uint32_t
+convert_timet_to_recurrence_minutes (time_t t)
+{
+ NTTIME minutes;
+
+ unix_to_nt_time (&minutes, t);
+
+ minutes = minutes / (60 * cFileTimeUnitsPerSecond);
+
+ return (uint32_t)(minutes);
+}
+
+static gboolean
+check_calendar_type (guint16 type)
+{
+ /* Calendar Type - We support Gregorian only. */
+ if (type == CAL_DEFAULT || type == CAL_GREGORIAN)
+ return TRUE;
+ else {
+ g_warning ("Calendar type = 0x%04X - Evolution does not handle such calendar types.", type);
+ return FALSE;
+ }
+}
+
+gboolean
+exchange_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp)
+{
+ struct icalrecurrencetype rt;
+ guint16 flag16;
+ guint32 flag32;
+ guint8 *ptr = ba->data;
+ gint i;
+ GSList *exdate_list = NULL, *modified_list = NULL;
+ gboolean repeats_until_date = FALSE;
+
+ icalrecurrencetype_clear (&rt);
+
+ /* Reader version */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (READER_VERSION != flag16)
+ return FALSE;
+
+ /* Writer version */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (WRITER_VERSION != flag16)
+ return FALSE;
+
+ /* FREQUENCY */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (flag16 == RecurFrequency_Daily) {
+ rt.freq = ICAL_DAILY_RECURRENCE;
+
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (flag16 == PatternType_Day) {
+ /* Daily every N days */
+
+ /* Calendar Type */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (!check_calendar_type (flag16))
+ return FALSE;
+
+ /* FirstDateTime (some crappy mod here) */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* INTERVAL */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ rt.interval = (short) (flag32 / (24 * 60));
+
+ /* some constant 0 for the stuff we handle */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32)
+ return FALSE;
+
+ } else if (flag16 == PatternType_Week) {
+ /* Daily every weekday */
+
+ /* Calendar Type */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (!check_calendar_type (flag16))
+ return FALSE;
+
+ /* NOTE: Evolution does not handle daily-every-weekday any different
+ * from a weekly recurrence.
+ */
+ rt.freq = ICAL_WEEKLY_RECURRENCE;
+
+ /* FirstDateTime (some crappy mod here) */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* INTERVAL */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ rt.interval = (short) (flag32);
+
+ /* some constant 0 for the stuff we handle */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32)
+ return FALSE;
+
+ /* BITMASK */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ i = 0;
+ if (flag32 & olSunday)
+ rt.by_day[i++] = ICAL_SUNDAY_WEEKDAY;
+ if (flag32 & olMonday)
+ rt.by_day[i++] = ICAL_MONDAY_WEEKDAY;
+ if (flag32 & olTuesday)
+ rt.by_day[i++] = ICAL_TUESDAY_WEEKDAY;
+ if (flag32 & olWednesday)
+ rt.by_day[i++] = ICAL_WEDNESDAY_WEEKDAY;
+ if (flag32 & olThursday)
+ rt.by_day[i++] = ICAL_THURSDAY_WEEKDAY;
+ if (flag32 & olFriday)
+ rt.by_day[i++] = ICAL_FRIDAY_WEEKDAY;
+ if (flag32 & olSaturday)
+ rt.by_day[i++] = ICAL_SATURDAY_WEEKDAY;
+ }
+
+ } else if (flag16 == RecurFrequency_Weekly) {
+ rt.freq = ICAL_WEEKLY_RECURRENCE;
+
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (flag16 == PatternType_Week) {
+ /* weekly every N weeks (for all events and non-regenerating tasks) */
+
+ /* Calendar Type */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (!check_calendar_type (flag16))
+ return FALSE;
+
+ /* FirstDateTime (some crappy mod here) */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* INTERVAL */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ rt.interval = (short) (flag32);
+
+ /* some constant 0 */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32)
+ return FALSE;
+
+ /* BITMASK */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ i = 0;
+ if (flag32 & olSunday)
+ rt.by_day[i++] = ICAL_SUNDAY_WEEKDAY;
+ if (flag32 & olMonday)
+ rt.by_day[i++] = ICAL_MONDAY_WEEKDAY;
+ if (flag32 & olTuesday)
+ rt.by_day[i++] = ICAL_TUESDAY_WEEKDAY;
+ if (flag32 & olWednesday)
+ rt.by_day[i++] = ICAL_WEDNESDAY_WEEKDAY;
+ if (flag32 & olThursday)
+ rt.by_day[i++] = ICAL_THURSDAY_WEEKDAY;
+ if (flag32 & olFriday)
+ rt.by_day[i++] = ICAL_FRIDAY_WEEKDAY;
+ if (flag32 & olSaturday)
+ rt.by_day[i++] = ICAL_SATURDAY_WEEKDAY;
+
+ } else if (flag16 == 0x0) {
+ /* weekly every N weeks (for all regenerating tasks) */
+
+ /* Calendar Type */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (!check_calendar_type (flag16))
+ return FALSE;
+
+ /* FIXME: we don't handle regenerating tasks */
+ g_warning ("Evolution does not handle recurring tasks.");
+ return FALSE;
+ }
+
+ } else if (flag16 == RecurFrequency_Monthly) {
+ rt.freq = ICAL_MONTHLY_RECURRENCE;
+
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (flag16 == PatternType_Month || flag16 == PatternType_MonthEnd) {
+ guint16 pattern = flag16;
+ /* Monthly every N months on day D or last day. */
+
+ /* Calendar Type */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (!check_calendar_type (flag16))
+ return FALSE;
+
+ /* FirstDateTime (some crappy mod here) */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* INTERVAL */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ rt.interval = (short) (flag32);
+
+ /* some constant 0 for the stuff we handle */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32)
+ return FALSE;
+
+ /* MONTH_DAY */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (pattern == PatternType_Month)
+ rt.by_month_day[0] = (short) (flag32);
+ else if (pattern == PatternType_MonthEnd)
+ rt.by_month_day[0] = (short) (-1);
+
+ } else if (flag16 == PatternType_MonthNth) {
+ gboolean post_process = FALSE;
+ guint32 mask = 0;
+ /* Monthly every N months on the Xth Y */
+
+ /* Calendar Type */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (!check_calendar_type (flag16))
+ return FALSE;
+
+ /* FirstDateTime (some crappy mod here) */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* INTERVAL */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ rt.interval = (short) (flag32);
+
+ /* some constant 0 for the stuff we handle */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32)
+ return FALSE;
+
+ /* BITMASK */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32 == olSunday)
+ rt.by_day[0] = ICAL_SUNDAY_WEEKDAY;
+ else if (flag32 == olMonday)
+ rt.by_day[0] = ICAL_MONDAY_WEEKDAY;
+ else if (flag32 == olTuesday)
+ rt.by_day[0] = ICAL_TUESDAY_WEEKDAY;
+ else if (flag32 == olWednesday)
+ rt.by_day[0] = ICAL_WEDNESDAY_WEEKDAY;
+ else if (flag32 == olThursday)
+ rt.by_day[0] = ICAL_THURSDAY_WEEKDAY;
+ else if (flag32 == olFriday)
+ rt.by_day[0] = ICAL_FRIDAY_WEEKDAY;
+ else if (flag32 == olSaturday)
+ rt.by_day[0] = ICAL_SATURDAY_WEEKDAY;
+ else {
+ post_process = TRUE;
+ mask = flag32;
+ }
+
+ /* RecurrenceN */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (!post_process) {
+ rt.by_set_pos[0] = get_ical_pos (flag32);
+ if (rt.by_set_pos[0] == 0)
+ return FALSE;
+ } else {
+ if (mask == (olSunday | olMonday | olTuesday | olWednesday | olThursday | olFriday | olSaturday)) {
+ rt.by_month_day[0] = get_ical_pos (flag32);
+ if (rt.by_month_day[0] == 0)
+ return FALSE;
+ } else {
+ /* FIXME: Can we/LibICAL support any other types here? Namely, weekday and weekend-day */
+ g_warning ("Encountered a recurrence type Evolution cannot handle. ");
+ return FALSE;
+ }
+ }
+ }
+
+ } else if (flag16 == RecurFrequency_Yearly) {
+ rt.freq = ICAL_YEARLY_RECURRENCE;
+
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (flag16 == PatternType_Month) {
+ /* Yearly on day D of month M */
+
+ /* Calendar Type */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (!check_calendar_type (flag16))
+ return FALSE;
+
+ /* FirstDateTime (some crappy mod here) */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* INTERVAL */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ rt.interval = (short) (flag32 / 12);
+
+ /* some constant 0 for the stuff we handle */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32)
+ return FALSE;
+
+ /* MONTH_DAY - but we don't need this */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ } else if (flag16 == PatternType_MonthNth) {
+ /* Yearly on the Xth Y of month M */
+
+ g_warning ("Encountered a recurrence pattern Evolution cannot handle.");
+
+ /* Calendar Type */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (!check_calendar_type (flag16))
+ return FALSE;
+
+ /* FirstDateTime (some crappy mod here) */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* INTERVAL */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ rt.interval = (short) (flag32 / 12);
+
+ /* some constant 0 */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32)
+ return FALSE;
+
+ /* BITMASK */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* RecurrenceN */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* TODO: Add support for this kinda recurrence in Evolution */
+ return FALSE;
+ }
+ } else
+ return FALSE;
+
+ /* End Type - followed by Occurence count */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32 == END_AFTER_DATE) {
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ repeats_until_date = TRUE;
+ } else if (flag32 == END_AFTER_N_OCCURRENCES) {
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ rt.count = flag32;
+ } else if (flag32 == END_NEVER_END) {
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ }
+
+ /* week_start */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ rt.week_start = get_ical_weekstart (flag32);
+
+ /* number of exceptions */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32) {
+ for (i = 0; i < flag32; ++i) {
+ uint32_t exdate;
+ struct icaltimetype tt, *val;
+ ECalComponentDateTime *dt = g_new0 (ECalComponentDateTime, 1);
+
+ exdate = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ tt = icaltime_from_timet_with_zone (convert_recurrence_minutes_to_timet (exdate), 1, 0);
+
+ val = g_new0(struct icaltimetype, 1);
+ memcpy (val, &tt, sizeof(struct icaltimetype));
+
+ dt->value = val;
+ dt->tzid = g_strdup ("UTC");
+
+ exdate_list = g_slist_append (exdate_list, dt);
+ }
+ }
+
+ /* number of changed exceptions */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ /* FIXME: Parse modified instances */
+ if (flag32)
+ ptr += flag32 * sizeof (guint32);
+
+ /* start date */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* end date */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (repeats_until_date) {
+ rt.until = icaltime_from_timet_with_zone (convert_recurrence_minutes_to_timet (flag32), 1, 0);
+ }
+
+ /* some constant */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32 != READER_VERSION2)
+ return FALSE;
+
+ /* some constant */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32 != WRITER_VERSION2)
+ return FALSE;
+
+ /* start time in mins */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* end time in mins */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+
+ /* modified exceptions */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ if (flag16 != 0x0)
+ return FALSE;
+
+ /* reserved block1 size - has to be 0 */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32 != 0x0)
+ return FALSE;
+
+ /* reserved block2 size - has to be 0 */
+ flag32 = *((guint32 *)ptr);
+ ptr += sizeof (guint32);
+ if (flag32 != 0x0)
+ return FALSE;
+
+ /* Set the recurrence */
+ {
+ GSList l;
+
+ l.data = &rt;
+ l.next = NULL;
+
+ e_cal_component_set_rrule_list (comp, &l);
+ }
+
+ /* FIXME: this also has modified instances */
+ e_cal_component_set_exdate_list (comp, exdate_list);
+
+ g_print ("\n== MAPI to ICAL == The recurrence blob data is as follows:\n");
+ for (i = 0; i < ba->len; ++i)
+ g_print ("0x%02X ", ba->data[i]);
+ g_print("\n== End of stream ==\n");
+
+ return TRUE;
+}
+
+static guint32
+compute_startdate (ECalComponent *comp)
+{
+ ECalComponentDateTime dtstart;
+ guint32 flag32;
+
+ e_cal_component_get_dtstart (comp, &dtstart);
+ dtstart.value->hour = dtstart.value->minute = dtstart.value->second = 0;
+ flag32 = convert_timet_to_recurrence_minutes (icaltime_as_timet_with_zone (*(dtstart.value), 0));
+
+ e_cal_component_free_datetime (&dtstart);
+
+ return flag32;
+}
+
+static guint32
+compute_rdaily_firstdatetime (ECalComponent *comp, guint32 period)
+{
+ return (compute_startdate (comp) % period);
+}
+
+static guint32
+compute_rweekly_firstdatetime (ECalComponent *comp, icalrecurrencetype_weekday week_start, guint32 period)
+{
+ ECalComponentDateTime dtstart;
+ guint32 flag32;
+ int cur_weekday = 0, weekstart_weekday = 0, diff = 0;
+ time_t t;
+
+ e_cal_component_get_dtstart (comp, &dtstart);
+ dtstart.value->hour = dtstart.value->minute = dtstart.value->second = 0;
+ cur_weekday = icaltime_day_of_week (*(dtstart.value));
+ t = icaltime_as_timet_with_zone (*(dtstart.value), 0);
+ e_cal_component_free_datetime (&dtstart);
+
+ switch (week_start) {
+ case ICAL_SUNDAY_WEEKDAY : weekstart_weekday = 1; break;
+ case ICAL_MONDAY_WEEKDAY : weekstart_weekday = 2; break;
+ case ICAL_TUESDAY_WEEKDAY : weekstart_weekday = 3; break;
+ case ICAL_WEDNESDAY_WEEKDAY : weekstart_weekday = 4; break;
+ case ICAL_THURSDAY_WEEKDAY : weekstart_weekday = 5; break;
+ case ICAL_FRIDAY_WEEKDAY : weekstart_weekday = 6; break;
+ case ICAL_SATURDAY_WEEKDAY : weekstart_weekday = 7; break;
+ default : weekstart_weekday = 1; break;
+ };
+
+ diff = cur_weekday - weekstart_weekday;
+
+ if (diff == 0);
+ else if (diff > 0)
+ t -= (diff * 24 * 60 * 60);
+ else if (diff < 0)
+ t -= ((diff + 7) * 24 * 60 * 60);
+
+ flag32 = convert_timet_to_recurrence_minutes (t);
+
+ return (flag32 % period);
+}
+
+/* The most fucked up algorithm ever conceived by (..you know who..) */
+static guint32
+compute_rmonthly_firstdatetime (ECalComponent *comp, guint32 period)
+{
+ const guint8 dinm[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ ECalComponentDateTime dtstart;
+ guint32 flag32, monthindex, i;
+
+ e_cal_component_get_dtstart (comp, &dtstart);
+ monthindex = (guint32)((((guint64)(12) * (dtstart.value->year - 1601)) + (dtstart.value->month - 1)) % period);
+ e_cal_component_free_datetime (&dtstart);
+
+ for (flag32 = 0, i = 0; i < monthindex; ++i)
+ flag32 += dinm[(i % 12) + 1] * 24 * 60;
+
+ return flag32;
+}
+
+static guint32
+calculate_no_of_occurrences (ECalComponent *comp, const struct icalrecurrencetype *rt)
+{
+ ECalComponentDateTime dtstart;
+ icalrecur_iterator *iter;
+ struct icaltimetype next;
+ guint32 count = 1;
+
+ e_cal_component_get_dtstart (comp, &dtstart);
+
+ for (iter = icalrecur_iterator_new (*rt, *(dtstart.value)),
+ next = icalrecur_iterator_next(iter);
+ !icaltime_is_null_time(next);
+ next = icalrecur_iterator_next(iter))
+ ++count;
+
+ icalrecur_iterator_free (iter);
+ e_cal_component_free_datetime (&dtstart);
+
+ return count;
+}
+
+static gint
+compare_guint32 (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ return (*((guint32 *) a) - *((guint32 *) b));
+}
+
+GByteArray *
+exchange_mapi_cal_util_rrule_to_bin (ECalComponent *comp, GSList *modified_comps)
+{
+ struct icalrecurrencetype *rt;
+ guint16 flag16;
+ guint32 flag32, end_type;
+ gint i;
+ GSList *rrule_list = NULL, *exdate_list = NULL;
+ GByteArray *ba = NULL;
+
+ if (!e_cal_component_has_recurrences (comp))
+ return NULL;
+
+ e_cal_component_get_rrule_list (comp, &rrule_list);
+ e_cal_component_get_exdate_list (comp, &exdate_list);
+
+ if (g_slist_length (rrule_list) != 1)
+ goto cleanup;
+
+ rt = (struct icalrecurrencetype *)(rrule_list->data);
+
+ ba = g_byte_array_new ();
+
+ /* Reader Version */
+ flag16 = READER_VERSION;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* Writer Version */
+ flag16 = WRITER_VERSION;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ if (rt->freq == ICAL_DAILY_RECURRENCE) {
+ flag16 = RecurFrequency_Daily;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* Pattern Type - it would be PatternType_Day since we have only "Daily every N days"
+ * The other type would be parsed as a weekly recurrence.
+ */
+ flag16 = PatternType_Day;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* Calendar Type */
+ flag16 = CAL_DEFAULT;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* FirstDateTime */
+ flag32 = compute_rdaily_firstdatetime (comp, (rt->interval * (60 * 24)));
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* INTERVAL */
+ flag32 = (rt->interval * (60 * 24));
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* This would be 0 for the stuff we handle */
+ flag32 = 0x0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* No PatternTypeSpecific for PatternType_Day */
+
+ } else if (rt->freq == ICAL_WEEKLY_RECURRENCE) {
+ flag16 = RecurFrequency_Weekly;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* Pattern Type - it would be PatternType_Week since we don't support any other type. */
+ flag16 = PatternType_Week;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* Calendar Type */
+ flag16 = CAL_DEFAULT;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* FirstDateTime */
+ flag32 = compute_rweekly_firstdatetime (comp, rt->week_start, (rt->interval * (60 * 24 * 7)));
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* INTERVAL */
+ flag32 = rt->interval;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* This would be 0 for the stuff we handle */
+ flag32 = 0x0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* BITMASK */
+ for (flag32 = 0x0, i = 0; i < ICAL_BY_DAY_SIZE; ++i) {
+ if (rt->by_day[i] == ICAL_SUNDAY_WEEKDAY)
+ flag32 |= olSunday;
+ else if (rt->by_day[i] == ICAL_MONDAY_WEEKDAY)
+ flag32 |= olMonday;
+ else if (rt->by_day[i] == ICAL_TUESDAY_WEEKDAY)
+ flag32 |= olTuesday;
+ else if (rt->by_day[i] == ICAL_WEDNESDAY_WEEKDAY)
+ flag32 |= olWednesday;
+ else if (rt->by_day[i] == ICAL_THURSDAY_WEEKDAY)
+ flag32 |= olThursday;
+ else if (rt->by_day[i] == ICAL_FRIDAY_WEEKDAY)
+ flag32 |= olFriday;
+ else if (rt->by_day[i] == ICAL_SATURDAY_WEEKDAY)
+ flag32 |= olSaturday;
+ else
+ break;
+ }
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ } else if (rt->freq == ICAL_MONTHLY_RECURRENCE) {
+ guint16 pattern = 0x0; guint32 mask = 0x0, flag = 0x0;
+
+ flag16 = RecurFrequency_Monthly;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ if (rt->by_month_day[0] >= 1 && rt->by_month_day[0] <= 31) {
+ pattern = PatternType_Month;
+ flag = rt->by_month_day[0];
+ } else if (rt->by_month_day[0] == -1) {
+ pattern = PatternType_MonthNth;
+ mask = (olSunday | olMonday | olTuesday | olWednesday | olThursday | olFriday | olSaturday);
+ flag = RecurrenceN_Last;
+ } else if (rt->by_day[0] >= ICAL_SUNDAY_WEEKDAY && rt->by_day[0] <= ICAL_SATURDAY_WEEKDAY) {
+ pattern = PatternType_MonthNth;
+ mask = get_mapi_day (rt->by_day[0]);
+ flag = get_mapi_pos (rt->by_set_pos[0]);
+ }
+
+ /* Pattern Type */
+ flag16 = pattern;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* Calendar Type */
+ flag16 = CAL_DEFAULT;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* FirstDateTime */
+ flag32 = compute_rmonthly_firstdatetime (comp, rt->interval);
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* INTERVAL */
+ flag32 = rt->interval;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* This would be 0 for the stuff we handle */
+ flag32 = 0x0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ if (pattern == PatternType_Month) {
+ flag32 = flag;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ if (!(flag))
+ g_warning ("Possibly setting incorrect values in the stream. ");
+ } else if (pattern == PatternType_MonthNth) {
+ flag32 = mask;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ flag32 = flag;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ if (!(flag && mask))
+ g_warning ("Possibly setting incorrect values in the stream. ");
+ } else
+ g_warning ("Possibly setting incorrect values in the stream. ");
+
+ } else if (rt->freq == ICAL_YEARLY_RECURRENCE) {
+ flag16 = RecurFrequency_Yearly;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* Pattern Type - it would be PatternType_Month since we don't support any other type. */
+ flag16 = PatternType_Month;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* Calendar Type */
+ flag16 = CAL_DEFAULT;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* FirstDateTime - uses the same function as monthly recurrence */
+ flag32 = compute_rmonthly_firstdatetime (comp, 0xC);
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* INTERVAL - should be 12 for yearly recurrence */
+ flag32 = 0xC;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* This would be 0 for the stuff we handle */
+ flag32 = 0x0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* MONTH_DAY */
+ {
+ ECalComponentDateTime dtstart;
+ e_cal_component_get_dtstart (comp, &dtstart);
+ flag32 = dtstart.value->day;
+ e_cal_component_free_datetime (&dtstart);
+ }
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ }
+
+ /* End Type followed by Occurence count */
+ if (!icaltime_is_null_time (rt->until)) {
+ flag32 = END_AFTER_DATE;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ flag32 = calculate_no_of_occurrences (comp, rt);
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ end_type = END_AFTER_DATE;
+ } else if (rt->count) {
+ flag32 = END_AFTER_N_OCCURRENCES;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ flag32 = rt->count;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ end_type = END_AFTER_N_OCCURRENCES;
+ } else {
+ flag32 = END_NEVER_END;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ flag32 = 0x0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ end_type = END_NEVER_END;
+ }
+
+ /* FirstDOW */
+ flag32 = get_mapi_weekstart (rt->week_start);
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* DeletedInstances */
+ flag32 = g_slist_length (exdate_list);
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+ if (flag32) {
+ GSList *l;
+ guint32 *sorted_list = g_new0(guint32, flag32);
+ /* FIXME: This should include modified dates */
+ for (i = 0, l = exdate_list; l; ++i, l = l->next) {
+ ECalComponentDateTime *dt = (ECalComponentDateTime *)(l->data);
+ dt->value->hour = dt->value->minute = dt->value->second = 0;
+ sorted_list[i] = convert_timet_to_recurrence_minutes (icaltime_as_timet_with_zone (*(dt->value), 0));
+ }
+
+ g_qsort_with_data (sorted_list, flag32, sizeof (guint32), compare_guint32, NULL);
+
+ for (i = 0; i < flag32; ++i)
+ ba = g_byte_array_append (ba, &(sorted_list[i]), sizeof (guint32));
+
+ g_free (sorted_list);
+ }
+
+ /* FIXME: Add support for modified instances */
+ /* ModifiedInstanceCount */
+ flag32 = 0x0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+ if (flag32) {
+ }
+
+ /* StartDate */
+ flag32 = compute_startdate (comp);
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* EndDate */
+ {
+ if (end_type == END_NEVER_END)
+ flag32 = 0x5AE980DF;
+ else if (end_type == END_AFTER_N_OCCURRENCES) {
+ ECalComponentDateTime dtstart;
+ gchar *rrule_str = icalrecurrencetype_as_string (rt);
+ time_t *array = g_new0 (time_t, rt->count);
+
+ e_cal_component_get_dtstart (comp, &dtstart);
+ dtstart.value->hour = dtstart.value->minute = dtstart.value->second = 0;
+
+ icalrecur_expand_recurrence (rrule_str, icaltime_as_timet_with_zone (*(dtstart.value), 0), rt->count, array);
+
+ flag32 = convert_timet_to_recurrence_minutes (array[(rt->count) - 1]);
+
+ g_free (array);
+ g_free (rrule_str);
+ e_cal_component_free_datetime (&dtstart);
+ } else if (end_type == END_AFTER_DATE) {
+ struct icaltimetype until;
+ memcpy (&until, &(rt->until), sizeof(struct icaltimetype));
+ until.hour = until.minute = until.second = 0;
+ flag32 = convert_timet_to_recurrence_minutes (icaltime_as_timet_with_zone (until, 0));
+ } else
+ flag32 = 0x0;
+ }
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* Reader Version 2 */
+ flag32 = READER_VERSION2;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* Writer Version 2 */
+ flag32 = WRITER_VERSION2;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* StartTimeOffset */
+ {
+ ECalComponentDateTime dtstart;
+ e_cal_component_get_dtstart (comp, &dtstart);
+ flag32 = (dtstart.value->hour * 60) + dtstart.value->minute;
+ e_cal_component_free_datetime (&dtstart);
+ }
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* EndTimeOffset */
+ {
+ ECalComponentDateTime dtend;
+ e_cal_component_get_dtend (comp, &dtend);
+ flag32 = (dtend.value->hour * 60) + dtend.value->minute;
+ e_cal_component_free_datetime (&dtend);
+ }
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* FIXME: Add support for modified instances */
+ /* ModifiedExceptionCount */
+ flag16 = 0x0;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* FIXME: Add the ExceptionInfo here */
+
+ /* Reserved Block 1 Size */
+ flag32 = 0x0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* FIXME: Add the ExtendedExceptionInfo here */
+
+ /* Reserved Block 2 Size */
+ flag32 = 0x0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+cleanup:
+ e_cal_component_free_exdate_list (exdate_list);
+ e_cal_component_free_recur_list (rrule_list);
+
+ g_print ("\n== ICAL to MAPI == The recurrence blob data is as follows:\n");
+ for (i = 0; i < ba->len; ++i)
+ g_print ("0x%02X ", ba->data[i]);
+ g_print("\n== End of stream ==\n");
+
+ return ba;
+}
+
Added: trunk/src/libexchangemapi/exchange-mapi-cal-recur-utils.h
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-cal-recur-utils.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EXCHANGE_MAPI_CAL_RECUR_UTILS_H
+#define EXCHANGE_MAPI_CAL_RECUR_UTILS_H
+
+#include <glib.h>
+
+#include "exchange-mapi-cal-utils.h"
+
+G_BEGIN_DECLS
+
+gboolean
+exchange_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp);
+
+GByteArray *
+exchange_mapi_cal_util_rrule_to_bin (ECalComponent *comp, GSList *modified_comps);
+
+G_END_DECLS
+
+#endif
+
Added: trunk/src/libexchangemapi/exchange-mapi-cal-tz-utils.c
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-cal-tz-utils.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,548 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "exchange-mapi-cal-tz-utils.h"
+
+#define d(x)
+
+#define MAPPING_SEPARATOR "~~~"
+
+static GStaticRecMutex mutex = G_STATIC_REC_MUTEX_INIT;
+
+static GHashTable *mapi_to_ical = NULL;
+static GHashTable *ical_to_mapi = NULL;
+
+const gchar *
+exchange_mapi_cal_tz_util_get_mapi_equivalent (const gchar *ical_tzid)
+{
+ const gchar *retval = NULL;
+
+ g_return_val_if_fail ((ical_tzid && *ical_tzid), NULL);
+
+ g_static_rec_mutex_lock(&mutex);
+ if (!exchange_mapi_cal_tz_util_populate()) {
+ g_static_rec_mutex_unlock(&mutex);
+ return NULL;
+ }
+
+ d(g_message("%s(%d): %s of '%s' ", __FILE__, __LINE__, __PRETTY_FUNCTION__, ical_tzid));
+
+ retval = g_hash_table_lookup (ical_to_mapi, ical_tzid);
+
+ g_static_rec_mutex_unlock(&mutex);
+
+ return retval;
+}
+
+const gchar *
+exchange_mapi_cal_tz_util_get_ical_equivalent (const gchar *mapi_tzid)
+{
+ const gchar *retval = NULL;
+
+ g_return_val_if_fail ((mapi_tzid && *mapi_tzid), NULL);
+
+ g_static_rec_mutex_lock(&mutex);
+ if (!exchange_mapi_cal_tz_util_populate()) {
+ g_static_rec_mutex_unlock(&mutex);
+ return NULL;
+ }
+
+ d(g_message("%s(%d): %s of '%s' ", __FILE__, __LINE__, __PRETTY_FUNCTION__, mapi_tzid));
+
+ retval = g_hash_table_lookup (mapi_to_ical, mapi_tzid);
+
+ g_static_rec_mutex_unlock(&mutex);
+
+ return retval;
+}
+
+void
+exchange_mapi_cal_tz_util_destroy ()
+{
+ g_static_rec_mutex_lock(&mutex);
+ if (!(mapi_to_ical && ical_to_mapi)) {
+ g_static_rec_mutex_unlock(&mutex);
+ return;
+ }
+
+ g_hash_table_destroy (mapi_to_ical);
+ g_hash_table_destroy (ical_to_mapi);
+
+ /* Reset all the values */
+ mapi_to_ical = NULL;
+ ical_to_mapi = NULL;
+
+ g_static_rec_mutex_unlock(&mutex);
+}
+
+static void
+file_contents_to_hashtable (const char *contents, GHashTable *table)
+{
+ gchar **array = NULL;
+ guint len = 0, i;
+
+ array = g_strsplit (contents, "\n", -1);
+ len = g_strv_length (array);
+
+ for (i=0; i < len-1; ++i) {
+ gchar **mapping = g_strsplit (array[i], MAPPING_SEPARATOR, -1);
+ if (g_strv_length (mapping) == 2)
+ g_hash_table_insert (table, g_strdup (mapping[0]), g_strdup (mapping[1]));
+ g_strfreev (mapping);
+ }
+
+ g_strfreev (array);
+}
+
+gboolean
+exchange_mapi_cal_tz_util_populate ()
+{
+ gchar *mtoi_fn = NULL, *itom_fn = NULL;
+ GMappedFile *mtoi_mf = NULL, *itom_mf = NULL;
+
+ g_static_rec_mutex_lock(&mutex);
+ if (mapi_to_ical && ical_to_mapi) {
+ g_static_rec_mutex_unlock(&mutex);
+ return TRUE;
+ }
+
+ mtoi_fn = g_build_filename (MAPI_DATADIR, "tz-mapi-to-ical", NULL);
+ itom_fn = g_build_filename (MAPI_DATADIR, "tz-ical-to-mapi", NULL);
+
+ mtoi_mf = g_mapped_file_new (mtoi_fn, FALSE, NULL);
+ itom_mf = g_mapped_file_new (itom_fn, FALSE, NULL);
+
+ g_free (mtoi_fn);
+ g_free (itom_fn);
+
+ if (!(mtoi_mf && itom_mf)) {
+ g_warning ("Could not map Exchange MAPI timezone files.");
+
+ if (mtoi_mf)
+ g_mapped_file_free (mtoi_mf);
+ if (itom_mf)
+ g_mapped_file_free (itom_mf);
+
+ g_static_rec_mutex_unlock(&mutex);
+ return FALSE;
+ }
+
+ mapi_to_ical = g_hash_table_new_full ((GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
+ file_contents_to_hashtable (g_mapped_file_get_contents (mtoi_mf), mapi_to_ical);
+
+ ical_to_mapi = g_hash_table_new_full ((GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
+ file_contents_to_hashtable (g_mapped_file_get_contents (itom_mf), ical_to_mapi);
+
+ if (!(g_hash_table_size (mapi_to_ical) && g_hash_table_size (ical_to_mapi))) {
+ g_warning ("Exchange MAPI timezone files are not valid.");
+
+ exchange_mapi_cal_tz_util_destroy ();
+
+ g_mapped_file_free (mtoi_mf);
+ g_mapped_file_free (itom_mf);
+
+ g_static_rec_mutex_unlock(&mutex);
+ return FALSE;
+ }
+
+ g_mapped_file_free (mtoi_mf);
+ g_mapped_file_free (itom_mf);
+
+ d(exchange_mapi_cal_tz_util_dump ());
+
+ g_static_rec_mutex_unlock(&mutex);
+
+ return TRUE;
+}
+
+static void
+exchange_mapi_cal_tz_util_dump_ical_tzs ()
+{
+ guint i;
+ icalarray *zones;
+ GList *l, *list_items = NULL;
+
+ /* Get the array of builtin timezones. */
+ zones = icaltimezone_get_builtin_timezones ();
+
+ g_message("%s(%d): %s: ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ for (i = 0; i < zones->num_elements; i++) {
+ icaltimezone *zone;
+ char *tzid = NULL;
+
+ zone = icalarray_element_at (zones, i);
+
+ tzid = icaltimezone_get_tzid (zone);
+
+ list_items = g_list_prepend (list_items, tzid);
+ }
+
+ list_items = g_list_sort (list_items, (GCompareFunc) g_ascii_strcasecmp);
+
+ /* Put the "UTC" entry at the top of the combo's list. */
+ list_items = g_list_prepend (list_items, "UTC");
+
+ for (l = list_items, i = 0; l != NULL; l = l->next, ++i)
+ g_print ("[%3d]\t%s\n", (i+1), (gchar *)(l->data));
+
+// icaltimezone_free_builtin_timezones ();
+
+ g_list_free (list_items);
+}
+
+void
+exchange_mapi_cal_tz_util_dump ()
+{
+ guint i;
+ GList *keys, *values, *l, *m;
+
+ g_static_rec_mutex_lock(&mutex);
+
+ exchange_mapi_cal_tz_util_dump_ical_tzs ();
+
+ if (!(mapi_to_ical && ical_to_mapi)) {
+ g_static_rec_mutex_unlock(&mutex);
+ return;
+ }
+
+ g_message("%s(%d): %s: ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+
+ g_message ("Dumping #table mapi_to_ical");
+ keys = g_hash_table_get_keys (mapi_to_ical);
+ values = g_hash_table_get_values (mapi_to_ical);
+ l = g_list_first (keys);
+ m = g_list_first (values);
+ for (i=0; l && m; ++i, l=l->next, m=m->next)
+ g_print ("[%3d]\t%s\t%s\t%s\n", (i+1), (gchar *)(l->data), MAPPING_SEPARATOR, (gchar *)(m->data));
+ g_message ("Dumping differences in #tables");
+ l = g_list_first (keys);
+ m = g_list_first (values);
+ for (i=0; l && m; ++i, l=l->next, m=m->next)
+ if (g_ascii_strcasecmp ((gchar *)(l->data), (gchar *) g_hash_table_lookup (ical_to_mapi, (m->data))))
+ g_print ("[%3d] Possible mis-match for %s\n", (i+1), (gchar *)(l->data));
+ g_list_free (keys);
+ g_list_free (values);
+
+ g_message ("Dumping #table ical_to_mapi");
+ keys = g_hash_table_get_keys (ical_to_mapi);
+ values = g_hash_table_get_values (ical_to_mapi);
+ l = g_list_first (keys);
+ m = g_list_first (values);
+ for (i=0; l && m; ++i, l=l->next, m=m->next)
+ g_print ("[%3d]\t%s\t%s\t%s\n", (i+1), (gchar *)(l->data), MAPPING_SEPARATOR, (gchar *)(m->data));
+ g_list_free (keys);
+ g_list_free (values);
+
+ g_static_rec_mutex_unlock(&mutex);
+}
+
+#if 0
+const WORD TZRULE_FLAG_RECUR_CURRENT_TZREG = 0x0001; // see dispidApptTZDefRecur
+const WORD TZRULE_FLAG_EFFECTIVE_TZREG = 0x0002;
+
+// Allocates return value with new.
+// clean up with delete[].
+TZDEFINITION* BinToTZDEFINITION(ULONG cbDef, LPBYTE lpbDef)
+{
+ if (!lpbDef) return NULL;
+
+ // Update this if parsing code is changed!
+ // this checks the size up to the flags member
+ if (cbDef < 2*sizeof(BYTE) + 2*sizeof(WORD)) return NULL;
+
+ TZDEFINITION tzDef;
+ TZRULE* lpRules = NULL;
+ LPBYTE lpPtr = lpbDef;
+ WORD cchKeyName = 0;
+ WCHAR* szKeyName = NULL;
+ WORD i = 0;
+
+ BYTE bMajorVersion = *((BYTE*)lpPtr);
+ lpPtr += sizeof(BYTE);
+ BYTE bMinorVersion = *((BYTE*)lpPtr);
+ lpPtr += sizeof(BYTE);
+
+ // We only understand TZ_BIN_VERSION_MAJOR
+ if (TZ_BIN_VERSION_MAJOR != bMajorVersion) return NULL;
+
+ // We only understand if >= TZ_BIN_VERSION_MINOR
+ if (TZ_BIN_VERSION_MINOR > bMinorVersion) return NULL;
+
+ WORD cbHeader = *((WORD*)lpPtr);
+ lpPtr += sizeof(WORD);
+
+ tzDef.wFlags = *((WORD*)lpPtr);
+ lpPtr += sizeof(WORD);
+
+ if (TZDEFINITION_FLAG_VALID_GUID & tzDef.wFlags)
+ {
+ if (lpbDef + cbDef - lpPtr < sizeof(GUID)) return NULL;
+ tzDef.guidTZID = *((GUID*)lpPtr);
+ lpPtr += sizeof(GUID);
+ }
+
+ if (TZDEFINITION_FLAG_VALID_KEYNAME & tzDef.wFlags)
+ {
+ if (lpbDef + cbDef - lpPtr < sizeof(WORD)) return NULL;
+ cchKeyName = *((WORD*)lpPtr);
+ lpPtr += sizeof(WORD);
+ if (cchKeyName)
+ {
+ if (lpbDef + cbDef - lpPtr < (BYTE)sizeof(WORD)*cchKeyName) return NULL;
+ szKeyName = (WCHAR*)lpPtr;
+ lpPtr += cchKeyName*sizeof(WORD);
+ }
+ }
+
+ if (lpbDef+ cbDef - lpPtr < sizeof(WORD)) return NULL;
+ tzDef.cRules = *((WORD*)lpPtr);
+ lpPtr += sizeof(WORD);
+
+ /* FIXME: parse rules */
+ if (tzDef.cRules) tzDef.cRules = 0;
+#if 0
+ if (tzDef.cRules)
+ {
+ lpRules = new TZRULE[tzDef.cRules];
+ if (!lpRules) return NULL;
+
+ LPBYTE lpNextRule = lpPtr;
+ BOOL bRuleOK = false;
+
+ for (i = 0;i < tzDef.cRules;i++)
+ {
+ bRuleOK = false;
+ lpPtr = lpNextRule;
+
+ if (lpbDef + cbDef - lpPtr <
+ 2*sizeof(BYTE) + 2*sizeof(WORD) + 3*sizeof(long) + 2*sizeof(SYSTEMTIME)) return NULL;
+ bRuleOK = true;
+ BYTE bRuleMajorVersion = *((BYTE*)lpPtr);
+ lpPtr += sizeof(BYTE);
+ BYTE bRuleMinorVersion = *((BYTE*)lpPtr);
+ lpPtr += sizeof(BYTE);
+
+ // We only understand TZ_BIN_VERSION_MAJOR
+ if (TZ_BIN_VERSION_MAJOR != bRuleMajorVersion) return NULL;
+
+ // We only understand if >= TZ_BIN_VERSION_MINOR
+ if (TZ_BIN_VERSION_MINOR > bRuleMinorVersion) return NULL;
+
+ WORD cbRule = *((WORD*)lpPtr);
+ lpPtr += sizeof(WORD);
+
+ lpNextRule = lpPtr + cbRule;
+
+ lpRules[i].wFlags = *((WORD*)lpPtr);
+ lpPtr += sizeof(WORD);
+
+ lpRules[i].stStart = *((SYSTEMTIME*)lpPtr);
+ lpPtr += sizeof(SYSTEMTIME);
+
+ lpRules[i].TZReg.lBias = *((long*)lpPtr);
+ lpPtr += sizeof(long);
+ lpRules[i].TZReg.lStandardBias = *((long*)lpPtr);
+ lpPtr += sizeof(long);
+ lpRules[i].TZReg.lDaylightBias = *((long*)lpPtr);
+ lpPtr += sizeof(long);
+
+ lpRules[i].TZReg.stStandardDate = *((SYSTEMTIME*)lpPtr);
+ lpPtr += sizeof(SYSTEMTIME);
+ lpRules[i].TZReg.stDaylightDate = *((SYSTEMTIME*)lpPtr);
+ lpPtr += sizeof(SYSTEMTIME);
+ }
+ if (!bRuleOK)
+ {
+ delete[] lpRules;
+ return NULL;
+ }
+ }
+#endif
+ // Now we've read everything - allocate a structure and copy it in
+ size_t cbTZDef = sizeof(TZDEFINITION) +
+ sizeof(WCHAR)*(cchKeyName+1) +
+ sizeof(TZRULE)*tzDef.cRules;
+
+ TZDEFINITION* ptzDef = (TZDEFINITION*) malloc (cbTZDef);
+
+ if (ptzDef)
+ {
+ // Copy main struct over
+ *ptzDef = tzDef;
+ lpPtr = (LPBYTE) ptzDef;
+ lpPtr += sizeof(TZDEFINITION);
+
+ if (szKeyName)
+ {
+ ptzDef->pwszKeyName = (WCHAR*)lpPtr;
+ memcpy(lpPtr,szKeyName,cchKeyName*sizeof(WCHAR));
+ ptzDef->pwszKeyName[cchKeyName] = 0;
+ lpPtr += (cchKeyName+1)*sizeof(WCHAR);
+ }
+
+ if (ptzDef -> cRules)
+ {
+ ptzDef -> rgRules = (TZRULE*)lpPtr;
+ for (i = 0;i < ptzDef -> cRules;i++)
+ {
+ ptzDef -> rgRules[i] = lpRules[i];
+ }
+ }
+ }
+// delete[] lpRules;
+
+ free (ptzDef);
+ ptzDef = NULL;
+
+ return ptzDef;
+}
+#endif
+
+#define TZDEFINITION_FLAG_VALID_GUID 0x0001 // the guid is valid
+#define TZDEFINITION_FLAG_VALID_KEYNAME 0x0002 // the keyname is valid
+#define TZ_MAX_RULES 1024
+#define TZ_BIN_VERSION_MAJOR 0x02
+#define TZ_BIN_VERSION_MINOR 0x01
+
+void
+exchange_mapi_cal_util_mapi_tz_to_bin (const char *mapi_tzid, struct SBinary *sb)
+{
+ GByteArray *ba;
+ guint8 flag8;
+ guint16 flag16;
+ gunichar2 *buf;
+ glong items_written;
+ guint32 i;
+
+ ba = g_byte_array_new ();
+
+ /* UTF-8 length of the keyname */
+ flag16 = g_utf8_strlen (mapi_tzid, -1);
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+ /* Keyname */
+ buf = g_utf8_to_utf16 (mapi_tzid, flag16, NULL, &items_written, NULL);
+ ba = g_byte_array_append (ba, buf, (sizeof (gunichar2) * items_written));
+ g_free (buf);
+
+ /* number of rules *//* FIXME: Need to support rules */
+ flag16 = 0x0000;
+ ba = g_byte_array_append (ba, &flag16, sizeof (guint16));
+
+ /* wFlags: we know only keyname based names */
+ flag16 = TZDEFINITION_FLAG_VALID_KEYNAME;
+ ba = g_byte_array_prepend (ba, &flag16, sizeof (guint16));
+
+ /* Length in bytes until rules info */
+ flag16 = (guint16) (ba->len);
+ ba = g_byte_array_prepend (ba, &flag16, sizeof (guint16));
+
+ /* Minor version */
+ flag8 = TZ_BIN_VERSION_MINOR;
+ ba = g_byte_array_prepend (ba, &flag8, sizeof (guint8));
+
+ /* Major version */
+ flag8 = TZ_BIN_VERSION_MAJOR;
+ ba = g_byte_array_prepend (ba, &flag8, sizeof (guint8));
+
+ /* Rules may now be appended here */
+
+ sb->lpb = ba->data;
+ sb->cb = ba->len;
+
+ d(g_message ("New timezone stream.. Length: %d bytes.. Hex-data follows:", ba->len));
+ d(for (i = 0; i < ba->len; i++)
+ g_print("0x%.2X ", ba->data[i]));
+
+ g_byte_array_free (ba, FALSE);
+}
+
+gchar *
+exchange_mapi_cal_util_bin_to_mapi_tz (GByteArray *ba)
+{
+ guint8 flag8;
+ guint16 flag16, cbHeader = 0;
+ guint8 *ptr = ba->data;
+// guint len = ba->len;
+ gchar *buf = NULL;
+
+ d(g_message ("New timezone stream.. Length: %d bytes.. Info follows:", ba->len));
+
+ /* Major version */
+ flag8 = *((guint8 *)ptr);
+ ptr += sizeof (guint8);
+ d(g_print ("Major version: %d\n", flag8));
+ if (TZ_BIN_VERSION_MAJOR != flag8)
+ return NULL;
+
+ /* Minor version */
+ flag8 = *((guint8 *)ptr);
+ ptr += sizeof (guint8);
+ d(g_print ("Minor version: %d\n", flag8));
+ if (TZ_BIN_VERSION_MINOR > flag8)
+ return NULL;
+
+ /* Length in bytes until rules info */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ d(g_print ("Length in bytes until rules: %d\n", flag16));
+ cbHeader = flag16;
+
+ /* wFlags: we don't yet understand GUID based names */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ d(g_print ("wFlags: %d\n", flag16));
+ cbHeader -= sizeof (guint16);
+ if (TZDEFINITION_FLAG_VALID_KEYNAME != flag16)
+ return NULL;
+
+ /* UTF-8 length of the keyname */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ d(g_print ("UTF8 length of keyname: %d\n", flag16));
+ cbHeader -= sizeof (guint16);
+
+ /* number of rules is at the end of the header.. we'll parse and use later */
+ cbHeader -= sizeof (guint16);
+
+ /* Keyname */
+ buf = g_utf16_to_utf8 ((const gunichar2 *)ptr, cbHeader/sizeof (gunichar2), NULL, NULL, NULL);
+ ptr += cbHeader;
+ d(g_print ("Keyname: %s\n", buf));
+
+ /* number of rules */
+ flag16 = *((guint16 *)ptr);
+ ptr += sizeof (guint16);
+ d(g_print ("Number of rules: %d\n", flag16));
+
+ /* FIXME: Need to support rules */
+
+ return buf;
+}
Added: trunk/src/libexchangemapi/exchange-mapi-cal-tz-utils.h
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-cal-tz-utils.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,116 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EXCHANGE_MAPI_CAL_TZ_UTILS_H
+#define EXCHANGE_MAPI_CAL_TZ_UTILS_H
+
+#include <glib.h>
+
+#include "exchange-mapi-cal-utils.h"
+
+G_BEGIN_DECLS
+
+const gchar *
+exchange_mapi_cal_tz_util_get_mapi_equivalent (const gchar *ical_tzid);
+
+const gchar *
+exchange_mapi_cal_tz_util_get_ical_equivalent (const gchar *mapi_tzid);
+
+gboolean
+exchange_mapi_cal_tz_util_populate (void);
+
+void
+exchange_mapi_cal_tz_util_destroy (void);
+
+void
+exchange_mapi_cal_tz_util_dump (void);
+
+void
+exchange_mapi_cal_util_mapi_tz_to_bin (const char *mapi_tzid, struct SBinary *sb);
+
+gchar *
+exchange_mapi_cal_util_bin_to_mapi_tz (GByteArray *ba);
+
+G_END_DECLS
+
+#if 0
+typedef int16_t WORD;
+typedef int8_t BYTE;
+typedef uint32_t GUID;
+typedef uint64_t ULONG;
+typedef time_t SYSTEMTIME;
+typedef uint8_t* LPBYTE;
+typedef char* LPWSTR;
+typedef char WCHAR;
+
+// TZREG
+// =====================
+// This is an individual description that defines when a daylight
+// saving shift, and the return to standard time occurs, and how
+// far the shift is. This is basically the same as
+// TIME_ZONE_INFORMATION documented in MSDN, except that the strings
+// describing the names "daylight" and "standard" time are omitted.
+//
+typedef struct RenTimeZone
+{
+ long lBias; // offset from GMT
+ long lStandardBias; // offset from bias during standard time
+ long lDaylightBias; // offset from bias during daylight time
+ SYSTEMTIME stStandardDate; // time to switch to standard time
+ SYSTEMTIME stDaylightDate; // time to switch to daylight time
+} TZREG;
+
+// TZRULE
+// =====================
+// This structure represents both a description when a daylight.
+// saving shift occurs, and in addition, the year in which that
+// timezone rule came into effect.
+//
+typedef struct
+{
+ WORD wFlags; // indicates which rule matches legacy recur
+ SYSTEMTIME stStart; // indicates when the rule starts
+ TZREG TZReg; // the timezone info
+} TZRULE;
+
+// TZDEFINITION
+// =====================
+// This represents an entire timezone including all historical, current
+// and future timezone shift rules for daylight saving time, etc. It's
+// identified by a unique GUID.
+//
+typedef struct
+{
+ WORD wFlags; // indicates which fields are valid
+ GUID guidTZID; // guid uniquely identifying this timezone
+ LPWSTR pwszKeyName; // the name of the key for this timezone
+ WORD cRules; // the number of timezone rules for this definition
+ TZRULE* rgRules; // an array of rules describing when shifts occur
+} TZDEFINITION;
+
+// Allocates return value with new.
+// clean up with delete[].
+TZDEFINITION* BinToTZDEFINITION(ULONG cbDef, LPBYTE lpbDef);
+#endif
+
+#endif
Added: trunk/src/libexchangemapi/exchange-mapi-cal-utils.c
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-cal-utils.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,2115 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include <glib/gstdio.h>
+#include <fcntl.h>
+#include <libecal/e-cal-util.h>
+#include "exchange-mapi-cal-utils.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#define d(x)
+
+static void appt_build_name_id (struct mapi_nameid *nameid);
+static void task_build_name_id (struct mapi_nameid *nameid);
+static void note_build_name_id (struct mapi_nameid *nameid);
+
+static icalparameter_role
+get_role_from_type (OlMailRecipientType type)
+{
+ switch (type) {
+ case olCC : return ICAL_ROLE_OPTPARTICIPANT;
+ case olOriginator :
+ case olTo :
+ case olBCC :
+ default : return ICAL_ROLE_REQPARTICIPANT;
+ }
+}
+
+static OlMailRecipientType
+get_type_from_role (icalparameter_role role)
+{
+ switch (role) {
+ case ICAL_ROLE_OPTPARTICIPANT : return olCC;
+ case ICAL_ROLE_CHAIR :
+ case ICAL_ROLE_REQPARTICIPANT :
+ case ICAL_ROLE_NONPARTICIPANT :
+ default : return olTo;
+ }
+}
+
+static icalparameter_partstat
+get_partstat_from_trackstatus (uint32_t trackstatus)
+{
+ switch (trackstatus) {
+ case olResponseOrganized :
+ case olResponseAccepted : return ICAL_PARTSTAT_ACCEPTED;
+ case olResponseTentative : return ICAL_PARTSTAT_TENTATIVE;
+ case olResponseDeclined : return ICAL_PARTSTAT_DECLINED;
+ default : return ICAL_PARTSTAT_NEEDSACTION;
+ }
+}
+
+static uint32_t
+get_trackstatus_from_partstat (icalparameter_partstat partstat)
+{
+ switch (partstat) {
+ case ICAL_PARTSTAT_ACCEPTED : return olResponseAccepted;
+ case ICAL_PARTSTAT_TENTATIVE : return olResponseTentative;
+ case ICAL_PARTSTAT_DECLINED : return olResponseDeclined;
+ default : return olResponseNone;
+ }
+}
+
+static icalproperty_transp
+get_transp_from_prop (uint32_t prop)
+{
+ /* FIXME: is this mapping correct ? */
+ switch (prop) {
+ case olFree :
+ case olTentative : return ICAL_TRANSP_TRANSPARENT;
+ case olBusy :
+ case olOutOfOffice :
+ default : return ICAL_TRANSP_OPAQUE;
+ }
+}
+
+static uint32_t
+get_prop_from_transp (icalproperty_transp transp)
+{
+ /* FIXME: is this mapping correct ? */
+ switch (transp) {
+ case ICAL_TRANSP_TRANSPARENT :
+ case ICAL_TRANSP_TRANSPARENTNOCONFLICT : return olFree;
+ case ICAL_TRANSP_OPAQUE :
+ case ICAL_TRANSP_OPAQUENOCONFLICT :
+ default : return olBusy;
+ }
+}
+
+static icalproperty_status
+get_taskstatus_from_prop (uint32_t prop)
+{
+ /* FIXME: is this mapping correct ? */
+ switch (prop) {
+ case olTaskComplete : return ICAL_STATUS_COMPLETED;
+ case olTaskWaiting :
+ case olTaskInProgress : return ICAL_STATUS_INPROCESS;
+ case olTaskDeferred : return ICAL_STATUS_CANCELLED;
+ case olTaskNotStarted :
+ default : return ICAL_STATUS_NEEDSACTION;
+ }
+}
+
+static uint32_t
+get_prop_from_taskstatus (icalproperty_status status)
+{
+ /* FIXME: is this mapping correct ? */
+ switch (status) {
+ case ICAL_STATUS_INPROCESS : return olTaskInProgress;
+ case ICAL_STATUS_COMPLETED : return olTaskComplete;
+ case ICAL_STATUS_CANCELLED : return olTaskDeferred;
+ default : return olTaskNotStarted;
+ }
+}
+
+static icalproperty_class
+get_class_from_prop (uint32_t prop)
+{
+ /* FIXME: is this mapping correct ? */
+ switch (prop) {
+ case olPersonal :
+ case olPrivate : return ICAL_CLASS_PRIVATE;
+ case olConfidential : return ICAL_CLASS_CONFIDENTIAL;
+ case olNormal :
+ default : return ICAL_CLASS_PUBLIC;
+ }
+}
+
+static uint32_t
+get_prop_from_class (icalproperty_class class)
+{
+ /* FIXME: is this mapping correct ? */
+ switch (class) {
+ case ICAL_CLASS_PRIVATE : return olPrivate;
+ case ICAL_CLASS_CONFIDENTIAL : return olConfidential;
+ default : return olNormal;
+ }
+}
+
+static int
+get_priority_from_prop (uint32_t prop)
+{
+ switch (prop) {
+ case PRIORITY_LOW : return 7;
+ case PRIORITY_HIGH : return 1;
+ case PRIORITY_NORMAL :
+ default : return 5;
+ }
+}
+
+static uint32_t
+get_prio_prop_from_priority (int priority)
+{
+ if (priority > 0 && priority <= 4)
+ return PRIORITY_HIGH;
+ else if (priority > 5 && priority <= 9)
+ return PRIORITY_LOW;
+ else
+ return PRIORITY_NORMAL;
+}
+
+static uint32_t
+get_imp_prop_from_priority (int priority)
+{
+ if (priority > 0 && priority <= 4)
+ return IMPORTANCE_HIGH;
+ else if (priority > 5 && priority <= 9)
+ return IMPORTANCE_LOW;
+ else
+ return IMPORTANCE_NORMAL;
+}
+
+void
+exchange_mapi_cal_util_fetch_attachments (ECalComponent *comp, GSList **attach_list, const char *local_store_uri)
+{
+ GSList *comp_attach_list = NULL, *new_attach_list = NULL;
+ GSList *l;
+ const char *uid;
+
+ e_cal_component_get_attachment_list (comp, &comp_attach_list);
+ e_cal_component_get_uid (comp, &uid);
+
+ for (l = comp_attach_list; l ; l = l->next) {
+ gchar *sfname_uri = (gchar *) l->data;
+ gchar *sfname = NULL, *filename = NULL;
+ GMappedFile *mapped_file;
+ GError *error = NULL;
+
+ sfname = g_filename_from_uri (sfname_uri, NULL, NULL);
+ mapped_file = g_mapped_file_new (sfname, FALSE, &error);
+ filename = g_path_get_basename (sfname);
+
+ if (mapped_file && g_str_has_prefix (filename, uid)) {
+ ExchangeMAPIAttachment *attach_item;
+ ExchangeMAPIStream *stream;
+ gchar *attach = g_mapped_file_get_contents (mapped_file);
+ guint filelength = g_mapped_file_get_length (mapped_file);
+ const gchar *split_name = (filename + strlen (uid) + strlen ("-"));
+ uint32_t flag;
+
+ new_attach_list = g_slist_append (new_attach_list, g_strdup (sfname_uri));
+
+ attach_item = g_new0 (ExchangeMAPIAttachment, 1);
+
+ attach_item->cValues = 4;
+ attach_item->lpProps = g_new0 (struct SPropValue, 4);
+
+ flag = ATTACH_BY_VALUE;
+ set_SPropValue_proptag(&(attach_item->lpProps[0]), PR_ATTACH_METHOD, (const void *) (&flag));
+
+ /* MSDN Documentation: When the supplied offset is -1 (0xFFFFFFFF), the
+ * attachment is not rendered using the PR_RENDERING_POSITION property.
+ * All values other than -1 indicate the position within PR_BODY at which
+ * the attachment is to be rendered.
+ */
+ flag = 0xFFFFFFFF;
+ set_SPropValue_proptag(&(attach_item->lpProps[1]), PR_RENDERING_POSITION, (const void *) (&flag));
+
+ set_SPropValue_proptag(&(attach_item->lpProps[2]), PR_ATTACH_FILENAME, (const void *) g_strdup(split_name));
+ set_SPropValue_proptag(&(attach_item->lpProps[3]), PR_ATTACH_LONG_FILENAME, (const void *) g_strdup(split_name));
+
+ stream = g_new0 (ExchangeMAPIStream, 1);
+ stream->proptag = PR_ATTACH_DATA_BIN;
+ stream->value = g_byte_array_sized_new (filelength);
+ stream->value = g_byte_array_append (stream->value, attach, filelength);
+ attach_item->streams = g_slist_append (attach_item->streams, stream);
+
+ *attach_list = g_slist_append (*attach_list, attach_item);
+
+ g_mapped_file_free (mapped_file);
+ } else {
+ g_debug ("Could not map %s: %s \n", sfname_uri, error->message);
+ g_error_free (error);
+ }
+
+ g_free (filename);
+ }
+
+ e_cal_component_set_attachment_list (comp, new_attach_list);
+
+ for (l = new_attach_list; l != NULL; l = l->next)
+ g_free (l->data);
+ g_slist_free (new_attach_list);
+}
+
+#define RECIP_SENDABLE 0x1
+#define RECIP_ORGANIZER 0x2
+
+void
+exchange_mapi_cal_util_fetch_organizer (ECalComponent *comp, GSList **recip_list)
+{
+ icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
+ icalproperty *org_prop = NULL;
+ const gchar *org = NULL;
+
+ org_prop = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY);
+ org = icalproperty_get_organizer (org_prop);
+ if (org && *org) {
+ ExchangeMAPIRecipient *recipient;
+ uint32_t val = 0;
+ const char *str = NULL;
+ icalparameter *param;
+
+ recipient = g_new0 (ExchangeMAPIRecipient, 1);
+
+ if (!g_ascii_strncasecmp (org, "mailto:", 7))
+ recipient->email_id = (org) + 7;
+ else
+ recipient->email_id = (org);
+
+ /* Required properties - set them always */
+ recipient->in.req_lpProps = g_new0 (struct SPropValue, 5);
+ recipient->in.req_cValues = 5;
+
+ val = 0;
+ set_SPropValue_proptag (&(recipient->in.req_lpProps[0]), PR_SEND_INTERNET_ENCODING, (const void *)&val);
+
+ val = RECIP_SENDABLE | RECIP_ORGANIZER;
+ set_SPropValue_proptag (&(recipient->in.req_lpProps[1]), PR_RECIPIENTS_FLAGS, (const void *)&val);
+
+ val = olResponseNone;
+ set_SPropValue_proptag (&(recipient->in.req_lpProps[2]), PR_RECIPIENT_TRACKSTATUS, (const void *)&val);
+
+ val = olTo;
+ set_SPropValue_proptag (&(recipient->in.req_lpProps[3]), PR_RECIPIENT_TYPE, (const void *) &val);
+
+ param = icalproperty_get_first_parameter (org_prop, ICAL_CN_PARAMETER);
+ str = icalparameter_get_cn (param);
+ if (!(str && *str))
+ str = "";
+ set_SPropValue_proptag (&(recipient->in.req_lpProps[4]), PR_RECIPIENT_DISPLAY_NAME, (const void *)(str));
+
+ /* External recipient properties - set them only when the recipient is unresolved */
+ recipient->in.ext_lpProps = g_new0 (struct SPropValue, 5);
+ recipient->in.ext_cValues = 5;
+
+ val = DT_MAILUSER;
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[0]), PR_DISPLAY_TYPE, (const void *)&val);
+ val = MAPI_MAILUSER;
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[1]), PR_OBJECT_TYPE, (const void *)&val);
+ str = "SMTP";
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[2]), PR_ADDRTYPE, (const void *)(str));
+ str = recipient->email_id;
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[3]), PR_SMTP_ADDRESS, (const void *)(str));
+
+ param = icalproperty_get_first_parameter (org_prop, ICAL_CN_PARAMETER);
+ str = icalparameter_get_cn (param);
+ if (!(str && *str))
+ str = "";
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[4]), PR_DISPLAY_NAME, (const void *)(str));
+
+ *recip_list = g_slist_append (*recip_list, recipient);
+ }
+}
+
+void
+exchange_mapi_cal_util_fetch_recipients (ECalComponent *comp, GSList **recip_list)
+{
+ icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
+ icalproperty *org_prop = NULL, *att_prop = NULL;
+ const gchar *org = NULL;
+
+ org_prop = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY);
+ org = icalproperty_get_organizer (org_prop);
+ if (!org)
+ org = "";
+
+ att_prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+ while (att_prop) {
+ ExchangeMAPIRecipient *recipient;
+ uint32_t val = 0;
+ const char *str = NULL;
+ icalparameter *param;
+
+ recipient = g_new0 (ExchangeMAPIRecipient, 1);
+
+ str = icalproperty_get_attendee (att_prop);
+ if (!g_ascii_strncasecmp (str, "mailto:", 7))
+ recipient->email_id = (str) + 7;
+ else
+ recipient->email_id = (str);
+
+ /* Required properties - set them always */
+ recipient->in.req_lpProps = g_new0 (struct SPropValue, 5);
+ recipient->in.req_cValues = 5;
+
+ val = 0;
+ set_SPropValue_proptag (&(recipient->in.req_lpProps[0]), PR_SEND_INTERNET_ENCODING, (const void *)&val);
+
+ val = RECIP_SENDABLE | (!g_ascii_strcasecmp(str, org) ? RECIP_ORGANIZER : 0);
+ set_SPropValue_proptag (&(recipient->in.req_lpProps[1]), PR_RECIPIENTS_FLAGS, (const void *)&val);
+
+ param = icalproperty_get_first_parameter (att_prop, ICAL_PARTSTAT_PARAMETER);
+ val = get_trackstatus_from_partstat (icalparameter_get_partstat(param));
+ set_SPropValue_proptag (&(recipient->in.req_lpProps[2]), PR_RECIPIENT_TRACKSTATUS, (const void *)&val);
+
+ param = icalproperty_get_first_parameter (att_prop, ICAL_ROLE_PARAMETER);
+ val = get_type_from_role (icalparameter_get_role(param));
+ set_SPropValue_proptag (&(recipient->in.req_lpProps[3]), PR_RECIPIENT_TYPE, (const void *) &val);
+
+ param = icalproperty_get_first_parameter (att_prop, ICAL_CN_PARAMETER);
+ str = icalparameter_get_cn (param);
+ if (!(str && *str))
+ str = "";
+ set_SPropValue_proptag (&(recipient->in.req_lpProps[4]), PR_RECIPIENT_DISPLAY_NAME, (const void *)(str));
+
+ /* External recipient properties - set them only when the recipient is unresolved */
+ recipient->in.ext_lpProps = g_new0 (struct SPropValue, 5);
+ recipient->in.ext_cValues = 5;
+
+ val = DT_MAILUSER;
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[0]), PR_DISPLAY_TYPE, (const void *)&val);
+ val = MAPI_MAILUSER;
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[1]), PR_OBJECT_TYPE, (const void *)&val);
+ str = "SMTP";
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[2]), PR_ADDRTYPE, (const void *)(str));
+ str = recipient->email_id;
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[3]), PR_SMTP_ADDRESS, (const void *)(str));
+
+ param = icalproperty_get_first_parameter (att_prop, ICAL_CN_PARAMETER);
+ str = icalparameter_get_cn (param);
+ if (!(str && *str))
+ str = "";
+ set_SPropValue_proptag (&(recipient->in.ext_lpProps[4]), PR_DISPLAY_NAME, (const void *)(str));
+
+ *recip_list = g_slist_append (*recip_list, recipient);
+
+ att_prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+ }
+}
+
+static void
+set_attachments_to_cal_component (ECalComponent *comp, GSList *attach_list, const char *local_store_uri)
+{
+ GSList *comp_attach_list = NULL, *l;
+ const char *uid;
+
+ g_return_if_fail (comp != NULL);
+
+ e_cal_component_get_uid (comp, &uid);
+ for (l = attach_list; l ; l = l->next) {
+ ExchangeMAPIAttachment *attach_item = (ExchangeMAPIAttachment *) (l->data);
+ ExchangeMAPIStream *stream;
+ gchar *attach_file_url, *filename;
+ const char *str, *attach;
+ guint len;
+ int fd = -1;
+
+ stream = exchange_mapi_util_find_stream (attach_item->streams, PR_ATTACH_DATA_BIN);
+ if (!stream)
+ continue;
+
+ attach = (const char *)stream->value->data;
+ len = stream->value->len;
+
+ str = (const char *) exchange_mapi_util_find_SPropVal_array_propval(attach_item->lpProps, PR_ATTACH_LONG_FILENAME);
+ if (!(str && *str))
+ str = (const char *) exchange_mapi_util_find_SPropVal_array_propval(attach_item->lpProps, PR_ATTACH_FILENAME);
+ attach_file_url = g_strconcat (local_store_uri, G_DIR_SEPARATOR_S, uid, "-", str, NULL);
+ filename = g_filename_from_uri (attach_file_url, NULL, NULL);
+
+ fd = g_open (filename, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600);
+ if (fd == -1) {
+ /* skip gracefully */
+ g_debug ("Could not open %s for writing \n", filename);
+ } else if (len && write (fd, attach, len) == -1) {
+ /* skip gracefully */
+ g_debug ("Attachment write failed \n");
+ }
+ if (fd != -1) {
+ close (fd);
+ comp_attach_list = g_slist_append (comp_attach_list, g_strdup (attach_file_url));
+ }
+
+ g_free (filename);
+ g_free (attach_file_url);
+ }
+
+ e_cal_component_set_attachment_list (comp, comp_attach_list);
+}
+
+static void
+ical_attendees_from_props (icalcomponent *ical_comp, GSList *recipients, gboolean rsvp)
+{
+ GSList *l;
+ for (l=recipients; l; l=l->next) {
+ ExchangeMAPIRecipient *recip = (ExchangeMAPIRecipient *)(l->data);
+ icalproperty *prop = NULL;
+ icalparameter *param;
+ gchar *val;
+ const uint32_t *ui32;
+ const char *str;
+ const uint32_t *flags;
+
+ if (recip->email_id)
+ val = g_strdup_printf ("MAILTO:%s", recip->email_id);
+ else
+ continue;
+
+ flags = (const uint32_t *) get_SPropValue(recip->out.all_lpProps, PR_RECIPIENTS_FLAGS);
+
+ if (flags && (*flags & RECIP_ORGANIZER)) {
+ prop = icalproperty_new_organizer (val);
+
+ /* CN */
+ str = (const char *) exchange_mapi_util_find_SPropVal_array_propval(recip->out.all_lpProps, PR_RECIPIENT_DISPLAY_NAME);
+ if (!str)
+ str = (const char *) exchange_mapi_util_find_SPropVal_array_propval(recip->out.all_lpProps, PR_DISPLAY_NAME);
+ if (str) {
+ param = icalparameter_new_cn (str);
+ icalproperty_add_parameter (prop, param);
+ }
+ } else {
+ prop = icalproperty_new_attendee (val);
+
+ /* CN */
+ str = (const char *) exchange_mapi_util_find_SPropVal_array_propval(recip->out.all_lpProps, PR_RECIPIENT_DISPLAY_NAME);
+ if (!str)
+ str = (const char *) exchange_mapi_util_find_SPropVal_array_propval(recip->out.all_lpProps, PR_DISPLAY_NAME);
+ if (str) {
+ param = icalparameter_new_cn (str);
+ icalproperty_add_parameter (prop, param);
+ }
+ /* RSVP */
+ param = icalparameter_new_rsvp (rsvp ? ICAL_RSVP_TRUE : ICAL_RSVP_FALSE);
+ icalproperty_add_parameter (prop, param);
+ /* PARTSTAT */
+ ui32 = (const uint32_t *) get_SPropValue(recip->out.all_lpProps, PR_RECIPIENT_TRACKSTATUS);
+ if (ui32) {
+ param = icalparameter_new_partstat (get_partstat_from_trackstatus (*ui32));
+ icalproperty_add_parameter (prop, param);
+ }
+ /* ROLE */
+ ui32 = (const uint32_t *) get_SPropValue(recip->out.all_lpProps, PR_RECIPIENT_TYPE);
+ if (ui32) {
+ param = icalparameter_new_role (get_role_from_type (*ui32));
+ icalproperty_add_parameter (prop, param);
+ }
+#if 0
+ /* CALENDAR USER TYPE */
+ param = icalparameter_new_cutype ();
+ icalproperty_add_parameter (prop, param);
+#endif
+ }
+
+ if (prop)
+ icalcomponent_add_property (ical_comp, prop);
+
+ g_free (val);
+ }
+}
+
+static const uint8_t GID_START_SEQ[] = {
+ 0x04, 0x00, 0x00, 0x00, 0x82, 0x00, 0xe0, 0x00,
+ 0x74, 0xc5, 0xb7, 0x10, 0x1a, 0x82, 0xe0, 0x08
+};
+
+void
+exchange_mapi_cal_util_generate_globalobjectid (gboolean is_clean, const char *uid, struct SBinary *sb)
+{
+ GByteArray *ba;
+ guint32 flag32;
+ guchar *buf = NULL;
+ gsize len;
+ d(guint32 i);
+
+ ba = g_byte_array_new ();
+
+ ba = g_byte_array_append (ba, GID_START_SEQ, (sizeof (GID_START_SEQ) / sizeof (GID_START_SEQ[0])));
+
+ /* FIXME for exceptions */
+ if (is_clean || TRUE) {
+ flag32 = 0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+ }
+
+ /* creation time - may be all 0's */
+ flag32 = 0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+ flag32 = 0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* RESERVED - should be all 0's */
+ flag32 = 0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+ flag32 = 0;
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+
+ /* FIXME: cleanup the UID first */
+
+ /* We put Evolution's UID in base64 here */
+ buf = g_base64_decode (uid, &len);
+ if (len % 2 != 0)
+ --len;
+ flag32 = len;
+
+ /* Size in bytes of the following data */
+ ba = g_byte_array_append (ba, &flag32, sizeof (guint32));
+ /* Data */
+ ba = g_byte_array_append (ba, buf, flag32);
+ g_free (buf);
+
+ sb->lpb = ba->data;
+ sb->cb = ba->len;
+
+ d(g_message ("New GlobalObjectId.. Length: %d bytes.. Hex-data follows:", ba->len));
+ d(for (i = 0; i < ba->len; i++)
+ g_print("0x%02X ", ba->data[i]));
+
+ g_byte_array_free (ba, FALSE);
+}
+
+static gchar *
+id_to_string (GByteArray *ba)
+{
+ guint8 *ptr;
+ guint len;
+ gchar *buf = NULL;
+ guint32 flag32, i, j;
+
+ g_return_val_if_fail (ba != NULL, NULL);
+ /* MSDN docs: the globalID must have an even number of bytes */
+ if ((ba->len)%2 != 0)
+ return NULL;
+
+ ptr = ba->data;
+ len = ba->len;
+
+ /* starting seq - len = 16 bytes */
+ for (i = 0, j = 0;(i < len) && (j < sizeof (GID_START_SEQ)); ++i, ++ptr, ++j)
+ if (*ptr != GID_START_SEQ[j])
+ return NULL;
+
+ /* FIXME: for exceptions - len = 4 bytes */
+ flag32 = *((guint32 *)ptr);
+ i += sizeof (guint32);
+ if (!(i < len) || flag32 != 0)
+ return NULL;
+ ptr += sizeof (guint32);
+
+ /* Creation time - len = 8 bytes - skip it */
+ flag32 = *((guint32 *)ptr);
+ i += sizeof (guint32);
+ if (!(i < len))
+ return NULL;
+ ptr += sizeof (guint32);
+
+ flag32 = *((guint32 *)ptr);
+ i += sizeof (guint32);
+ if (!(i < len))
+ return NULL;
+ ptr += sizeof (guint32);
+
+ /* Reserved bytes - len = 8 bytes */
+ flag32 = *((guint32 *)ptr);
+ i += sizeof (guint32);
+ if (!(i < len) || flag32 != 0)
+ return NULL;
+ ptr += sizeof (guint32);
+
+ flag32 = *((guint32 *)ptr);
+ i += sizeof (guint32);
+ if (!(i < len) || flag32 != 0)
+ return NULL;
+ ptr += sizeof (guint32);
+
+ /* This is the real data */
+ flag32 = *((guint32 *)ptr);
+ i += sizeof (guint32);
+ if (!(i < len) || flag32 != (len - i))
+ return NULL;
+ ptr += sizeof (guint32);
+
+ buf = g_base64_encode (ptr, flag32);
+
+ return buf;
+}
+
+ECalComponent *
+exchange_mapi_cal_util_mapi_props_to_comp (icalcomponent_kind kind, const gchar *mid, struct mapi_SPropValue_array *properties,
+ GSList *streams, GSList *recipients, GSList *attachments,
+ const char *local_store_uri, const icaltimezone *default_zone)
+{
+ ECalComponent *comp = NULL;
+ struct timeval t;
+ ExchangeMAPIStream *body_stream;
+ const gchar *subject = NULL, *body = NULL;
+ const uint32_t *ui32;
+ const bool *b;
+ icalcomponent *ical_comp;
+ icalproperty *prop = NULL;
+ icalparameter *param = NULL;
+ const icaltimezone *utc_zone;
+
+ switch (kind) {
+ case ICAL_VEVENT_COMPONENT:
+ case ICAL_VTODO_COMPONENT:
+ case ICAL_VJOURNAL_COMPONENT:
+ comp = e_cal_component_new ();
+ ical_comp = icalcomponent_new (kind);
+ e_cal_component_set_icalcomponent (comp, ical_comp);
+ icalcomponent_set_uid (ical_comp, mid);
+ e_cal_component_set_uid (comp, mid);
+ break;
+ default:
+ return NULL;
+ }
+
+ utc_zone = icaltimezone_get_utc_timezone ();
+
+ subject = (const gchar *)exchange_mapi_util_find_array_propval(properties, PR_SUBJECT);
+ if (!subject)
+ subject = (const gchar *)exchange_mapi_util_find_array_propval(properties, PR_NORMALIZED_SUBJECT);
+ if (!subject)
+ subject = (const gchar *)exchange_mapi_util_find_array_propval(properties, PR_CONVERSATION_TOPIC);
+ if (!subject)
+ subject = "";
+
+ body = (const gchar *)exchange_mapi_util_find_array_propval(properties, PR_BODY);
+ if (!body) {
+ body_stream = exchange_mapi_util_find_stream (streams, PR_HTML);
+ body = body_stream ? (const gchar *) body_stream->value->data : "";
+ }
+
+ /* set dtstamp - in UTC */
+ if (get_mapi_SPropValue_array_date_timeval (&t, properties, PR_CREATION_TIME) == MAPI_E_SUCCESS)
+ icalcomponent_set_dtstamp (ical_comp, icaltime_from_timet_with_zone (t.tv_sec, 0, utc_zone));
+
+ /* created - in UTC */
+ prop = icalproperty_new_created (icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ()));
+ icalcomponent_add_property (ical_comp, prop);
+
+ /* last modified - in UTC */
+ if (get_mapi_SPropValue_array_date_timeval (&t, properties, PR_LAST_MODIFICATION_TIME) == MAPI_E_SUCCESS) {
+ prop = icalproperty_new_lastmodified (icaltime_from_timet_with_zone (t.tv_sec, 0, utc_zone));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ icalcomponent_set_summary (ical_comp, subject);
+ icalcomponent_set_description (ical_comp, body);
+
+ if (icalcomponent_isa (ical_comp) == ICAL_VEVENT_COMPONENT) {
+ const char *location = NULL;
+ const gchar *dtstart_tz = NULL, *dtend_tz = NULL;
+ ExchangeMAPIStream *stream;
+
+ /* CleanGlobalObjectId */
+ stream = exchange_mapi_util_find_stream (streams, PROP_TAG(PT_BINARY, 0x0023));
+ if (stream) {
+ gchar *value = id_to_string (stream->value);
+ prop = icalproperty_new_x (value);
+ icalproperty_set_x_name (prop, "X-EVOLUTION-MAPI-CLEAN-GLOBALID");
+ icalcomponent_add_property (ical_comp, prop);
+ g_free (value);
+ }
+
+ /* GlobalObjectId */
+ stream = exchange_mapi_util_find_stream (streams, PROP_TAG(PT_BINARY, 0x0003));
+ if (stream) {
+ gchar *value = id_to_string (stream->value);
+ prop = icalproperty_new_x (value);
+ icalproperty_set_x_name (prop, "X-EVOLUTION-MAPI-GLOBALID");
+ icalcomponent_add_property (ical_comp, prop);
+ g_free (value);
+ }
+
+ /* AppointmentSequence */
+ ui32 = (const uint32_t *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_LONG, 0x8201));
+ if (ui32) {
+ gchar *value = g_strdup_printf ("%d", *ui32);
+ prop = icalproperty_new_x (value);
+ icalproperty_set_x_name (prop, "X-EVOLUTION-MAPI-APPTSEQ");
+ icalcomponent_add_property (ical_comp, prop);
+ g_free (value);
+ }
+
+ location = (const char *)exchange_mapi_util_find_array_propval(properties, PROP_TAG(PT_STRING8, 0x8208));
+ if (location && *location)
+ icalcomponent_set_location (ical_comp, location);
+
+ b = (const bool *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BOOLEAN, 0x8215));
+
+ stream = exchange_mapi_util_find_stream (streams, PROP_TAG(PT_BINARY, 0x825E));
+ if (stream) {
+ gchar *buf = exchange_mapi_cal_util_bin_to_mapi_tz (stream->value);
+ dtstart_tz = exchange_mapi_cal_tz_util_get_ical_equivalent (buf);
+ g_free (buf);
+ }
+
+ if (get_mapi_SPropValue_array_date_timeval (&t, properties, PROP_TAG(PT_SYSTIME, 0x820D)) == MAPI_E_SUCCESS) {
+ icaltimezone *zone = dtstart_tz ? icaltimezone_get_builtin_timezone_from_tzid (dtstart_tz) : default_zone;
+ prop = icalproperty_new_dtstart (icaltime_from_timet_with_zone (t.tv_sec, (b && *b), zone));
+ icalproperty_add_parameter (prop, icalparameter_new_tzid(dtstart_tz));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ stream = exchange_mapi_util_find_stream (streams, PROP_TAG(PT_BINARY, 0x825F));
+ if (stream) {
+ gchar *buf = exchange_mapi_cal_util_bin_to_mapi_tz (stream->value);
+ dtend_tz = exchange_mapi_cal_tz_util_get_ical_equivalent (buf);
+ g_free (buf);
+ }
+
+ if (get_mapi_SPropValue_array_date_timeval (&t, properties, PROP_TAG(PT_SYSTIME, 0x820E)) == MAPI_E_SUCCESS) {
+ icaltimezone *zone = dtend_tz ? icaltimezone_get_builtin_timezone_from_tzid (dtend_tz) : default_zone;
+ prop = icalproperty_new_dtend (icaltime_from_timet_with_zone (t.tv_sec, (b && *b), zone));
+ icalproperty_add_parameter (prop, icalparameter_new_tzid(dtend_tz));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ ui32 = (const uint32_t *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_LONG, 0x8205));
+ if (ui32) {
+ prop = icalproperty_new_transp (get_transp_from_prop (*ui32));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ if (recipients) {
+ b = (const bool *)find_mapi_SPropValue_data(properties, PR_RESPONSE_REQUESTED);
+ ical_attendees_from_props (ical_comp, recipients, (b && *b));
+ if (icalcomponent_get_first_property (ical_comp, ICAL_ORGANIZER_PROPERTY) == NULL) {
+ gchar *val;
+// const char *sender_name = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENDER_NAME);
+ const char *sender_email_type = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENDER_ADDRTYPE);
+ const char *sender_email = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENDER_EMAIL_ADDRESS);
+ const char *sent_name = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_NAME);
+ const char *sent_email_type = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_ADDRTYPE);
+ const char *sent_email = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_EMAIL_ADDRESS);
+
+ if (!g_utf8_collate (sender_email_type, "EX"))
+ sender_email = exchange_mapi_util_ex_to_smtp (sender_email);
+ if (!g_utf8_collate (sent_email_type, "EX"))
+ sent_email = exchange_mapi_util_ex_to_smtp (sent_email);
+
+ val = g_strdup_printf ("MAILTO:%s", sent_email);
+ prop = icalproperty_new_organizer (val);
+ g_free (val);
+ /* CN */
+ param = icalparameter_new_cn (sent_name);
+ icalproperty_add_parameter (prop, param);
+ /* SENTBY */
+ if (g_utf8_collate (sent_email, sender_email)) {
+ val = g_strdup_printf ("MAILTO:%s", sender_email);
+ param = icalparameter_new_sentby (val);
+ icalproperty_add_parameter (prop, param);
+ g_free (val);
+ }
+
+ icalcomponent_add_property (ical_comp, prop);
+ }
+ }
+
+ b = (const bool *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BOOLEAN, 0x8223));
+ if (b && *b) {
+ stream = exchange_mapi_util_find_stream (streams, PROP_TAG(PT_BINARY, 0x8216));
+ if (stream) {
+ exchange_mapi_cal_util_bin_to_rrule (stream->value, comp);
+ }
+ }
+
+ b = (const bool *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BOOLEAN, 0x8503));
+ if (b && *b) {
+ struct timeval start, displaytime;
+
+ if ((get_mapi_SPropValue_array_date_timeval (&start, properties, PROP_TAG(PT_SYSTIME, 0x8502)) == MAPI_E_SUCCESS)
+ && (get_mapi_SPropValue_array_date_timeval (&displaytime, properties, PROP_TAG(PT_SYSTIME, 0x8560)) == MAPI_E_SUCCESS)) {
+ ECalComponentAlarm *e_alarm = e_cal_component_alarm_new ();
+ ECalComponentAlarmTrigger trigger;
+
+ trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
+ trigger.u.rel_duration = icaltime_subtract (icaltime_from_timet_with_zone (displaytime.tv_sec, 0, 0),
+ icaltime_from_timet_with_zone (start.tv_sec, 0, 0));
+
+ e_cal_component_alarm_set_action (e_alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
+ e_cal_component_alarm_set_trigger (e_alarm, trigger);
+
+ e_cal_component_add_alarm (comp, e_alarm);
+ }
+ } else
+ e_cal_component_remove_all_alarms (comp);
+
+ } else if (icalcomponent_isa (ical_comp) == ICAL_VTODO_COMPONENT) {
+ const double *complete = 0;
+
+ /* NOTE: Exchange tasks are DATE values, not DATE-TIME values, but maybe someday, we could expect Exchange to support it ;) */
+ if (get_mapi_SPropValue_array_date_timeval (&t, properties, PROP_TAG(PT_SYSTIME, 0x8104)) == MAPI_E_SUCCESS)
+ icalcomponent_set_dtstart (ical_comp, icaltime_from_timet_with_zone (t.tv_sec, 1, default_zone));
+ if (get_mapi_SPropValue_array_date_timeval (&t, properties, PROP_TAG(PT_SYSTIME, 0x8105)) == MAPI_E_SUCCESS)
+ icalcomponent_set_due (ical_comp, icaltime_from_timet_with_zone (t.tv_sec, 1, default_zone));
+
+ ui32 = (const uint32_t *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_LONG, 0x8101));
+ if (ui32) {
+ icalcomponent_set_status (ical_comp, get_taskstatus_from_prop(*ui32));
+ if (*ui32 == olTaskComplete
+ && get_mapi_SPropValue_array_date_timeval (&t, properties, PROP_TAG(PT_SYSTIME, 0x810F)) == MAPI_E_SUCCESS) {
+ prop = icalproperty_new_completed (icaltime_from_timet_with_zone (t.tv_sec, 1, default_zone));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+ }
+
+ complete = (const double *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_DOUBLE, 0x8102));
+ if (complete) {
+ prop = icalproperty_new_percentcomplete ((int)(*complete * 100));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ b = (const bool *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BOOLEAN, 0x8126));
+ if (b && *b) {
+ /* FIXME: Evolution does not support recurring tasks */
+ g_warning ("Encountered a recurring task.");
+ }
+
+ b = (const bool *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BOOLEAN, 0x8503));
+ if (b && *b) {
+ struct timeval abs;
+
+ if (get_mapi_SPropValue_array_date_timeval (&abs, properties, PROP_TAG(PT_SYSTIME, 0x8502)) == MAPI_E_SUCCESS) {
+ ECalComponentAlarm *e_alarm = e_cal_component_alarm_new ();
+ ECalComponentAlarmTrigger trigger;
+
+ trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE;
+ trigger.u.abs_time = icaltime_from_timet_with_zone (abs.tv_sec, 0, default_zone);
+
+ e_cal_component_alarm_set_action (e_alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
+ e_cal_component_alarm_set_trigger (e_alarm, trigger);
+
+ e_cal_component_add_alarm (comp, e_alarm);
+ }
+ } else
+ e_cal_component_remove_all_alarms (comp);
+
+ } else if (icalcomponent_isa (ical_comp) == ICAL_VJOURNAL_COMPONENT) {
+ if (get_mapi_SPropValue_array_date_timeval (&t, properties, PR_LAST_MODIFICATION_TIME) == MAPI_E_SUCCESS)
+ icalcomponent_set_dtstart (ical_comp, icaltime_from_timet_with_zone (t.tv_sec, 1, default_zone));
+ }
+
+ if (icalcomponent_isa (ical_comp) == ICAL_VEVENT_COMPONENT || icalcomponent_isa (ical_comp) == ICAL_VTODO_COMPONENT) {
+ /* priority */
+ ui32 = (const uint32_t *)find_mapi_SPropValue_data(properties, PR_PRIORITY);
+ if (ui32) {
+ prop = icalproperty_new_priority (get_priority_from_prop (*ui32));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+ }
+
+ /* classification */
+ ui32 = (const uint32_t *)find_mapi_SPropValue_data(properties, PR_SENSITIVITY);
+ if (ui32) {
+ prop = icalproperty_new_class (get_class_from_prop (*ui32));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ /* FIXME: categories */
+
+ set_attachments_to_cal_component (comp, attachments, local_store_uri);
+
+ e_cal_component_rescan (comp);
+
+ return comp;
+}
+
+#define TEMP_ATTACH_STORE ".evolution/cache/tmp"
+
+static void
+change_partstat (ECalComponent *comp, const gchar *att, const gchar *sentby, icalparameter_partstat partstat)
+{
+ icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
+ icalproperty *attendee;
+ gboolean found = FALSE;
+
+ attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+ while (attendee) {
+ const char *value = icalproperty_get_attendee (attendee);
+ if (!g_ascii_strcasecmp (value, att)) {
+ icalparameter *param = icalparameter_new_partstat (partstat);
+ icalproperty_set_parameter (attendee, param);
+ if (g_ascii_strcasecmp(att, sentby)) {
+ icalparameter *sentby_param = icalparameter_new_sentby (sentby);
+ icalproperty_set_parameter (attendee, sentby_param);
+ }
+ found = TRUE;
+ break;
+ }
+ attendee = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+ }
+
+ if (found) {
+ icalproperty *prop = icalproperty_new_x ("1");
+ icalproperty_set_x_name (prop, "X-EVOLUTION-IS-REPLY");
+ icalcomponent_add_property (icalcomp, prop);
+ }
+
+ e_cal_component_set_icalcomponent (comp, icalcomp);
+}
+
+static void
+remove_other_attendees (ECalComponent *comp, const gchar *att)
+{
+ icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
+ icalproperty *attendee;
+
+ attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+ while (attendee) {
+ const char *value = icalproperty_get_attendee (attendee);
+ if (g_ascii_strcasecmp (value, att))
+ icalcomponent_remove_property (icalcomp, attendee);
+
+ attendee = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+ }
+
+ e_cal_component_set_icalcomponent (comp, icalcomp);
+}
+
+static gboolean
+fetch_server_data_cb (struct mapi_SPropValue_array *properties, const mapi_id_t fid, const mapi_id_t mid,
+ GSList *streams, GSList *recipients, GSList *attachments, gpointer data)
+{
+ icalcomponent_kind kind = ICAL_VEVENT_COMPONENT;
+ gchar *filename = g_build_filename (g_get_home_dir (), TEMP_ATTACH_STORE, NULL);
+ gchar *fileuri = g_filename_to_uri (filename, NULL, NULL);
+ gchar *smid = exchange_mapi_util_mapi_id_to_string (mid);
+ ECalComponent *comp = exchange_mapi_cal_util_mapi_props_to_comp (kind, smid, properties, streams, recipients, attachments, fileuri, NULL);
+ struct cbdata *cbdata = (struct cbdata *)(data);
+ const uint32_t *ui32;
+
+ ui32 = (const uint32_t *)find_mapi_SPropValue_data(properties, PR_OWNER_APPT_ID);
+ cbdata->appt_id = ui32 ? *ui32 : 0;
+ ui32 = (const uint32_t *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_LONG, 0x8201));
+ cbdata->appt_seq = ui32 ? *ui32 : 0;
+ cbdata->username = exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_NAME);
+ cbdata->useridtype = exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_ADDRTYPE);
+ cbdata->userid = exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_EMAIL_ADDRESS);
+ cbdata->ownername = exchange_mapi_util_find_array_propval (properties, PR_SENDER_NAME);
+ cbdata->owneridtype = exchange_mapi_util_find_array_propval (properties, PR_SENDER_ADDRTYPE);
+ cbdata->ownerid = exchange_mapi_util_find_array_propval (properties, PR_SENDER_EMAIL_ADDRESS);
+
+ cbdata->comp = comp;
+
+ g_free (smid);
+ g_free (fileuri);
+ g_free (filename);
+
+ return TRUE;
+}
+
+static void
+fetch_server_data (mapi_id_t mid, struct cbdata *cbd)
+{
+ icalcomponent_kind kind = ICAL_VEVENT_COMPONENT;
+ mapi_id_t fid;
+
+ fid = exchange_mapi_get_default_folder_id (olFolderCalendar);
+
+ exchange_mapi_connection_fetch_item (fid, mid,
+ cal_GetPropsList, G_N_ELEMENTS (cal_GetPropsList),
+ exchange_mapi_cal_util_build_name_id, GINT_TO_POINTER (kind),
+ fetch_server_data_cb, cbd,
+ MAPI_OPTIONS_FETCH_RECIPIENTS | MAPI_OPTIONS_FETCH_GENERIC_STREAMS);
+
+}
+
+static ECalComponent *
+update_attendee_status (struct mapi_SPropValue_array *properties, mapi_id_t mid)
+{
+ const gchar *att, *att_sentby, *addrtype;
+ icalparameter_partstat partstat = ICAL_PARTSTAT_NONE;
+ const gchar *state = (const gchar *) exchange_mapi_util_find_array_propval (properties, PR_MESSAGE_CLASS);
+ struct cbdata cbdata;
+ gchar *matt, *matt_sentby;
+ uint32_t cur_seq;
+ const uint32_t *ui32;
+
+ if (!(state && *state))
+ return NULL;
+
+ if (!g_ascii_strcasecmp (state, IPM_SCHEDULE_MEETING_RESP_POS))
+ partstat = ICAL_PARTSTAT_ACCEPTED;
+ else if (!g_ascii_strcasecmp (state, IPM_SCHEDULE_MEETING_RESP_TENT))
+ partstat = ICAL_PARTSTAT_TENTATIVE;
+ else if (!g_ascii_strcasecmp (state, IPM_SCHEDULE_MEETING_RESP_NEG))
+ partstat = ICAL_PARTSTAT_DECLINED;
+ else
+ return NULL;
+
+ fetch_server_data (mid, &cbdata);
+
+ att = exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_EMAIL_ADDRESS);
+ addrtype = exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_ADDRTYPE);
+ if (addrtype && !g_ascii_strcasecmp (addrtype, "EX"))
+ att = exchange_mapi_util_ex_to_smtp (att);
+
+ att_sentby = exchange_mapi_util_find_array_propval (properties, PR_SENDER_EMAIL_ADDRESS);
+ addrtype = exchange_mapi_util_find_array_propval (properties, PR_SENDER_ADDRTYPE);
+ if (addrtype && !g_ascii_strcasecmp (addrtype, "EX"))
+ att_sentby = exchange_mapi_util_ex_to_smtp (att_sentby);
+
+ matt = g_strdup_printf ("MAILTO:%s", att);
+ matt_sentby = g_strdup_printf ("MAILTO:%s", att_sentby);
+
+ change_partstat (cbdata.comp, matt, matt_sentby, partstat);
+
+ ui32 = (const uint32_t *) find_mapi_SPropValue_data(properties, PROP_TAG(PT_LONG, 0x8201));
+ cur_seq = ui32 ? *ui32 : 0;
+
+ if (cbdata.appt_seq == cur_seq) {
+
+/*
+ * The itip-formatter provides an option to update the attendee's status.
+ * Hence, we need not update the server straight away.
+ */
+#if 0
+ gchar *filename = g_build_filename (g_get_home_dir (), TEMP_ATTACH_STORE, NULL);
+ gchar *fileuri = g_filename_to_uri (filename, NULL, NULL);
+ GSList *attachments = NULL, *recipients = NULL, *streams = NULL;
+
+ if (e_cal_component_has_attachments (cbdata.comp))
+ exchange_mapi_cal_util_fetch_attachments (cbdata.comp, &attachments, fileuri);
+
+ if (e_cal_component_has_attendees (cbdata.comp))
+ exchange_mapi_cal_util_fetch_recipients (cbdata.comp, &recipients);
+
+ cbdata.meeting_type = (recipients != NULL) ? MEETING_OBJECT : NOT_A_MEETING;
+ cbdata.resp = (recipients != NULL) ? olResponseOrganized : olResponseNone;
+ cbdata.msgflags = MSGFLAG_READ;
+ cbdata.is_modify = TRUE;
+ cbdata.cleanglobalid = (const struct SBinary *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BINARY, 0x0023));
+ cbdata.globalid = (const struct SBinary *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BINARY, 0x0003));
+
+ status = exchange_mapi_modify_item (olFolderCalendar, fid, mid,
+ exchange_mapi_cal_util_build_name_id, GINT_TO_POINTER (kind),
+ exchange_mapi_cal_util_build_props, &cbdata,
+ recipients, attachments, streams, MAPI_OPTIONS_DONT_SUBMIT);
+ g_free (cbdata.props);
+
+ exchange_mapi_util_free_recipient_list (&recipients);
+ exchange_mapi_util_free_attachment_list (&attachments);
+ g_free (fileuri);
+ g_free (filename);
+#endif
+
+ /* remove the other attendees so not to confuse itip-formatter */
+ remove_other_attendees (cbdata.comp, matt);
+ } else {
+ g_object_unref (cbdata.comp);
+ cbdata.comp = NULL;
+ }
+
+ g_free (matt);
+ g_free (matt_sentby);
+
+ return cbdata.comp;
+}
+
+static void
+update_server_object (struct mapi_SPropValue_array *properties, GSList *attachments, ECalComponent *comp, mapi_id_t *mid)
+{
+ const uint32_t *ui32 = NULL;
+ uint32_t cur_seq;
+ mapi_id_t fid;
+ gboolean create_new = TRUE;
+
+ fid = exchange_mapi_get_default_folder_id (olFolderCalendar);
+
+ ui32 = (const uint32_t *) find_mapi_SPropValue_data(properties, PROP_TAG(PT_LONG, 0x8201));
+ cur_seq = ui32 ? *ui32 : 0;
+
+ if (*mid) {
+ struct cbdata server_cbd;
+ fetch_server_data (*mid, &server_cbd);
+
+ if (cur_seq > server_cbd.appt_seq) {
+ struct id_list idlist;
+ GSList *ids = NULL;
+
+ idlist.id = *mid;
+ ids = g_slist_append (ids, &idlist);
+
+ exchange_mapi_remove_items (olFolderCalendar, fid, ids);
+ g_slist_free (ids);
+ } else
+ create_new = FALSE;
+ }
+
+ if (create_new) {
+ struct cbdata cbdata;
+ GSList *myrecipients = NULL;
+ GSList *myattachments = NULL;
+ icalcomponent_kind kind = icalcomponent_isa (e_cal_component_get_icalcomponent(comp));
+
+ cbdata.comp = comp;
+ cbdata.username = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENDER_NAME);
+ cbdata.useridtype = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENDER_ADDRTYPE);
+ cbdata.userid = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENDER_EMAIL_ADDRESS);
+ cbdata.ownername = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_NAME);
+ cbdata.owneridtype = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_ADDRTYPE);
+ cbdata.ownerid = (const char *) exchange_mapi_util_find_array_propval (properties, PR_SENT_REPRESENTING_EMAIL_ADDRESS);
+ cbdata.is_modify = FALSE;
+ cbdata.msgflags = MSGFLAG_READ;
+ cbdata.meeting_type = MEETING_REQUEST_RCVD;
+ cbdata.resp = olResponseNone;
+ cbdata.appt_seq = (*(const uint32_t *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_LONG, 0x8201)));
+ cbdata.appt_id = (*(const uint32_t *)find_mapi_SPropValue_data(properties, PR_OWNER_APPT_ID));
+ cbdata.globalid = (const struct SBinary *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BINARY, 0x0003));
+ cbdata.cleanglobalid = (const struct SBinary *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BINARY, 0x0023));
+
+ exchange_mapi_cal_util_fetch_recipients (comp, &myrecipients);
+ myattachments = attachments;
+ *mid = exchange_mapi_create_item (olFolderCalendar, 0,
+ exchange_mapi_cal_util_build_name_id, GINT_TO_POINTER(kind),
+ exchange_mapi_cal_util_build_props, &cbdata,
+ myrecipients, myattachments, NULL, MAPI_OPTIONS_DONT_SUBMIT);
+ g_free (cbdata.props);
+ exchange_mapi_util_free_recipient_list (&myrecipients);
+ }
+}
+
+static void
+check_server_for_object (struct mapi_SPropValue_array *properties, mapi_id_t *mid)
+{
+ struct mapi_SRestriction res;
+ struct SPropValue sprop;
+ const struct SBinary *sb;
+ uint32_t proptag = 0x0;
+ struct SPropTagArray *array;
+ GSList *ids = NULL, *l;
+ mapi_id_t fid;
+
+ *mid = 0;
+
+ fid = exchange_mapi_get_default_folder_id (olFolderCalendar);
+
+ array = exchange_mapi_util_resolve_named_prop (olFolderCalendar, fid, 0x0023, PSETID_Meeting);
+ proptag = array->aulPropTag[0];
+
+ res.rt = RES_PROPERTY;
+ res.res.resProperty.relop = RELOP_EQ;
+ res.res.resProperty.ulPropTag = proptag;
+
+ sb = (const struct SBinary *)find_mapi_SPropValue_data(properties, PROP_TAG(PT_BINARY, 0x0023));
+
+ set_SPropValue_proptag (&sprop, proptag, (const void *) sb);
+ cast_mapi_SPropValue (&(res.res.resProperty.lpProp), &sprop);
+
+ ids = exchange_mapi_util_check_restriction (fid, &res);
+
+ if (ids && g_slist_length(ids) == 1) {
+ struct id_list *idlist = (struct id_list *)(ids->data);
+ *mid = idlist->id;
+ } else
+ /* FIXME: what to do here? */
+ ;
+
+ for (l = ids; l; l = l->next)
+ g_free(l->data);
+ g_slist_free(l);
+}
+
+char *
+exchange_mapi_cal_util_camel_helper (struct mapi_SPropValue_array *properties,
+ GSList *streams, GSList *recipients, GSList *attachments)
+{
+ ECalComponent *comp = NULL;
+ icalcomponent_kind kind = ICAL_NO_COMPONENT;
+ icalproperty_method method = ICAL_METHOD_NONE;
+ const char *msg_class = NULL;
+ mapi_id_t mid = 0;
+ icalcomponent *icalcomp = NULL;
+ gchar *str = NULL, *smid = NULL, *tmp, *filename, *fileuri;
+
+ msg_class = (const char *) exchange_mapi_util_find_array_propval (properties, PR_MESSAGE_CLASS);
+ g_return_val_if_fail (msg_class && *msg_class, NULL);
+ if (!g_ascii_strcasecmp (msg_class, IPM_SCHEDULE_MEETING_REQUEST)) {
+ method = ICAL_METHOD_REQUEST;
+ kind = ICAL_VEVENT_COMPONENT;
+ } else if (!g_ascii_strcasecmp (msg_class, IPM_SCHEDULE_MEETING_CANCELED)) {
+ method = ICAL_METHOD_CANCEL;
+ kind = ICAL_VEVENT_COMPONENT;
+ } else if (g_str_has_prefix (msg_class, IPM_SCHEDULE_MEETING_RESP_PREFIX)) {
+ method = ICAL_METHOD_REPLY;
+ kind = ICAL_VEVENT_COMPONENT;
+ } else
+ return (g_strdup (""));
+
+ filename = g_build_filename (g_get_home_dir (), TEMP_ATTACH_STORE, NULL);
+ fileuri = g_filename_to_uri (filename, NULL, NULL);
+
+ check_server_for_object (properties, &mid);
+
+ if (method == ICAL_METHOD_REPLY) {
+ if (mid) {
+ comp = update_attendee_status (properties, mid);
+ set_attachments_to_cal_component (comp, attachments, fileuri);
+ }
+ } else if (method == ICAL_METHOD_CANCEL) {
+ if (mid) {
+ struct cbdata server_cbd;
+ fetch_server_data (mid, &server_cbd);
+ comp = server_cbd.comp;
+ set_attachments_to_cal_component (comp, attachments, fileuri);
+ }
+ } else if (method == ICAL_METHOD_REQUEST) {
+ if (mid)
+ smid = exchange_mapi_util_mapi_id_to_string (mid);
+ else
+ smid = e_cal_component_gen_uid();
+
+ comp = exchange_mapi_cal_util_mapi_props_to_comp (kind, smid,
+ properties, streams, recipients,
+ NULL, NULL, NULL);
+ set_attachments_to_cal_component (comp, attachments, fileuri);
+
+ update_server_object (properties, attachments, comp, &mid);
+
+ tmp = exchange_mapi_util_mapi_id_to_string (mid);
+ e_cal_component_set_uid (comp, tmp);
+ g_free (tmp);
+ g_free (smid);
+ }
+
+ g_free (fileuri);
+ g_free (filename);
+
+ icalcomp = e_cal_util_new_top_level ();
+ icalcomponent_set_method (icalcomp, method);
+ if (comp)
+ icalcomponent_add_component (icalcomp,
+ icalcomponent_new_clone(e_cal_component_get_icalcomponent(comp)));
+ str = icalcomponent_as_ical_string (icalcomp);
+ icalcomponent_free (icalcomp);
+ if (comp)
+ g_object_unref (comp);
+
+ return str;
+}
+
+
+#define COMMON_NAMED_PROPS_N 9
+
+typedef enum
+{
+ I_COMMON_REMMINS = 0 ,
+ I_COMMON_REMTIME ,
+ I_COMMON_REMSET ,
+ I_COMMON_ISPRIVATE ,
+ I_COMMON_SIDEEFFECTS ,
+ I_COMMON_START ,
+ I_COMMON_END ,
+ I_COMMON_TASKMODE ,
+ I_COMMON_REMNEXTTIME
+} CommonNamedPropsIndex;
+
+gboolean
+exchange_mapi_cal_util_build_name_id (struct mapi_nameid *nameid, gpointer data)
+{
+ icalcomponent_kind kind = GPOINTER_TO_INT (data);
+
+ /* NOTE: Avoid using mapi_nameid_OOM_add because:
+ * a) its inefficient (uses strcmp)
+ * b) names may vary in different server/libmapi versions
+ */
+
+ mapi_nameid_lid_add(nameid, 0x8501, PSETID_Common); // PT_LONG - ReminderMinutesBeforeStart
+ mapi_nameid_lid_add(nameid, 0x8502, PSETID_Common); // PT_SYSTIME - ReminderTime
+ mapi_nameid_lid_add(nameid, 0x8503, PSETID_Common); // PT_BOOLEAN - ReminderSet
+ mapi_nameid_lid_add(nameid, 0x8506, PSETID_Common); // PT_BOOLEAN - Private
+ mapi_nameid_lid_add(nameid, 0x8510, PSETID_Common); // PT_LONG - (context menu flags)
+ mapi_nameid_lid_add(nameid, 0x8516, PSETID_Common); // PT_SYSTIME - CommonStart
+ mapi_nameid_lid_add(nameid, 0x8517, PSETID_Common); // PT_SYSTIME - CommonEnd
+ mapi_nameid_lid_add(nameid, 0x8518, PSETID_Common); // PT_LONG - TaskMode
+ mapi_nameid_lid_add(nameid, 0x8560, PSETID_Common); // PT_SYSTIME - ReminderNextTime
+
+ if (kind == ICAL_VEVENT_COMPONENT)
+ appt_build_name_id (nameid);
+ else if (kind == ICAL_VTODO_COMPONENT)
+ task_build_name_id (nameid);
+ else if (kind == ICAL_VJOURNAL_COMPONENT)
+ note_build_name_id (nameid);
+
+ return TRUE;
+}
+
+/**
+ * NOTE: The enumerations '(Appt/Task/Note)NamedPropsIndex' have been defined
+ * only to make life a little easier for developers. Here's the logic
+ * behind the definition:
+ 1) The first element is initialized with 'COMMON_NAMED_PROPS_N' : When
+ adding named props, we add the common named props first and then the
+ specific named props. So.. the index of the first specific
+ named property = COMMON_NAMED_PROPS_N
+ 2) The order in the enumeration 'must' be the same as that in the routine
+ which adds the specific named props - (appt/task/note)_build_name_id
+ 3) If a specific named prop is added/deleted, an index needs to
+ be created/deleted at the correct position. [Don't forget to update
+ (APPT/TASK/NOTE)_NAMED_PROPS_N].
+
+ * To summarize the pros:
+ 1) Addition/deletion of a common-named-prop would not affect the indexes
+ of the specific named props once COMMON_NAMED_PROPS_N is updated.
+ 2) Values of named props can be added in any order.
+ */
+
+
+#define APPT_NAMED_PROPS_N 29
+#define DEFAULT_APPT_REMINDER_MINS 15
+
+typedef enum
+{
+ I_APPT_SEQ = COMMON_NAMED_PROPS_N ,
+ I_APPT_BUSYSTATUS ,
+ I_APPT_LOCATION ,
+ I_APPT_START ,
+ I_APPT_END ,
+ I_APPT_DURATION ,
+ I_APPT_ALLDAY ,
+ I_APPT_RECURBLOB ,
+ I_APPT_STATEFLAGS ,
+ I_APPT_RESPONSESTATUS ,
+ I_APPT_RECURRING ,
+ I_APPT_INTENDEDBUSY ,
+ I_APPT_RECURBASE ,
+ I_APPT_INVITED ,
+ I_APPT_RECURTYPE ,
+ I_APPT_CLIPSTART ,
+ I_APPT_CLIPEND ,
+ I_APPT_AUTOLOCATION ,
+ I_APPT_ISCOUNTERPROPOSAL ,
+ I_APPT_NOTALLOWPROPOSE ,
+ I_APPT_STARTTZBLOB ,
+ I_APPT_ENDTZBLOB ,
+
+ I_MEET_WHERE ,
+ I_MEET_GUID ,
+ I_MEET_ISRECURRING ,
+ I_MEET_ISEXCEPTION ,
+ I_MEET_CLEANGUID ,
+ I_MEET_APPTMSGCLASS ,
+ I_MEET_TYPE
+
+// I_APPT_SENDASICAL ,
+// I_APPT_SEQTIME ,
+// I_APPT_LABEL ,
+// I_APPT_RECURPATTERN ,
+// I_APPT_DISPTZ ,
+// I_APPT_ALLATTENDEES ,
+// I_APPT_TOATTENDEES ,
+// I_APPT_CCATTENDEES ,
+} ApptNamedPropsIndex;
+
+static void
+appt_build_name_id (struct mapi_nameid *nameid)
+{
+ mapi_nameid_lid_add(nameid, 0x8201, PSETID_Appointment); // PT_LONG - ApptSequence
+ mapi_nameid_lid_add(nameid, 0x8205, PSETID_Appointment); // PT_LONG - BusyStatus
+ mapi_nameid_lid_add(nameid, 0x8208, PSETID_Appointment); // PT_UNICODE - Location
+ mapi_nameid_lid_add(nameid, 0x820D, PSETID_Appointment); // PT_SYSTIME - Start/ApptStartWhole
+ mapi_nameid_lid_add(nameid, 0x820E, PSETID_Appointment); // PT_SYSTIME - End/ApptEndWhole
+ mapi_nameid_lid_add(nameid, 0x8213, PSETID_Appointment); // PT_LONG - Duration/ApptDuration
+ mapi_nameid_lid_add(nameid, 0x8215, PSETID_Appointment); // PT_BOOLEAN - AllDayEvent (also called ApptSubType)
+ mapi_nameid_lid_add(nameid, 0x8216, PSETID_Appointment); // PT_BINARY - (recurrence blob)
+ mapi_nameid_lid_add(nameid, 0x8217, PSETID_Appointment); // PT_LONG - ApptStateFlags
+ mapi_nameid_lid_add(nameid, 0x8218, PSETID_Appointment); // PT_LONG - ResponseStatus
+ mapi_nameid_lid_add(nameid, 0x8223, PSETID_Appointment); // PT_BOOLEAN - Recurring
+ mapi_nameid_lid_add(nameid, 0x8224, PSETID_Appointment); // PT_LONG - IntendedBusyStatus
+ mapi_nameid_lid_add(nameid, 0x8228, PSETID_Appointment); // PT_SYSTIME - RecurrenceBase
+ mapi_nameid_lid_add(nameid, 0x8229, PSETID_Appointment); // PT_BOOLEAN - FInvited
+ mapi_nameid_lid_add(nameid, 0x8231, PSETID_Appointment); // PT_LONG - RecurrenceType
+ mapi_nameid_lid_add(nameid, 0x8235, PSETID_Appointment); // PT_SYSTIME - (dtstart)(for recurring events UTC 12 AM of day of start)
+ mapi_nameid_lid_add(nameid, 0x8236, PSETID_Appointment); // PT_SYSTIME - (dtend)(for recurring events UTC 12 AM of day of end)
+ mapi_nameid_lid_add(nameid, 0x823A, PSETID_Appointment); // PT_BOOLEAN - AutoFillLocation
+ mapi_nameid_lid_add(nameid, 0x8257, PSETID_Appointment); // PT_BOOLEAN - ApptCounterProposal
+ mapi_nameid_lid_add(nameid, 0x825A, PSETID_Appointment); // PT_BOOLEAN - ApptNotAllowPropose
+ mapi_nameid_lid_add(nameid, 0x825E, PSETID_Appointment); // PT_BINARY - (timezone for dtstart)
+ mapi_nameid_lid_add(nameid, 0x825F, PSETID_Appointment); // PT_BINARY - (timezone for dtend)
+
+ mapi_nameid_lid_add(nameid, 0x0002, PSETID_Meeting); // PT_UNICODE - Where
+ mapi_nameid_lid_add(nameid, 0x0003, PSETID_Meeting); // PT_BINARY - GlobalObjectId
+ mapi_nameid_lid_add(nameid, 0x0005, PSETID_Meeting); // PT_BOOLEAN - IsRecurring
+ mapi_nameid_lid_add(nameid, 0x000A, PSETID_Meeting); // PT_BOOLEAN - IsException
+ mapi_nameid_lid_add(nameid, 0x0023, PSETID_Meeting); // PT_BINARY - CleanGlobalObjectId
+ mapi_nameid_lid_add(nameid, 0x0024, PSETID_Meeting); // PT_STRING8 - AppointmentMessageClass
+ mapi_nameid_lid_add(nameid, 0x0026, PSETID_Meeting); // PT_LONG - MeetingType
+
+ /* These probably would never be used from Evolution */
+// mapi_nameid_lid_add(nameid, 0x8200, PSETID_Appointment); // PT_BOOLEAN - SendAsICAL
+// mapi_nameid_lid_add(nameid, 0x8202, PSETID_Appointment); // PT_SYSTIME - ApptSequenceTime
+// mapi_nameid_lid_add(nameid, 0x8214, PSETID_Appointment); // PT_LONG - Label
+// mapi_nameid_lid_add(nameid, 0x8232, PSETID_Appointment); // PT_STRING8 - RecurrencePattern
+// mapi_nameid_lid_add(nameid, 0x8234, PSETID_Appointment); // PT_STRING8 - display TimeZone
+// mapi_nameid_lid_add(nameid, 0x8238, PSETID_Appointment); // PT_STRING8 - AllAttendees
+// mapi_nameid_lid_add(nameid, 0x823B, PSETID_Appointment); // PT_STRING8 - ToAttendeesString (dupe PR_DISPLAY_TO)
+// mapi_nameid_lid_add(nameid, 0x823C, PSETID_Appointment); // PT_STRING8 - CCAttendeesString (dupe PR_DISPLAY_CC)
+}
+
+#define TASK_NAMED_PROPS_N 13
+#define DEFAULT_TASK_REMINDER_MINS 1080
+
+typedef enum
+{
+ I_TASK_STATUS = COMMON_NAMED_PROPS_N ,
+ I_TASK_PERCENT ,
+ I_TASK_ISTEAMTASK ,
+ I_TASK_START ,
+ I_TASK_DUE ,
+ I_TASK_COMPLETED ,
+// I_TASK_RECURBLOB ,
+ I_TASK_ISCOMPLETE ,
+ I_TASK_OWNER ,
+ I_TASK_DELEGATOR ,
+ I_TASK_ISRECURRING ,
+ I_TASK_ROLE ,
+ I_TASK_OWNERSHIP ,
+ I_TASK_DELEGATIONSTATE ,
+// I_TASK_ACTUALWORK ,
+// I_TASK_TOTALWORK
+} TaskNamedPropsIndex;
+
+static void
+task_build_name_id (struct mapi_nameid *nameid)
+{
+ mapi_nameid_lid_add(nameid, 0x8101, PSETID_Task); // PT_LONG - Status
+ mapi_nameid_lid_add(nameid, 0x8102, PSETID_Task); // PT_DOUBLE - PercentComplete
+ mapi_nameid_lid_add(nameid, 0x8103, PSETID_Task); // PT_BOOLEAN - TeamTask
+ mapi_nameid_lid_add(nameid, 0x8104, PSETID_Task); // PT_SYSTIME - StartDate/TaskStartDate
+ mapi_nameid_lid_add(nameid, 0x8105, PSETID_Task); // PT_SYSTIME - DueDate/TaskDueDate
+ mapi_nameid_lid_add(nameid, 0x810F, PSETID_Task); // PT_SYSTIME - DateCompleted
+// mapi_nameid_lid_add(nameid, 0x8116, PSETID_Task); // PT_BINARY - (recurrence blob)
+ mapi_nameid_lid_add(nameid, 0x811C, PSETID_Task); // PT_BOOLEAN - Complete
+ mapi_nameid_lid_add(nameid, 0x811F, PSETID_Task); // PT_STRING8 - Owner
+ mapi_nameid_lid_add(nameid, 0x8121, PSETID_Task); // PT_STRING8 - Delegator
+ mapi_nameid_lid_add(nameid, 0x8126, PSETID_Task); // PT_BOOLEAN - IsRecurring/TaskFRecur
+ mapi_nameid_lid_add(nameid, 0x8127, PSETID_Task); // PT_STRING8 - Role
+ mapi_nameid_lid_add(nameid, 0x8129, PSETID_Task); // PT_LONG - Ownership
+ mapi_nameid_lid_add(nameid, 0x812A, PSETID_Task); // PT_LONG - DelegationState
+
+ /* These probably would never be used from Evolution */
+// mapi_nameid_lid_add(nameid, 0x8110, PSETID_Task); // PT_LONG - ActualWork/TaskActualEffort
+// mapi_nameid_lid_add(nameid, 0x8111, PSETID_Task); // PT_LONG - TotalWork/TaskEstimatedEffort
+}
+
+
+#define NOTE_NAMED_PROPS_N 3
+
+typedef enum
+{
+ I_NOTE_COLOR = COMMON_NAMED_PROPS_N ,
+ I_NOTE_WIDTH ,
+ I_NOTE_HEIGHT
+} NoteNamedPropsIndex;
+
+static void
+note_build_name_id (struct mapi_nameid *nameid)
+{
+ mapi_nameid_lid_add(nameid, 0x8B00, PSETID_Note); // PT_LONG - Color
+ mapi_nameid_lid_add(nameid, 0x8B02, PSETID_Note); // PT_LONG - Width
+ mapi_nameid_lid_add(nameid, 0x8B03, PSETID_Note); // PT_LONG - Height
+}
+
+#define MINUTES_IN_HOUR 60
+#define SECS_IN_MINUTE 60
+
+/**
+ * NOTE: When a new regular property (PR_***) is added, 'REGULAR_PROPS_N'
+ * should be updated.
+ */
+#define REGULAR_PROPS_N 22
+
+int
+exchange_mapi_cal_util_build_props (struct SPropValue **value, struct SPropTagArray *proptag_array, gpointer data)
+{
+ struct cbdata *cbdata = (struct cbdata *) data;
+ ECalComponent *comp = cbdata->comp;
+ icalcomponent *ical_comp = e_cal_component_get_icalcomponent (comp);
+ icalcomponent_kind kind = icalcomponent_isa (ical_comp);
+ struct SPropValue *props = NULL;
+ int i=0;
+ uint32_t flag32;
+ bool b;
+ icalproperty *prop;
+ struct icaltimetype dtstart, dtend, utc_dtstart, utc_dtend;
+ const icaltimezone *utc_zone;
+ const char *dtstart_tzid, *dtend_tzid, *text = NULL;
+ struct timeval t;
+
+ flag32 = REGULAR_PROPS_N + COMMON_NAMED_PROPS_N;
+ switch (kind) {
+ case ICAL_VEVENT_COMPONENT:
+ flag32 += APPT_NAMED_PROPS_N;
+ break;
+ case ICAL_VTODO_COMPONENT:
+ flag32 += TASK_NAMED_PROPS_N;
+ break;
+ case ICAL_VJOURNAL_COMPONENT:
+ flag32 += NOTE_NAMED_PROPS_N;
+ break;
+ default:
+ return 0;
+ }
+
+ d(g_debug ("Allocating space for %d props ", flag32));
+ props = g_new0 (struct SPropValue, flag32);
+
+ /* PR_MESSAGE_CLASS needs to be set appropriately */ /* propcount++ */
+
+ utc_zone = icaltimezone_get_utc_timezone ();
+
+ dtstart = icalcomponent_get_dtstart (ical_comp);
+
+ /* For VEVENTs */
+ if (icalcomponent_get_first_property (ical_comp, ICAL_DTEND_PROPERTY) != 0)
+ dtend = icalcomponent_get_dtend (ical_comp);
+ /* For VTODOs */
+ else if (icalcomponent_get_first_property (ical_comp, ICAL_DUE_PROPERTY) != 0)
+ dtend = icalcomponent_get_due (ical_comp);
+ else
+ dtend = icalcomponent_get_dtstart (ical_comp);
+
+ dtstart_tzid = icaltime_get_tzid (dtstart);
+ dtend_tzid = icaltime_get_tzid (dtend);
+
+ utc_dtstart = icaltime_convert_to_zone (dtstart, utc_zone);
+ utc_dtend = icaltime_convert_to_zone (dtend, utc_zone);
+
+ text = icalcomponent_get_summary (ical_comp);
+ if (!(text && *text))
+ text = "";
+ set_SPropValue_proptag(&props[i++], PR_SUBJECT, /* propcount++ */
+ (const void *) text);
+ set_SPropValue_proptag(&props[i++], PR_NORMALIZED_SUBJECT, /* propcount++ */
+ (const void *) text);
+ if (cbdata->appt_seq == 0)
+ set_SPropValue_proptag(&props[i++], PR_CONVERSATION_TOPIC, /* propcount++ */
+ (const void *) text);
+ text = NULL;
+
+ /* we don't support HTML event/task/memo editor */
+ flag32 = olEditorText;
+ set_SPropValue_proptag(&props[i++], PR_MSG_EDITOR_FORMAT, &flag32); /* propcount++ */
+
+ /* it'd be better to convert, then set it in unicode */
+ text = icalcomponent_get_description (ical_comp);
+ if (!(text && *text) || !g_utf8_validate (text, -1, NULL))
+ text = "";
+ set_SPropValue_proptag(&props[i++], PR_BODY, /* propcount++ */
+ (const void *) text);
+ text = NULL;
+
+ /* Priority and Importance */
+ prop = icalcomponent_get_first_property (ical_comp, ICAL_PRIORITY_PROPERTY);
+ flag32 = prop ? get_prio_prop_from_priority (icalproperty_get_priority (prop)) : PRIORITY_NORMAL;
+ set_SPropValue_proptag(&props[i++], PR_PRIORITY, (const void *) &flag32); /* propcount++ */
+ flag32 = prop ? get_imp_prop_from_priority (icalproperty_get_priority (prop)) : IMPORTANCE_NORMAL;
+ set_SPropValue_proptag(&props[i++], PR_IMPORTANCE, (const void *) &flag32); /* propcount++ */
+
+ set_SPropValue_proptag(&props[i++], PR_SENT_REPRESENTING_NAME,
+ (const void *) cbdata->ownername); /* propcount++ */
+ set_SPropValue_proptag(&props[i++], PR_SENT_REPRESENTING_ADDRTYPE,
+ (const void *) cbdata->owneridtype); /* propcount++ */
+ set_SPropValue_proptag(&props[i++], PR_SENT_REPRESENTING_EMAIL_ADDRESS,
+ (const void *) cbdata->ownerid); /* propcount++ */
+ set_SPropValue_proptag(&props[i++], PR_SENDER_NAME,
+ (const void *) cbdata->username); /* propcount++ */
+ set_SPropValue_proptag(&props[i++], PR_SENDER_ADDRTYPE,
+ (const void *) cbdata->useridtype); /* propcount++ */
+ set_SPropValue_proptag(&props[i++], PR_SENDER_EMAIL_ADDRESS,
+ (const void *) cbdata->userid); /* propcount++ */
+
+ flag32 = cbdata->msgflags;
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_FLAGS, (const void *) &flag32); /* propcount++ */
+
+ flag32 = 0x0;
+ b = e_cal_component_has_alarms (comp);
+ if (b) {
+ /* We know there would be only a single alarm of type:DISPLAY [static properties of the backend] */
+ GList *alarm_uids = e_cal_component_get_alarm_uids (comp);
+ ECalComponentAlarm *alarm = e_cal_component_get_alarm (comp, (const char *)(alarm_uids->data));
+ ECalComponentAlarmAction action;
+ e_cal_component_alarm_get_action (alarm, &action);
+ if (action == E_CAL_COMPONENT_ALARM_DISPLAY) {
+ ECalComponentAlarmTrigger trigger;
+ e_cal_component_alarm_get_trigger (alarm, &trigger);
+ int dur_int = 0;
+ switch (trigger.type) {
+ case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START :
+ dur_int = (icaldurationtype_as_int (trigger.u.rel_duration)) / SECS_IN_MINUTE;
+ /* we cannot set an alarm to popup after the start of an appointment on Exchange */
+ flag32 = (dur_int < 0) ? -(dur_int) : 0;
+ break;
+ default :
+ break;
+ }
+ }
+ e_cal_component_alarm_free (alarm);
+ cal_obj_uid_list_free (alarm_uids);
+ }
+ if (!flag32)
+ switch (kind) {
+ case ICAL_VEVENT_COMPONENT:
+ flag32 = DEFAULT_APPT_REMINDER_MINS;
+ break;
+ case ICAL_VTODO_COMPONENT:
+ flag32 = DEFAULT_TASK_REMINDER_MINS;
+ break;
+ default:
+ break;
+ }
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_REMSET], (const void *) &b);
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_REMMINS], (const void *) &flag32);
+ t.tv_sec = icaltime_as_timet (utc_dtstart);
+ t.tv_usec = 0;
+ set_SPropValue_proptag_date_timeval(&props[i++], proptag_array->aulPropTag[I_COMMON_REMTIME], &t);
+ t.tv_sec = icaltime_as_timet (utc_dtstart) - (flag32 * SECS_IN_MINUTE);
+ t.tv_usec = 0;
+ /* ReminderNextTime: FIXME for recurrence */
+ set_SPropValue_proptag_date_timeval(&props[i++], proptag_array->aulPropTag[I_COMMON_REMNEXTTIME], &t);
+
+ /* Sensitivity, Private */
+ flag32 = olNormal; /* default */
+ b = 0; /* default */
+ prop = icalcomponent_get_first_property (ical_comp, ICAL_CLASS_PROPERTY);
+ if (prop)
+ flag32 = get_prop_from_class (icalproperty_get_class (prop));
+ if (flag32 == olPrivate || flag32 == olConfidential)
+ b = 1;
+ set_SPropValue_proptag(&props[i++], PR_SENSITIVITY, (const void *) &flag32); /* propcount++ */
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_ISPRIVATE], (const void *) &b);
+
+ t.tv_sec = icaltime_as_timet (utc_dtstart);
+ t.tv_usec = 0;
+ set_SPropValue_proptag_date_timeval(&props[i++], proptag_array->aulPropTag[I_COMMON_START], &t);
+ set_SPropValue_proptag_date_timeval(&props[i++], PR_START_DATE, &t); /* propcount++ */
+
+ t.tv_sec = icaltime_as_timet (utc_dtend);
+ t.tv_usec = 0;
+ set_SPropValue_proptag_date_timeval(&props[i++], proptag_array->aulPropTag[I_COMMON_END], &t);
+ set_SPropValue_proptag_date_timeval(&props[i++], PR_END_DATE, &t); /* propcount++ */
+
+ b = 1;
+ set_SPropValue_proptag(&props[i++], PR_RESPONSE_REQUESTED, (const void *) &b); /* propcount++ */
+
+ /* PR_OWNER_APPT_ID needs to be set in certain cases only */ /* propcount++ */
+ /* PR_ICON_INDEX needs to be set appropriately */ /* propcount++ */
+
+ b = 0;
+ set_SPropValue_proptag(&props[i++], PR_RTF_IN_SYNC, (const void *) &b); /* propcount++ */
+
+ if (kind == ICAL_VEVENT_COMPONENT) {
+ const char *mapi_tzid;
+ struct SBinary start_tz, end_tz;
+
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_APPTMSGCLASS], (const void *) IPM_APPOINTMENT);
+
+ /* Busy Status */
+ flag32 = olBusy; /* default */
+ prop = icalcomponent_get_first_property (ical_comp, ICAL_TRANSP_PROPERTY);
+ if (prop)
+ flag32 = get_prop_from_transp (icalproperty_get_transp (prop));
+ if (cbdata->meeting_type == MEETING_CANCEL)
+ flag32 = olFree;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_INTENDEDBUSY], (const void *) &flag32);
+
+ if (cbdata->meeting_type == MEETING_REQUEST || cbdata->meeting_type == MEETING_REQUEST_RCVD) {
+ flag32 = olTentative;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_BUSYSTATUS], (const void *) &flag32);
+ } else if (cbdata->meeting_type == MEETING_CANCEL) {
+ flag32 = olFree;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_BUSYSTATUS], (const void *) &flag32);
+ } else
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_BUSYSTATUS], (const void *) &flag32);
+
+ /* Location */
+ text = icalcomponent_get_location (ical_comp);
+ if (!(text && *text))
+ text = "";
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_LOCATION], (const void *) text);
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_WHERE], (const void *) text);
+ text = NULL;
+ /* Auto-Location is always FALSE - Evolution doesn't work that way */
+ b = 0;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_AUTOLOCATION], (const void *) &b);
+
+ /* Start */
+ t.tv_sec = icaltime_as_timet (utc_dtstart);
+ t.tv_usec = 0;
+ set_SPropValue_proptag_date_timeval(&props[i++], proptag_array->aulPropTag[I_APPT_START], &t);
+ /* FIXME: for recurrence */
+ set_SPropValue_proptag_date_timeval(&props[i++], proptag_array->aulPropTag[I_APPT_CLIPSTART], &t);
+
+ /* Start TZ */
+ mapi_tzid = exchange_mapi_cal_tz_util_get_mapi_equivalent ((dtstart_tzid && *dtstart_tzid) ? dtstart_tzid : "UTC");
+ if (mapi_tzid && *mapi_tzid) {
+ exchange_mapi_cal_util_mapi_tz_to_bin (mapi_tzid, &start_tz);
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_STARTTZBLOB], (const void *) &start_tz);
+ }
+
+ /* End */
+ t.tv_sec = icaltime_as_timet (utc_dtend);
+ t.tv_usec = 0;
+ set_SPropValue_proptag_date_timeval(&props[i++], proptag_array->aulPropTag[I_APPT_END], &t);
+ /* FIXME: for recurrence */
+ set_SPropValue_proptag_date_timeval(&props[i++], proptag_array->aulPropTag[I_APPT_CLIPEND], &t);
+
+ /* End TZ */
+ mapi_tzid = exchange_mapi_cal_tz_util_get_mapi_equivalent ((dtend_tzid && *dtend_tzid) ? dtend_tzid : "UTC");
+ if (mapi_tzid && *mapi_tzid) {
+ exchange_mapi_cal_util_mapi_tz_to_bin (mapi_tzid, &end_tz);
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_ENDTZBLOB], (const void *) &end_tz);
+ }
+
+ /* Duration */
+ flag32 = icaldurationtype_as_int (icaltime_subtract (dtend, dtstart));
+ flag32 /= MINUTES_IN_HOUR;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_DURATION], (const void *) &flag32);
+
+ /* All-day event */
+ b = (icaltime_is_date (dtstart) && icaltime_is_date (dtend));
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_ALLDAY], (const void *) &b);
+
+ if (e_cal_component_has_recurrences (comp)) {
+ GSList *rrule_list = NULL;
+ struct icalrecurrencetype *rt = NULL;
+
+ e_cal_component_get_rrule_list (comp, &rrule_list);
+ rt = (struct icalrecurrencetype *)(rrule_list->data);
+
+ if (rt->freq == ICAL_DAILY_RECURRENCE)
+ flag32 = rectypeDaily;
+ else if (rt->freq == ICAL_WEEKLY_RECURRENCE)
+ flag32 = rectypeWeekly;
+ else if (rt->freq == ICAL_MONTHLY_RECURRENCE)
+ flag32 = rectypeMonthly;
+ else if (rt->freq == ICAL_YEARLY_RECURRENCE)
+ flag32 = rectypeYearly;
+ else
+ flag32 = rectypeNone;
+
+ e_cal_component_free_recur_list (rrule_list);
+ } else
+ flag32 = rectypeNone;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_RECURTYPE], (const void *) &flag32);
+
+ flag32 = cbdata->appt_id;
+ set_SPropValue_proptag(&props[i++], PR_OWNER_APPT_ID, (const void *) &flag32);
+
+ flag32 = cbdata->appt_seq;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_SEQ], (const void *) &flag32);
+
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_CLEANGUID], (const void *) cbdata->cleanglobalid);
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_GUID], (const void *) cbdata->globalid);
+
+ flag32 = cbdata->resp;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_RESPONSESTATUS], (const void *) &flag32);
+
+ switch (cbdata->meeting_type) {
+ case MEETING_OBJECT :
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_CLASS, (const void *) IPM_APPOINTMENT);
+
+ flag32 = e_cal_component_has_recurrences (comp) ? RecurMeet : SingleMeet;
+ set_SPropValue_proptag(&props[i++], PR_ICON_INDEX, (const void *) &flag32);
+
+ flag32 = 0x0171;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_SIDEEFFECTS], (const void *) &flag32);
+
+ flag32 = asfMeeting;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_STATEFLAGS], (const void *) &flag32);
+
+ flag32 = mtgRequest;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_TYPE], (const void *) &flag32);
+
+ b = 1;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_INVITED], (const void *) &b);
+
+ break;
+ case MEETING_OBJECT_RCVD :
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_CLASS, (const void *) IPM_APPOINTMENT);
+
+ flag32 = e_cal_component_has_recurrences (comp) ? RecurMeet : SingleMeet;
+ set_SPropValue_proptag(&props[i++], PR_ICON_INDEX, (const void *) &flag32);
+
+ flag32 = 0x0171;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_SIDEEFFECTS], (const void *) &flag32);
+
+ flag32 = asfMeeting | asfReceived;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_STATEFLAGS], (const void *) &flag32);
+
+ flag32 = mtgRequest;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_TYPE], (const void *) &flag32);
+
+ b = 1;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_INVITED], (const void *) &b);
+
+ break;
+ case MEETING_REQUEST :
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_CLASS, (const void *) IPM_SCHEDULE_MEETING_REQUEST);
+
+ flag32 = 0xFFFFFFFF; /* no idea why this has to be -1, but that's what the docs say */
+ set_SPropValue_proptag(&props[i++], PR_ICON_INDEX, (const void *) &flag32);
+
+ flag32 = 0x1C61;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_SIDEEFFECTS], (const void *) &flag32);
+
+ flag32 = asfMeeting | asfReceived;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_STATEFLAGS], (const void *) &flag32);
+
+ flag32 = (cbdata->appt_seq == 0) ? mtgRequest : mtgFull;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_TYPE], (const void *) &flag32);
+
+ b = 1;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_INVITED], (const void *) &b);
+
+ break;
+ case MEETING_REQUEST_RCVD :
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_CLASS, (const void *) IPM_APPOINTMENT);
+
+ flag32 = e_cal_component_has_recurrences (comp) ? RecurMeet : SingleMeet;
+ set_SPropValue_proptag(&props[i++], PR_ICON_INDEX, (const void *) &flag32);
+
+ flag32 = 0x0171;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_SIDEEFFECTS], (const void *) &flag32);
+
+ flag32 = asfMeeting | asfReceived;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_STATEFLAGS], (const void *) &flag32);
+
+ flag32 = mtgRequest;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_TYPE], (const void *) &flag32);
+
+ b = 1;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_INVITED], (const void *) &b);
+
+ break;
+ case MEETING_CANCEL :
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_CLASS, (const void *) IPM_SCHEDULE_MEETING_CANCELED);
+
+ flag32 = 0xFFFFFFFF; /* no idea why this has to be -1, but that's what the docs say */
+ set_SPropValue_proptag(&props[i++], PR_ICON_INDEX, (const void *) &flag32);
+
+ flag32 = 0x1C61;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_SIDEEFFECTS], (const void *) &flag32);
+
+ flag32 = asfMeeting | asfReceived | asfCanceled;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_STATEFLAGS], (const void *) &flag32);
+
+ flag32 = mtgEmpty;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_TYPE], (const void *) &flag32);
+
+ b = 1;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_INVITED], (const void *) &b);
+
+ break;
+ case MEETING_RESPONSE :
+ if (cbdata->resp == olResponseAccepted) {
+ text = IPM_SCHEDULE_MEETING_RESP_POS;
+ } else if (cbdata->resp == olResponseTentative) {
+ text = IPM_SCHEDULE_MEETING_RESP_TENT;
+ } else if (cbdata->resp == olResponseDeclined) {
+ text = IPM_SCHEDULE_MEETING_RESP_NEG;
+ } else {
+ text = "";
+ }
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_CLASS, (const void *) text);
+ text = NULL;
+
+ flag32 = 0xFFFFFFFF; /* no idea why this has to be -1, but that's what the docs say */
+ set_SPropValue_proptag(&props[i++], PR_ICON_INDEX, (const void *) &flag32);
+
+ flag32 = 0x1C61;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_SIDEEFFECTS], (const void *) &flag32);
+
+ flag32 = asfNone;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_STATEFLAGS], (const void *) &flag32);
+
+ flag32 = mtgEmpty;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_TYPE], (const void *) &flag32);
+
+ break;
+ case NOT_A_MEETING :
+ default :
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_CLASS, (const void *) IPM_APPOINTMENT);
+
+ flag32 = e_cal_component_has_recurrences (comp) ? RecurAppt : SingleAppt;
+ set_SPropValue_proptag(&props[i++], PR_ICON_INDEX, (const void *) &flag32);
+
+ flag32 = 0x0171;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_SIDEEFFECTS], (const void *) &flag32);
+
+ flag32 = 0;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_STATEFLAGS], (const void *) &flag32);
+
+ b = 0;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_INVITED], (const void *) &b);
+
+ break;
+ }
+
+ b = e_cal_component_has_recurrences (comp);
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_RECURRING], (const void *) &b);
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_ISRECURRING], (const void *) &b);
+ /* FIXME: Modified exceptions */
+ b = e_cal_component_has_exceptions (comp) && FALSE; b = 0;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_MEET_ISEXCEPTION], (const void *) &b);
+
+ /* Counter Proposal for appointments : not supported */
+ b = 1;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_NOTALLOWPROPOSE], (const void *) &b);
+ b = 0;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_APPT_ISCOUNTERPROPOSAL], (const void *) &b);
+
+ } else if (kind == ICAL_VTODO_COMPONENT) {
+ double d;
+
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_CLASS, (const void *) IPM_TASK);
+
+ /* Context menu flags */ /* FIXME: for assigned tasks */
+ flag32 = 0x0110;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_SIDEEFFECTS], (const void *) &flag32);
+
+ /* Status, Percent complete, IsComplete */
+ flag32 = olTaskNotStarted; /* default */
+ b = 0; /* default */
+ d = 0.0;
+ prop = icalcomponent_get_first_property (ical_comp, ICAL_PERCENTCOMPLETE_PROPERTY);
+ if (prop)
+ d = 0.01 * icalproperty_get_percentcomplete (prop);
+
+ flag32 = get_prop_from_taskstatus (icalcomponent_get_status (ical_comp));
+ if (flag32 == olTaskComplete) {
+ b = 1;
+ d = 1.0;
+ }
+
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_TASK_STATUS], (const void *) &flag32);
+
+ /* FIXME: bug in LibMAPI - does not handle PT_DOUBLE in set_SPropValue() */
+// set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_TASK_PERCENT], (const void *) &d);
+ props[i].ulPropTag = proptag_array->aulPropTag[I_TASK_PERCENT];
+ props[i].dwAlignPad = 0x0;
+ memcpy (&(props[i].value.dbl), &d, sizeof(double));
+ i++;
+
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_TASK_ISCOMPLETE], (const void *) &b);
+
+ /* Date completed */
+ if (b) {
+ struct icaltimetype completed;
+ prop = icalcomponent_get_first_property (ical_comp, ICAL_COMPLETED_PROPERTY);
+ completed = icalproperty_get_completed (prop);
+
+ completed.hour = completed.minute = completed.second = 0; completed.is_date = completed.is_utc = 1;
+ t.tv_sec = icaltime_as_timet (completed);
+ t.tv_usec = 0;
+ set_SPropValue_proptag_date_timeval(&props[i++], proptag_array->aulPropTag[I_TASK_COMPLETED], &t);
+ }
+
+ /* Start */
+ dtstart.hour = dtstart.minute = dtstart.second = 0; dtstart.is_date = dtstart.is_utc = 1;
+ t.tv_sec = icaltime_as_timet (dtstart);
+ t.tv_usec = 0;
+ if (!icaltime_is_null_time (dtstart))
+ set_SPropValue_proptag_date_timeval(&props[i++], proptag_array->aulPropTag[I_TASK_START], &t);
+
+ /* Due */
+ dtend.hour = dtend.minute = dtend.second = 0; dtend.is_date = dtend.is_utc = 1;
+ t.tv_sec = icaltime_as_timet (dtend);
+ t.tv_usec = 0;
+ if (!icaltime_is_null_time (dtend))
+ set_SPropValue_proptag_date_timeval(&props[i++], proptag_array->aulPropTag[I_TASK_DUE], &t);
+
+ /* FIXME: Evolution does not support recurring tasks */
+ b = 0;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_TASK_ISRECURRING], (const void *) &b);
+
+ } else if (kind == ICAL_VJOURNAL_COMPONENT) {
+ uint32_t color = olYellow;
+
+ set_SPropValue_proptag(&props[i++], PR_MESSAGE_CLASS, (const void *) IPM_STICKYNOTE);
+
+ /* Context menu flags */
+ flag32 = 0x0110;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_COMMON_SIDEEFFECTS], (const void *) &flag32);
+
+ flag32 = 0x0300 + color;
+ set_SPropValue_proptag(&props[i++], PR_ICON_INDEX, (const void *) &flag32);
+
+ flag32 = color;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_NOTE_COLOR], (const void *) &flag32);
+
+ /* some random value */
+ flag32 = 0x00FF;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_NOTE_WIDTH], (const void *) &flag32);
+
+ /* some random value */
+ flag32 = 0x00FF;
+ set_SPropValue_proptag(&props[i++], proptag_array->aulPropTag[I_NOTE_HEIGHT], (const void *) &flag32);
+ }
+
+ *value = props;
+ /* Free this memory at the backends. */
+ cbdata->props = props;
+
+ d(g_debug ("Ended up setting %d props ", i));
+
+ return i;
+}
+
+uint32_t
+exchange_mapi_cal_util_get_new_appt_id (mapi_id_t fid)
+{
+ struct mapi_SRestriction res;
+ struct SPropValue sprop;
+ uint32_t id;
+ gboolean found = FALSE;
+
+ res.rt = RES_PROPERTY;
+ res.res.resProperty.relop = RELOP_EQ;
+ res.res.resProperty.ulPropTag = PR_OWNER_APPT_ID;
+
+ while (!found) {
+ id = g_random_int ();
+ if (id) {
+ GSList *ids = NULL;
+ set_SPropValue_proptag (&sprop, PR_OWNER_APPT_ID, (const void *) &id);
+ cast_mapi_SPropValue (&(res.res.resProperty.lpProp), &sprop);
+ ids = exchange_mapi_util_check_restriction (fid, &res);
+ if (ids) {
+ GSList *l;
+ for (l = ids; l; l = l->next)
+ g_free (l->data);
+ } else
+ found = TRUE;
+ }
+ };
+
+ return id;
+}
+
Added: trunk/src/libexchangemapi/exchange-mapi-cal-utils.h
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-cal-utils.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,153 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EXCHANGE_MAPI_CAL_UTILS_H
+#define EXCHANGE_MAPI_CAL_UTILS_H
+
+#include <libecal/e-cal-component.h>
+
+#include "exchange-mapi-connection.h"
+#include "exchange-mapi-defs.h"
+#include "exchange-mapi-utils.h"
+
+#include "exchange-mapi-cal-tz-utils.h"
+#include "exchange-mapi-cal-recur-utils.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ NOT_A_MEETING = (1 << 0),
+ MEETING_OBJECT = (1 << 1),
+ MEETING_OBJECT_SENT = (1 << 2),
+ MEETING_OBJECT_RCVD = (1 << 3),
+ MEETING_REQUEST = (1 << 4),
+ MEETING_REQUEST_RCVD = (1 << 5),
+ MEETING_RESPONSE = (1 << 6),
+ MEETING_RESPONSE_RCVD = (1 << 7),
+ MEETING_CANCEL = (1 << 8),
+ MEETING_CANCEL_RCVD = (1 << 9)
+} MAPIMeetingOptions;
+
+struct cbdata {
+ ECalComponent *comp;
+ struct SPropValue *props;
+ gboolean is_modify;
+
+ /* These are appt specific data */
+ MAPIMeetingOptions meeting_type;
+ uint32_t appt_id;
+ uint32_t appt_seq;
+ struct SBinary *globalid;
+ struct SBinary *cleanglobalid;
+
+ uint32_t msgflags;
+ OlResponseStatus resp;
+ const char *username;
+ const char *useridtype;
+ const char *userid;
+ const char *ownername;
+ const char *owneridtype;
+ const char *ownerid;
+};
+
+void
+exchange_mapi_cal_util_fetch_organizer (ECalComponent *comp, GSList **recip_list);
+void
+exchange_mapi_cal_util_fetch_recipients (ECalComponent *comp, GSList **recip_list);
+void
+exchange_mapi_cal_util_fetch_attachments (ECalComponent *comp, GSList **attach_list, const char *local_store_uri);
+
+ECalComponent *
+exchange_mapi_cal_util_mapi_props_to_comp (icalcomponent_kind kind, const gchar *mid, struct mapi_SPropValue_array *properties,
+ GSList *streams, GSList *recipients, GSList *attachments,
+ const char *local_store_uri, const icaltimezone *default_zone);
+gboolean
+exchange_mapi_cal_util_build_name_id (struct mapi_nameid *nameid, gpointer data);
+
+int
+exchange_mapi_cal_util_build_props (struct SPropValue **value, struct SPropTagArray *proptag_array, gpointer data);
+
+void
+exchange_mapi_cal_util_generate_globalobjectid (gboolean is_clean, const char *uid, struct SBinary *sb);
+
+char *
+exchange_mapi_cal_util_camel_helper (struct mapi_SPropValue_array *properties,
+ GSList *streams, GSList *recipients, GSList *attachments);
+
+uint32_t
+exchange_mapi_cal_util_get_new_appt_id (mapi_id_t fid);
+
+static const uint32_t cal_GetPropsList[] = {
+ PR_FID,
+ PR_MID,
+
+ PR_SUBJECT,
+ PR_SUBJECT_UNICODE,
+ PR_NORMALIZED_SUBJECT,
+ PR_NORMALIZED_SUBJECT_UNICODE,
+ PR_CONVERSATION_TOPIC,
+ PR_CONVERSATION_TOPIC_UNICODE,
+ PR_BODY,
+ PR_BODY_UNICODE,
+
+ PR_CREATION_TIME,
+ PR_LAST_MODIFICATION_TIME,
+ PR_PRIORITY,
+ PR_SENSITIVITY,
+ PR_START_DATE,
+ PR_END_DATE,
+ PR_RESPONSE_REQUESTED,
+ PR_OWNER_APPT_ID,
+ PR_PROCESSED,
+ PR_MSG_EDITOR_FORMAT,
+
+ PR_SENT_REPRESENTING_NAME,
+ PR_SENT_REPRESENTING_NAME_UNICODE,
+ PR_SENT_REPRESENTING_ADDRTYPE,
+ PR_SENT_REPRESENTING_ADDRTYPE_UNICODE,
+ PR_SENT_REPRESENTING_EMAIL_ADDRESS,
+ PR_SENT_REPRESENTING_EMAIL_ADDRESS_UNICODE,
+
+ PR_SENDER_NAME,
+ PR_SENDER_NAME_UNICODE,
+ PR_SENDER_ADDRTYPE,
+ PR_SENDER_ADDRTYPE_UNICODE,
+ PR_SENDER_EMAIL_ADDRESS,
+ PR_SENDER_EMAIL_ADDRESS_UNICODE,
+
+ PR_RCVD_REPRESENTING_NAME,
+ PR_RCVD_REPRESENTING_NAME_UNICODE,
+ PR_RCVD_REPRESENTING_ADDRTYPE,
+ PR_RCVD_REPRESENTING_ADDRTYPE_UNICODE,
+ PR_RCVD_REPRESENTING_EMAIL_ADDRESS,
+ PR_RCVD_REPRESENTING_EMAIL_ADDRESS_UNICODE
+};
+
+static const uint32_t cal_IDList[] = {
+ PR_FID,
+ PR_MID
+};
+
+G_END_DECLS
+
+#endif
Added: trunk/src/libexchangemapi/exchange-mapi-connection.c
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-connection.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,2660 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan novell com>
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "exchange-mapi-connection.h"
+#include "exchange-mapi-folder.h"
+#include "exchange-mapi-utils.h"
+#include <param.h>
+
+#define DEFAULT_PROF_PATH ".evolution/mapi-profiles.ldb"
+#define d(x) x
+
+static struct mapi_session *global_mapi_session= NULL;
+static GStaticRecMutex connect_lock = G_STATIC_REC_MUTEX_INIT;
+
+
+#define LOCK() g_message("%s(%d): %s: lock(connect_lock)", __FILE__, __LINE__, __PRETTY_FUNCTION__);g_static_rec_mutex_lock(&connect_lock);
+#define UNLOCK() g_message("%s(%d): %s: unlock(connect_lock)", __FILE__, __LINE__, __PRETTY_FUNCTION__);g_static_rec_mutex_unlock(&connect_lock);
+
+#if 0
+#define LOGALL() lp_set_cmdline(global_mapi_ctx->lp_ctx, "log level", "10"); global_mapi_ctx->dumpdata = TRUE;
+#define LOGNONE() lp_set_cmdline(global_mapi_ctx->lp_ctx, "log level", "0"); global_mapi_ctx->dumpdata = FALSE;
+
+#define ENABLE_VERBOSE_LOG() global_mapi_ctx->dumpdata = TRUE;
+#define DISABLE_VERBOSE_LOG() global_mapi_ctx->dumpdata = FALSE;
+#endif
+
+//#if 0
+#define LOGALL()
+#define LOGNONE()
+
+#define ENABLE_VERBOSE_LOG()
+#define DISABLE_VERBOSE_LOG()
+//#endif
+
+/* Specifies READ/WRITE sizes to be used while handling normal streams */
+#define STREAM_MAX_READ_SIZE 0x1000
+#define STREAM_MAX_WRITE_SIZE 0x1000
+#define STREAM_ACCESS_READ 0x0000
+#define STREAM_ACCESS_WRITE 0x0001
+#define STREAM_ACCESS_READWRITE 0x0002
+
+static struct mapi_session *
+mapi_profile_load (const char *profname, const char *password)
+{
+ enum MAPISTATUS retval = MAPI_E_SUCCESS;
+ struct mapi_session *session = NULL;
+ gchar *profpath = NULL;
+ const char *profile = NULL;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ profpath = g_build_filename (g_get_home_dir(), DEFAULT_PROF_PATH, NULL);
+ if (!g_file_test (profpath, G_FILE_TEST_EXISTS)) {
+ g_warning ("\nMAPI profile database @ %s not found ", profpath);
+ goto cleanup;
+ }
+
+ MAPIUninitialize ();
+
+ retval = MAPIInitialize(profpath);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("MAPIInitialize", GetLastError());
+ if (retval == MAPI_E_SESSION_LIMIT)
+ g_print("\n%s(%d): %s: Already connected ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ goto cleanup;
+ }
+
+ if (profname)
+ profile = profname;
+ else {
+ retval = GetDefaultProfile(&profile);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetDefaultProfile", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ g_print("\nLoading profile %s ", profile);
+
+ retval = MapiLogonEx(&session, profile, password);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("MapiLogonEx", GetLastError());
+ goto cleanup;
+ }
+
+cleanup:
+ if (retval != MAPI_E_SUCCESS && retval != MAPI_E_SESSION_LIMIT)
+ MAPIUninitialize ();
+ g_free (profpath);
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return session;
+}
+
+gboolean
+exchange_mapi_connection_exists ()
+{
+ return global_mapi_session != NULL;
+}
+
+gboolean
+exchange_mapi_connection_new (const char *profile, const char *password)
+{
+ LOCK();
+ if (!global_mapi_session)
+ global_mapi_session = mapi_profile_load (profile, password);
+ UNLOCK();
+
+ if (!global_mapi_session)
+ g_warning ("\n%s(%d): %s: Login failed ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ else
+ g_message ("\n%s(%d): %s: Connected ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+
+ return global_mapi_session != NULL;
+}
+
+void
+exchange_mapi_connection_close ()
+{
+ LOCK();
+ global_mapi_session = NULL;
+ MAPIUninitialize ();
+ UNLOCK();
+ /* TODO : Return status. get last error ? */
+}
+
+static gboolean
+exchange_mapi_util_read_generic_stream (mapi_object_t *obj_message, uint32_t proptag, GSList **stream_list)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_stream;
+ uint16_t cn_read = 0;
+ uint32_t off_data = 0;
+ uint8_t *buf_data = NULL;
+ uint32_t buf_size = 0;
+ gboolean done = FALSE;
+
+ /* sanity */
+ g_return_val_if_fail (obj_message, FALSE);
+ g_return_val_if_fail (((proptag & 0xFFFF) == PT_BINARY), FALSE);
+
+ /* if compressed RTF stream, then return */
+ g_return_val_if_fail (proptag != PR_RTF_COMPRESSED, FALSE);
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+ d(g_print("\nAttempt to read stream for proptag 0x%08X ", proptag));
+
+ mem_ctx = talloc_init ("ExchangeMAPI_ReadGenericStream");
+ mapi_object_init(&obj_stream);
+
+ /* get a stream on specified proptag */
+ retval = OpenStream(obj_message, proptag, STREAM_ACCESS_READ, &obj_stream);
+ if (retval != MAPI_E_SUCCESS) {
+ /* If OpenStream failed, should we attempt any other call(s) to fetch the blob? */
+ mapi_errstr("OpenStream", GetLastError());
+ goto cleanup;
+ }
+
+ /* NOTE: This may prove unreliable for streams larger than 4GB length */
+ retval = GetStreamSize(&obj_stream, &buf_size);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetStreamSize", GetLastError());
+ goto cleanup;
+ }
+
+ buf_data = talloc_size (mem_ctx, buf_size);
+ if (!buf_data)
+ goto cleanup;
+
+ /* Read from the stream */
+ while (!done) {
+ retval = ReadStream(&obj_stream,
+ (buf_data) + off_data,
+ STREAM_MAX_READ_SIZE,
+ &cn_read);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("ReadStream", GetLastError());
+ done = TRUE;
+ } else if (cn_read == 0) {
+ done = TRUE;
+ } else {
+ off_data += cn_read;
+ if (off_data >= buf_size)
+ done = TRUE;
+ }
+ };
+
+ if (retval == MAPI_E_SUCCESS) {
+ ExchangeMAPIStream *stream = g_new0 (ExchangeMAPIStream, 1);
+ struct mapi_SPropValue_array properties_array;
+
+ stream->value = g_byte_array_sized_new (off_data);
+ stream->value = g_byte_array_append (stream->value, buf_data, off_data);
+
+ /* Build a mapi_SPropValue_array structure */
+ properties_array.cValues = 1;
+ properties_array.lpProps = talloc_array (mem_ctx, struct mapi_SPropValue, properties_array.cValues);
+ properties_array.lpProps[0].ulPropTag = proptag;
+ /* This call is needed in case the read stream was a named prop. */
+ mapi_SPropValue_array_named (obj_message, &properties_array);
+
+ stream->proptag = properties_array.lpProps[0].ulPropTag;
+
+ d(g_print("\nAttempt succeeded for proptag 0x%08X (after name conversion) ", stream->proptag));
+
+ *stream_list = g_slist_append (*stream_list, stream);
+ }
+
+cleanup:
+ mapi_object_release(&obj_stream);
+ talloc_free (mem_ctx);
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return (retval == MAPI_E_SUCCESS);
+}
+
+static gboolean
+exchange_mapi_util_read_body_stream (mapi_object_t *obj_message, GSList **stream_list, gboolean getbestbody)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ struct SPropTagArray *SPropTagArray;
+ struct SPropValue *lpProps;
+ uint32_t count;
+ DATA_BLOB body;
+ uint8_t editor;
+ const char *data = NULL;
+ const bool *rtf_in_sync;
+ uint32_t proptag = 0;
+
+ /* sanity check */
+ g_return_val_if_fail (obj_message, FALSE);
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ mem_ctx = talloc_init ("ExchangeMAPI_ReadBodyStream");
+
+ /* Build the array of properties we want to fetch */
+ SPropTagArray = set_SPropTagArray(mem_ctx, 0x6,
+ PR_MSG_EDITOR_FORMAT,
+ PR_BODY,
+ PR_BODY_UNICODE,
+ PR_HTML,
+ PR_RTF_COMPRESSED,
+ PR_RTF_IN_SYNC);
+
+ lpProps = talloc_zero(mem_ctx, struct SPropValue);
+ retval = GetProps(obj_message, SPropTagArray, &lpProps, &count);
+ MAPIFreeBuffer(SPropTagArray);
+
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetProps", GetLastError());
+ return FALSE;
+ }
+
+ if (getbestbody) {
+ /* Use BestBody Algo */
+ retval = GetBestBody(obj_message, &editor);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetBestBody", GetLastError());
+ /* On failure, fallback to Plain Text */
+ editor = olEditorText;
+ }
+
+ /* HACK : We can't handle RTF. So default to HTML */
+ if (editor != olEditorText && editor != olEditorHTML)
+ editor = olEditorHTML;
+ } else {
+ const uint32_t *ui32 = (const uint32_t *) get_SPropValue(lpProps, PR_MSG_EDITOR_FORMAT);
+ /* if PR_MSG_EDITOR_FORMAT doesn't exist, set it to PLAINTEXT */
+ editor = ui32 ? *ui32 : olEditorText;
+ }
+
+ /* initialize body DATA_BLOB */
+ body.data = NULL;
+ body.length = 0;
+
+ retval = -1;
+ switch (editor) {
+ case olEditorText:
+ if ((data = (const char *) get_SPropValue (lpProps, PR_BODY_UNICODE)) != NULL)
+ proptag = PR_BODY_UNICODE;
+ else if ((data = (const char *) get_SPropValue (lpProps, PR_BODY)) != NULL)
+ proptag = PR_BODY;
+ if (data) {
+ size_t size = strlen(data)+1;
+ body.data = talloc_memdup(mem_ctx, data, size);
+ body.length = size;
+ retval = MAPI_E_SUCCESS;
+ }
+ break;
+ case olEditorHTML:
+ /* Fixme : */
+ /*if ((data = (const char *) get_SPropValue (lpProps, PR_BODY_HTML_UNICODE)) != NULL) */
+ /* proptag = PR_BODY_HTML_UNICODE; */
+ if ((data = (const char *) get_SPropValue (lpProps, PR_BODY_HTML)) != NULL)
+ proptag = PR_BODY_HTML;
+
+ if (data) {
+ size_t size = strlen(data)+1;
+ body.data = talloc_memdup(mem_ctx, data, size);
+ body.length = size;
+ retval = MAPI_E_SUCCESS;
+ } else if (exchange_mapi_util_read_generic_stream (obj_message, PR_HTML, stream_list)) {
+ retval = MAPI_E_SUCCESS;
+ }
+ break;
+ case olEditorRTF:
+ rtf_in_sync = (const bool *) get_SPropValue (lpProps, PR_RTF_IN_SYNC);
+// if (!(rtf_in_sync && *rtf_in_sync))
+ {
+ mapi_object_t obj_stream;
+
+ mapi_object_init(&obj_stream);
+
+ retval = OpenStream(obj_message, PR_RTF_COMPRESSED, STREAM_ACCESS_READ, &obj_stream);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenStream", GetLastError());
+ mapi_object_release(&obj_stream);
+ break;
+ }
+
+ retval = WrapCompressedRTFStream(&obj_stream, &body);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("WrapCompressedRTFStream", GetLastError());
+ mapi_object_release(&obj_stream);
+ break;
+ }
+
+ proptag = PR_RTF_COMPRESSED;
+
+ mapi_object_release(&obj_stream);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (retval == MAPI_E_SUCCESS && proptag) {
+ ExchangeMAPIStream *stream = g_new0 (ExchangeMAPIStream, 1);
+
+ stream->value = g_byte_array_sized_new (body.length);
+ stream->value = g_byte_array_append (stream->value, body.data, body.length);
+
+ stream->proptag = proptag;
+
+ *stream_list = g_slist_append (*stream_list, stream);
+ }
+
+ talloc_free (mem_ctx);
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return (retval == MAPI_E_SUCCESS);
+}
+
+/* Returns TRUE if all streams were written succcesfully, else returns FALSE */
+static gboolean
+exchange_mapi_util_write_generic_streams (mapi_object_t *obj_message, GSList *stream_list)
+{
+
+// TALLOC_CTX *mem_ctx;
+ GSList *l;
+ enum MAPISTATUS retval;
+ gboolean status = TRUE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+// mem_ctx = talloc_init ("ExchangeMAPI_WriteGenericStreams");
+
+ for (l = stream_list; l; l = l->next) {
+ ExchangeMAPIStream *stream = (ExchangeMAPIStream *) (l->data);
+ uint32_t total_written;
+ gboolean done = FALSE;
+ mapi_object_t obj_stream;
+
+ mapi_object_init(&obj_stream);
+
+ /* OpenStream on required proptag */
+ retval = OpenStream(obj_message, stream->proptag, STREAM_ACCESS_READWRITE, &obj_stream);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenStream", GetLastError());
+ goto cleanup;
+ }
+
+ /* Set the stream size */
+ retval = SetStreamSize(&obj_stream, stream->value->len);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetStreamSize", GetLastError());
+ goto cleanup;
+ }
+
+ total_written = 0;
+ /* Write attachment */
+ while (!done) {
+ uint16_t cn_written = 0;
+ DATA_BLOB blob;
+
+ blob.length = (stream->value->len - total_written) < STREAM_MAX_WRITE_SIZE ?
+ (stream->value->len - total_written) : STREAM_MAX_WRITE_SIZE;
+ blob.data = (stream->value->data) + total_written;
+
+ retval = WriteStream(&obj_stream,
+ &blob,
+ &cn_written);
+
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("WriteStream", GetLastError());
+ done = TRUE;
+ } else if (cn_written == 0) {
+ done = TRUE;
+ } else {
+ total_written += cn_written;
+ if (total_written >= stream->value->len)
+ done = TRUE;
+ }
+ }
+
+ /* Commit the stream */
+ retval = CommitStream(&obj_stream);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("CommitStream", GetLastError());
+ goto cleanup;
+ }
+
+ cleanup:
+ if (retval != MAPI_E_SUCCESS)
+ status = FALSE;
+ mapi_object_release(&obj_stream);
+ }
+
+// talloc_free (mem_ctx);
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return status;
+}
+
+static gboolean
+exchange_mapi_util_delete_attachments (mapi_object_t *obj_message)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_tb_attach;
+ struct SPropTagArray *proptags;
+ struct SRowSet rows_attach;
+ uint32_t attach_count;
+ uint32_t i_row_attach;
+ gboolean status = TRUE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ mem_ctx = talloc_init ("ExchangeMAPI_DeleteAttachments");
+
+ proptags = set_SPropTagArray(mem_ctx, 0x4,
+ PR_ATTACH_NUM,
+ PR_INSTANCE_KEY,
+ PR_RECORD_KEY,
+ PR_RENDERING_POSITION);
+
+ mapi_object_init(&obj_tb_attach);
+
+ /* open attachment table */
+ retval = GetAttachmentTable(obj_message, &obj_tb_attach);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetAttachmentTable", GetLastError());
+ goto cleanup;
+ }
+
+ retval = SetColumns(&obj_tb_attach, proptags);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetColumns", GetLastError());
+ goto cleanup;
+ }
+
+ retval = GetRowCount(&obj_tb_attach, &attach_count);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetRowCount", GetLastError());
+ goto cleanup;
+ }
+
+ retval = QueryRows(&obj_tb_attach, attach_count, TBL_ADVANCE, &rows_attach);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("QueryRows", GetLastError());
+ goto cleanup;
+ }
+
+ /* foreach attachment, delete by PR_ATTACH_NUM */
+ for (i_row_attach = 0; i_row_attach < rows_attach.cRows; i_row_attach++) {
+ const uint32_t *num_attach;
+
+ num_attach = (const uint32_t *) get_SPropValue_SRow_data(&rows_attach.aRow[i_row_attach], PR_ATTACH_NUM);
+
+ retval = DeleteAttach(obj_message, *num_attach);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("DeleteAttach", GetLastError());
+ status = FALSE;
+ }
+ }
+
+cleanup:
+ if (retval != MAPI_E_SUCCESS)
+ status = FALSE;
+ mapi_object_release(&obj_tb_attach);
+ talloc_free (mem_ctx);
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return status;
+}
+
+/* Returns TRUE if all attachments were written succcesfully, else returns FALSE */
+static gboolean
+exchange_mapi_util_set_attachments (mapi_object_t *obj_message, GSList *attach_list, gboolean remove_existing)
+{
+// TALLOC_CTX *mem_ctx;
+ GSList *l;
+ enum MAPISTATUS retval;
+ gboolean status = TRUE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ if (remove_existing)
+ exchange_mapi_util_delete_attachments (obj_message);
+
+// mem_ctx = talloc_init ("ExchangeMAPI_SetAttachments");
+
+ for (l = attach_list; l; l = l->next) {
+ ExchangeMAPIAttachment *attachment = (ExchangeMAPIAttachment *) (l->data);
+ mapi_object_t obj_attach;
+
+ mapi_object_init(&obj_attach);
+
+ /* CreateAttach */
+ retval = CreateAttach(obj_message, &obj_attach);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("CreateAttach", GetLastError());
+ goto cleanup;
+ }
+
+ /* SetProps */
+ retval = SetProps(&obj_attach, attachment->lpProps, attachment->cValues);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetProps", GetLastError());
+ goto cleanup;
+ }
+
+ /* If there are any streams to be set, write them. */
+ exchange_mapi_util_write_generic_streams (&obj_attach, attachment->streams);
+
+ /* message->SaveChangesAttachment() */
+ retval = SaveChangesAttachment(obj_message, &obj_attach, KEEP_OPEN_READWRITE);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SaveChangesAttachment", GetLastError());
+ goto cleanup;
+ }
+
+ cleanup:
+ if (retval != MAPI_E_SUCCESS)
+ status = FALSE;
+ mapi_object_release(&obj_attach);
+ }
+
+// talloc_free (mem_ctx);
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return status;
+}
+
+/* Returns TRUE if all attachments were read succcesfully, else returns FALSE */
+static gboolean
+exchange_mapi_util_get_attachments (mapi_object_t *obj_message, GSList **attach_list)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_tb_attach;
+ struct SPropTagArray *proptags;
+ struct SRowSet rows_attach;
+ uint32_t attach_count;
+ uint32_t i_row_attach;
+ gboolean status = TRUE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ mem_ctx = talloc_init ("ExchangeMAPI_GetAttachments");
+
+ proptags = set_SPropTagArray(mem_ctx, 0x5,
+ PR_ATTACH_NUM,
+ PR_INSTANCE_KEY,
+ PR_RECORD_KEY,
+ PR_RENDERING_POSITION,
+ PR_ATTACH_METHOD);
+
+ mapi_object_init(&obj_tb_attach);
+
+ /* open attachment table */
+ retval = GetAttachmentTable(obj_message, &obj_tb_attach);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetAttachmentTable", GetLastError());
+ goto cleanup;
+ }
+
+ retval = SetColumns(&obj_tb_attach, proptags);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetColumns", GetLastError());
+ goto cleanup;
+ }
+
+ retval = GetRowCount(&obj_tb_attach, &attach_count);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetRowCount", GetLastError());
+ goto cleanup;
+ }
+
+ retval = QueryRows(&obj_tb_attach, attach_count, TBL_ADVANCE, &rows_attach);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("QueryRows", GetLastError());
+ goto cleanup;
+ }
+
+ /* foreach attachment, open by PR_ATTACH_NUM */
+ for (i_row_attach = 0; i_row_attach < rows_attach.cRows; i_row_attach++) {
+ ExchangeMAPIAttachment *attachment;
+ struct mapi_SPropValue_array properties;
+ const uint32_t *ui32;
+ mapi_object_t obj_attach;
+ uint32_t z;
+
+ mapi_object_init(&obj_attach);
+
+ ui32 = (const uint32_t *) get_SPropValue_SRow_data(&rows_attach.aRow[i_row_attach], PR_ATTACH_NUM);
+
+ retval = OpenAttach(obj_message, *ui32, &obj_attach);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenAttach", GetLastError());
+ goto loop_cleanup;
+ }
+
+ retval = GetPropsAll (&obj_attach, &properties);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetPropsAll", GetLastError());
+ goto loop_cleanup;
+ }
+
+ attachment = g_new0 (ExchangeMAPIAttachment, 1);
+ attachment->cValues = properties.cValues;
+ attachment->lpProps = g_new0 (struct SPropValue, attachment->cValues);
+ for (z=0; z < properties.cValues; z++)
+ cast_SPropValue (&properties.lpProps[z], &(attachment->lpProps[z]));
+
+ /* just to get all the other streams */
+ for (z=0; z < properties.cValues; z++) {
+ if ((properties.lpProps[z].ulPropTag & 0xFFFF) == PT_BINARY)
+ exchange_mapi_util_read_generic_stream (&obj_attach, properties.lpProps[z].ulPropTag, &(attachment->streams));
+ }
+
+ /* HACK */
+ ui32 = (const uint32_t *) get_SPropValue_SRow_data(&rows_attach.aRow[i_row_attach], PR_ATTACH_METHOD);
+ if (ui32 && *ui32 == ATTACH_BY_VALUE)
+ exchange_mapi_util_read_generic_stream (&obj_attach, PR_ATTACH_DATA_BIN, &(attachment->streams));
+
+ *attach_list = g_slist_append (*attach_list, attachment);
+
+ loop_cleanup:
+ if (retval != MAPI_E_SUCCESS)
+ status = FALSE;
+ mapi_object_release(&obj_attach);
+ }
+
+cleanup:
+ if (retval != MAPI_E_SUCCESS)
+ status = FALSE;
+ mapi_object_release(&obj_tb_attach);
+ talloc_free (mem_ctx);
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return status;
+}
+
+/* Returns TRUE if all recipients were read succcesfully, else returns FALSE */
+static gboolean
+exchange_mapi_util_get_recipients (mapi_object_t *obj_message, GSList **recip_list)
+{
+ enum MAPISTATUS retval;
+// TALLOC_CTX *mem_ctx;
+ struct SPropTagArray proptags;
+ struct SRowSet rows_recip;
+ uint32_t i_row_recip;
+ gboolean status = TRUE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+// mem_ctx = talloc_init ("ExchangeMAPI_GetRecipients");
+
+ /* fetch recipient table */
+ retval = GetRecipientTable(obj_message, &rows_recip, &proptags);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetRecipientTable", GetLastError());
+ goto cleanup;
+ }
+
+ for (i_row_recip = 0; i_row_recip < rows_recip.cRows; i_row_recip++) {
+ ExchangeMAPIRecipient *recipient = g_new0 (ExchangeMAPIRecipient, 1);
+
+ recipient->email_id = (const char *) exchange_mapi_util_find_row_propval (&(rows_recip.aRow[i_row_recip]), PR_SMTP_ADDRESS);
+ /* fallback */
+ if (!recipient->email_id) {
+ const char *addrtype = (const char *) exchange_mapi_util_find_row_propval (&(rows_recip.aRow[i_row_recip]), PR_ADDRTYPE);
+ if (addrtype && !g_ascii_strcasecmp(addrtype, "SMTP"))
+ recipient->email_id = (const char *) exchange_mapi_util_find_row_propval (&(rows_recip.aRow[i_row_recip]), PR_EMAIL_ADDRESS);
+ }
+ /* fail */
+ if (!recipient->email_id) {
+ g_warning ("\n%s:%d %s() - object has a recipient without a PR_SMTP_ADDRESS ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ mapidump_SRow (&(rows_recip.aRow[i_row_recip]), " ");
+ }
+
+ recipient->out.all_lpProps = rows_recip.aRow[i_row_recip].lpProps;
+ recipient->out.all_cValues = rows_recip.aRow[i_row_recip].cValues;
+
+ *recip_list = g_slist_append (*recip_list, recipient);
+ }
+
+cleanup:
+ if (retval != MAPI_E_SUCCESS)
+ status = FALSE;
+// talloc_free (mem_ctx);
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return status;
+}
+
+static void
+set_recipient_properties (TALLOC_CTX *mem_ctx, struct SRow *aRow, ExchangeMAPIRecipient *recipient, gboolean is_external)
+{
+ uint32_t i;
+
+ if (is_external && recipient->in.ext_lpProps) {
+ struct SBinary *oneoff_eid;
+ struct SPropValue sprop;
+ const gchar *dn = NULL, *email = NULL;
+
+ for (i = 0; i < recipient->in.ext_cValues; ++i)
+ SRow_addprop (aRow, recipient->in.ext_lpProps[i]);
+
+ dn = (const gchar *) get_SPropValue (recipient->in.ext_lpProps, PR_DISPLAY_NAME);
+ if (!dn)
+ dn = "";
+ email = (const gchar *) get_SPropValue (recipient->in.ext_lpProps, PR_SMTP_ADDRESS);
+ if (!email)
+ email = "";
+ oneoff_eid = exchange_mapi_util_entryid_generate_oneoff (mem_ctx, dn, email, FALSE);
+ set_SPropValue_proptag (&sprop, PR_ENTRYID, (const void *)(oneoff_eid));
+ SRow_addprop (aRow, sprop);
+ }
+
+ for (i = 0; i < recipient->in.req_cValues; ++i)
+ SRow_addprop (aRow, recipient->in.req_lpProps[i]);
+}
+
+/* DON'T f***ing touch this function. */
+static void
+exchange_mapi_util_modify_recipients (TALLOC_CTX *mem_ctx, mapi_object_t *obj_message , GSList *recipients, gboolean remove_existing)
+{
+ enum MAPISTATUS retval;
+ struct SPropTagArray *SPropTagArray = NULL;
+ struct SRowSet *SRowSet = NULL;
+ struct FlagList *FlagList = NULL;
+ GSList *l;
+ const char **users = NULL;
+ uint32_t i, j, count = 0;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ SPropTagArray = set_SPropTagArray(mem_ctx, 0x7,
+ PR_DISPLAY_TYPE,
+ PR_OBJECT_TYPE,
+// PR_ADDRTYPE,
+// PR_EMAIL_ADDRESS,
+ PR_SMTP_ADDRESS,
+ PR_DISPLAY_NAME,
+ PR_GIVEN_NAME,
+ PR_SURNAME,
+ PR_7BIT_DISPLAY_NAME);
+// PR_ENTRYID,
+// PR_SEARCH_KEY,
+// PR_TRANSMITTABLE_DISPLAY_NAME);
+
+ SRowSet = talloc_zero(mem_ctx, struct SRowSet);
+ FlagList = talloc_zero(mem_ctx, struct FlagList);
+ count = g_slist_length (recipients);
+ users = g_new0 (const char *, count + 1);
+
+ for (i = 0, l = recipients; (i < count && l != NULL); ++i, l = l->next) {
+ ExchangeMAPIRecipient *recipient = (ExchangeMAPIRecipient *)(l->data);
+ users[i] = recipient->email_id;
+ }
+
+ /* Attempt to resolve names from the server */
+ retval = ResolveNames (users, SPropTagArray, &SRowSet, &FlagList, 0);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("ResolveNames", GetLastError());
+ goto cleanup;
+ }
+
+ g_assert (count == FlagList->cFlags);
+
+ for (i = 0, l = recipients, j = 0; (i < count && l != NULL); ++i, l = l->next) {
+ ExchangeMAPIRecipient *recipient = (ExchangeMAPIRecipient *)(l->data);
+ uint32_t last;
+
+ switch (FlagList->ulFlags[i]) {
+ case MAPI_AMBIGUOUS:
+ /* We should never get an ambiguous resolution as we use the email-id for resolving.
+ * However, if we do still get an ambiguous entry, we can't handle it :-( */
+ g_warning ("\n%s:%d %s() - '%s' is ambiguous ", __FILE__, __LINE__, __PRETTY_FUNCTION__, recipient->email_id);
+ break;
+ case MAPI_UNRESOLVED:
+ /* This is currently a bug in libmapi that unresolved recipients are not added to the SRowSet.
+ * Julien knows about it and would fix it. */
+ SRowSet->aRow = talloc_realloc(mem_ctx, SRowSet->aRow, struct SRow, SRowSet->cRows + 1);
+ last = SRowSet->cRows;
+ SRowSet->aRow[last].cValues = 0;
+ SRowSet->aRow[last].lpProps = talloc_zero(mem_ctx, struct SPropValue);
+ set_recipient_properties (mem_ctx, &SRowSet->aRow[last], recipient, TRUE);
+ SRowSet->cRows += 1;
+ break;
+ case MAPI_RESOLVED:
+ set_recipient_properties (mem_ctx, &SRowSet->aRow[j], recipient, FALSE);
+ j += 1;
+ break;
+ }
+ }
+
+ if (remove_existing) {
+ RemoveAllRecipients (obj_message);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("RemoveAllRecipients", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Modify the recipient table */
+ retval = ModifyRecipients (obj_message, SRowSet);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("ModifyRecpients", GetLastError());
+ goto cleanup;
+ }
+
+cleanup:
+ g_free (users);
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+}
+
+GSList *
+exchange_mapi_util_check_restriction (mapi_id_t fid, struct mapi_SRestriction *res)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ mapi_object_t obj_table;
+ struct SPropTagArray *SPropTagArray, *GetPropsTagArray;
+ struct SRowSet SRowSet;
+ uint32_t count, i;
+ GSList *mids = NULL;
+
+ d(g_print("\n%s(%d): Entering %s: folder-id %016llX ", __FILE__, __LINE__, __PRETTY_FUNCTION__, fid));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_CheckRestriction");
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+ mapi_object_init(&obj_table);
+
+ /* Open the message store */
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* Attempt to open the folder */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Get a handle on the container */
+ retval = GetContentsTable(&obj_folder, &obj_table, 0, NULL);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetContentsTable", GetLastError());
+ goto cleanup;
+ }
+
+ GetPropsTagArray = talloc_zero(mem_ctx, struct SPropTagArray);
+ GetPropsTagArray->cValues = 0;
+
+ // FIXME : Why are we fetching all these props ?
+
+ SPropTagArray = set_SPropTagArray(mem_ctx, 0xA,
+ PR_FID,
+ PR_MID,
+ PR_INST_ID,
+ PR_INSTANCE_NUM,
+ PR_SUBJECT,
+ PR_MESSAGE_CLASS,
+ PR_LAST_MODIFICATION_TIME,
+ PR_HASATTACH,
+ PR_RULE_MSG_PROVIDER,
+ PR_RULE_MSG_NAME);
+
+ /* Set primary columns to be fetched */
+ retval = SetColumns(&obj_table, SPropTagArray);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetColumns", GetLastError());
+ goto cleanup;
+ }
+
+ if (res) {
+ /* Applying any restriction that are set. */
+ retval = Restrict(&obj_table, res);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("Restrict", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Number of items in the container */
+ retval = GetRowCount(&obj_table, &count);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetRowCount", GetLastError());
+ goto cleanup;
+ }
+
+ /* Fill the table columns with data from the rows */
+ retval = QueryRows(&obj_table, count, TBL_ADVANCE, &SRowSet);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("QueryRows", GetLastError());
+ goto cleanup;
+ }
+
+ for (i = 0; i < SRowSet.cRows; i++) {
+ mapi_id_t *pmid = (mapi_id_t *) get_SPropValue_SRow_data(&SRowSet.aRow[i], PR_MID);
+ struct id_list *id_list = g_new0 (struct id_list, 1);
+ id_list->id = *pmid;
+ mids = g_slist_prepend (mids, id_list);
+ }
+
+cleanup:
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_table);
+ mapi_object_release(&obj_store);
+ talloc_free (mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return mids;
+}
+
+gboolean
+exchange_mapi_connection_fetch_items (mapi_id_t fid,
+ struct mapi_SRestriction *res,
+ const uint32_t *GetPropsList, const uint16_t cn_props,
+ BuildNameID build_name_id, gpointer build_name_data,
+ FetchCallback cb, gpointer data,
+ guint32 options)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ mapi_object_t obj_table;
+ struct SPropTagArray *SPropTagArray, *GetPropsTagArray;
+ struct SRowSet SRowSet;
+ uint32_t count, i;
+ gboolean result = FALSE;
+
+ d(g_print("\n%s(%d): Entering %s: folder-id %016llX ", __FILE__, __LINE__, __PRETTY_FUNCTION__, fid));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_FetchItems");
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+ mapi_object_init(&obj_table);
+
+ /* Open the message store */
+ retval = ((options & MAPI_OPTIONS_USE_PFSTORE) ? OpenPublicFolder(&obj_store) : OpenMsgStore(&obj_store));
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore / OpenPublicFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Attempt to open the folder */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Get a handle on the container */
+ retval = GetContentsTable(&obj_folder, &obj_table, 0, NULL);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetContentsTable", GetLastError());
+ goto cleanup;
+ }
+
+ GetPropsTagArray = talloc_zero(mem_ctx, struct SPropTagArray);
+ GetPropsTagArray->cValues = 0;
+
+ SPropTagArray = set_SPropTagArray(mem_ctx, 0x4,
+ PR_FID,
+ PR_MID,
+ PR_LAST_MODIFICATION_TIME,
+ PR_HASATTACH);
+
+ /* Set primary columns to be fetched */
+ retval = SetColumns(&obj_table, SPropTagArray);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetColumns", GetLastError());
+ goto cleanup;
+ }
+
+ if (res) {
+ /* Applying any restriction that are set. */
+ retval = Restrict(&obj_table, res);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("Restrict", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Number of items in the container */
+ retval = GetRowCount(&obj_table, &count);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetRowCount", GetLastError());
+ goto cleanup;
+ }
+
+ /* Fill the table columns with data from the rows */
+ retval = QueryRows(&obj_table, count, TBL_ADVANCE, &SRowSet);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("QueryRows", GetLastError());
+ goto cleanup;
+ }
+
+ if ((GetPropsList && (cn_props > 0)) || build_name_id) {
+ struct SPropTagArray *NamedPropsTagArray;
+ uint32_t m, n=0;
+ struct mapi_nameid *nameid;
+
+ nameid = mapi_nameid_new(mem_ctx);
+ NamedPropsTagArray = talloc_zero(mem_ctx, struct SPropTagArray);
+
+ NamedPropsTagArray->cValues = 0;
+ /* Add named props using callback */
+ if (build_name_id) {
+ if (!build_name_id (nameid, build_name_data)) {
+ g_warning ("\n%s(%d): (%s): Could not build named props ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ goto GetProps_cleanup;
+ }
+
+ retval = mapi_nameid_GetIDsFromNames(nameid, &obj_folder, NamedPropsTagArray);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("mapi_nameid_GetIDsFromNames", GetLastError());
+ goto GetProps_cleanup;
+ }
+ }
+
+ GetPropsTagArray->cValues = (cn_props + NamedPropsTagArray->cValues);
+ GetPropsTagArray->aulPropTag = talloc_array(mem_ctx, uint32_t, (cn_props + NamedPropsTagArray->cValues));
+
+ for (m = 0; m < NamedPropsTagArray->cValues; m++, n++)
+ GetPropsTagArray->aulPropTag[n] = NamedPropsTagArray->aulPropTag[m];
+
+ for (m = 0; m < cn_props; m++, n++)
+ GetPropsTagArray->aulPropTag[n] = GetPropsList[m];
+
+ GetProps_cleanup:
+ MAPIFreeBuffer (NamedPropsTagArray);
+ talloc_free (nameid);
+ }
+
+ for (i = 0; i < SRowSet.cRows; i++) {
+ mapi_object_t obj_message;
+ struct mapi_SPropValue_array properties_array;
+ const mapi_id_t *pfid;
+ const mapi_id_t *pmid;
+ const bool *has_attach = NULL;
+ GSList *attach_list = NULL;
+ GSList *recip_list = NULL;
+ GSList *stream_list = NULL;
+
+ mapi_object_init(&obj_message);
+
+ pfid = (const uint64_t *) get_SPropValue_SRow_data(&SRowSet.aRow[i], PR_FID);
+ pmid = (const uint64_t *) get_SPropValue_SRow_data(&SRowSet.aRow[i], PR_MID);
+
+ has_attach = (const bool *) get_SPropValue_SRow_data(&SRowSet.aRow[i], PR_HASATTACH);
+
+ retval = OpenMessage(&obj_folder, *pfid, *pmid, &obj_message, 0);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMessage", GetLastError());
+ goto loop_cleanup;
+ }
+
+ if (has_attach && *has_attach && (MAPI_OPTIONS_FETCH_ATTACHMENTS & options)) {
+ exchange_mapi_util_get_attachments (&obj_message, &attach_list);
+ }
+
+ if (options & MAPI_OPTIONS_FETCH_RECIPIENTS)
+ exchange_mapi_util_get_recipients (&obj_message, &recip_list);
+
+ /* get the main body stream no matter what */
+ if (options & MAPI_OPTIONS_FETCH_BODY_STREAM)
+ exchange_mapi_util_read_body_stream (&obj_message, &stream_list,
+ options & MAPI_OPTIONS_GETBESTBODY);
+
+ if (GetPropsTagArray->cValues) {
+ struct SPropValue *lpProps;
+ uint32_t prop_count = 0, k;
+
+ lpProps = talloc_zero(mem_ctx, struct SPropValue);
+ retval = GetProps (&obj_message, GetPropsTagArray, &lpProps, &prop_count);
+
+ /* Conversion from SPropValue to mapi_SPropValue. (no padding here) */
+ properties_array.cValues = prop_count;
+ properties_array.lpProps = talloc_array (mem_ctx, struct mapi_SPropValue, prop_count);
+ for (k=0; k < prop_count; k++)
+ cast_mapi_SPropValue(&properties_array.lpProps[k], &lpProps[k]);
+
+ MAPIFreeBuffer(lpProps);
+ } else
+ retval = GetPropsAll (&obj_message, &properties_array);
+
+ if (retval == MAPI_E_SUCCESS) {
+ uint32_t z;
+
+ /* just to get all the other streams */
+ for (z=0; z < properties_array.cValues; z++) {
+ if ((properties_array.lpProps[z].ulPropTag & 0xFFFF) == PT_BINARY && (options & MAPI_OPTIONS_FETCH_GENERIC_STREAMS))
+ exchange_mapi_util_read_generic_stream (&obj_message, properties_array.lpProps[z].ulPropTag, &stream_list);
+ }
+
+ mapi_SPropValue_array_named(&obj_message, &properties_array);
+
+ /* NOTE: stream_list, recipient_list and attach_list should be freed by the callback */
+ FetchItemsCallbackData *item_data = g_new0 (FetchItemsCallbackData, 1);
+ item_data->fid = *pfid;
+ item_data->mid = *pmid;
+ item_data->properties = &properties_array;
+ item_data->streams = stream_list;
+ item_data->recipients = recip_list;
+ item_data->attachments = attach_list;
+ item_data->total = SRowSet.cRows;
+ item_data->index = i;
+
+ if (!cb (item_data, data)) {
+ g_warning ("\n%s(%d): %s: Callback failed for message-id %016llX ", __FILE__, __LINE__, __PRETTY_FUNCTION__, *pmid);
+ }
+
+ g_free (item_data);
+ }
+
+ if (GetPropsTagArray->cValues)
+ talloc_free (properties_array.lpProps);
+
+ loop_cleanup:
+ mapi_object_release(&obj_message);
+ }
+
+ result = TRUE;
+
+cleanup:
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_table);
+ mapi_object_release(&obj_store);
+ talloc_free (mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s: folder-id %016llX ", __FILE__, __LINE__, __PRETTY_FUNCTION__, fid));
+
+ return result;
+}
+
+gboolean
+exchange_mapi_connection_fetch_item (mapi_id_t fid, mapi_id_t mid,
+ const uint32_t *GetPropsList, const uint16_t cn_props,
+ BuildNameID build_name_id, gpointer build_name_data,
+ FetchCallback cb, gpointer data,
+ guint32 options)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ mapi_object_t obj_message;
+ struct mapi_SPropValue_array properties_array;
+ struct SPropTagArray *GetPropsTagArray;
+ GSList *attach_list = NULL;
+ GSList *recip_list = NULL;
+ GSList *stream_list = NULL;
+ gboolean result = FALSE;
+
+ d(g_print("\n%s(%d): Entering %s: folder-id %016llX message-id %016llX ", __FILE__, __LINE__, __PRETTY_FUNCTION__, fid, mid));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_FetchItem");
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+ mapi_object_init(&obj_message);
+
+ /* Open the message store */
+ retval = ((options & MAPI_OPTIONS_USE_PFSTORE) ? OpenPublicFolder(&obj_store) : OpenMsgStore(&obj_store));
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* Attempt to open the folder */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ GetPropsTagArray = talloc_zero(mem_ctx, struct SPropTagArray);
+ GetPropsTagArray->cValues = 0;
+
+ if ((GetPropsList && (cn_props > 0)) || build_name_id) {
+ struct SPropTagArray *NamedPropsTagArray;
+ uint32_t m, n=0;
+ struct mapi_nameid *nameid;
+
+ nameid = mapi_nameid_new(mem_ctx);
+ NamedPropsTagArray = talloc_zero(mem_ctx, struct SPropTagArray);
+
+ NamedPropsTagArray->cValues = 0;
+ /* Add named props using callback */
+ if (build_name_id) {
+ if (!build_name_id (nameid, build_name_data)) {
+ g_warning ("\n%s(%d): (%s): Could not build named props ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ goto GetProps_cleanup;
+ }
+
+ retval = mapi_nameid_GetIDsFromNames(nameid, &obj_folder, NamedPropsTagArray);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("mapi_nameid_GetIDsFromNames", GetLastError());
+ goto GetProps_cleanup;
+ }
+ }
+
+ GetPropsTagArray->cValues = (cn_props + NamedPropsTagArray->cValues);
+ GetPropsTagArray->aulPropTag = talloc_array(mem_ctx, uint32_t, (cn_props + NamedPropsTagArray->cValues));
+
+ for (m = 0; m < NamedPropsTagArray->cValues; m++, n++)
+ GetPropsTagArray->aulPropTag[n] = NamedPropsTagArray->aulPropTag[m];
+
+ for (m = 0; m < cn_props; m++, n++)
+ GetPropsTagArray->aulPropTag[n] = GetPropsList[m];
+
+ GetProps_cleanup:
+ MAPIFreeBuffer (NamedPropsTagArray);
+ talloc_free (nameid);
+ }
+
+ /* Open the item */
+ retval = OpenMessage(&obj_folder, fid, mid, &obj_message, 0x0);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMessage", GetLastError());
+ goto cleanup;
+ }
+
+ /* Fetch attachments */
+ if (options & MAPI_OPTIONS_FETCH_ATTACHMENTS)
+ exchange_mapi_util_get_attachments (&obj_message, &attach_list);
+
+ /* Fetch recipients */
+ if (options & MAPI_OPTIONS_FETCH_RECIPIENTS)
+ exchange_mapi_util_get_recipients (&obj_message, &recip_list);
+
+ /* get the main body stream no matter what */
+ if (options & MAPI_OPTIONS_FETCH_BODY_STREAM)
+ exchange_mapi_util_read_body_stream (&obj_message, &stream_list,
+ options & MAPI_OPTIONS_GETBESTBODY);
+
+ if (GetPropsTagArray->cValues) {
+ struct SPropValue *lpProps;
+ uint32_t prop_count = 0, k;
+
+ lpProps = talloc_zero(mem_ctx, struct SPropValue);
+ retval = GetProps (&obj_message, GetPropsTagArray, &lpProps, &prop_count);
+
+ /* Conversion from SPropValue to mapi_SPropValue. (no padding here) */
+ properties_array.cValues = prop_count;
+ properties_array.lpProps = talloc_array (mem_ctx, struct mapi_SPropValue, prop_count);
+ for (k=0; k < prop_count; k++)
+ cast_mapi_SPropValue(&properties_array.lpProps[k], &lpProps[k]);
+
+ MAPIFreeBuffer(lpProps);
+ } else
+ retval = GetPropsAll (&obj_message, &properties_array);
+
+ if (retval == MAPI_E_SUCCESS) {
+ uint32_t z;
+
+ /* just to get all the other streams */
+ for (z=0; z < properties_array.cValues; z++)
+ if ((properties_array.lpProps[z].ulPropTag & 0xFFFF) == PT_BINARY && (options & MAPI_OPTIONS_FETCH_GENERIC_STREAMS))
+ exchange_mapi_util_read_generic_stream (&obj_message, properties_array.lpProps[z].ulPropTag, &stream_list);
+
+ mapi_SPropValue_array_named(&obj_message, &properties_array);
+
+ FetchItemsCallbackData *item_data = g_new0 (FetchItemsCallbackData, 1);
+ item_data->fid = fid;
+ item_data->mid = mid;
+ item_data->properties = &properties_array;
+ item_data->streams = stream_list;
+ item_data->recipients = recip_list;
+ item_data->attachments = attach_list;
+
+ /* NOTE: stream_list, recipient_list and attach_list should be freed by the callback */
+ cb (item_data, data);
+
+ g_free (item_data);
+ }
+
+// if (GetPropsTagArray->cValues)
+// talloc_free (properties_array.lpProps);
+
+ result = TRUE;
+
+cleanup:
+ mapi_object_release(&obj_message);
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_store);
+ talloc_free (mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return result;
+}
+
+mapi_id_t
+exchange_mapi_create_folder (uint32_t olFolder, mapi_id_t pfid, const char *name)
+{
+ enum MAPISTATUS retval;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ mapi_object_t obj_top;
+ struct SPropValue vals[1];
+ const char *type;
+ mapi_id_t fid = 0;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_top);
+ mapi_object_init(&obj_folder);
+
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* We now open the top/parent folder */
+ retval = OpenFolder(&obj_store, pfid, &obj_top);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Attempt to create the folder */
+ retval = CreateFolder(&obj_top, FOLDER_GENERIC, name, "Created using Evolution/LibMAPI", OPEN_IF_EXISTS, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("CreateFolder", GetLastError());
+ goto cleanup;
+ }
+
+ switch (olFolder) {
+ case olFolderInbox:
+ type = IPF_NOTE;
+ break;
+ case olFolderCalendar:
+ type = IPF_APPOINTMENT;
+ break;
+ case olFolderContacts:
+ type = IPF_CONTACT;
+ break;
+ case olFolderTasks:
+ type = IPF_TASK;
+ break;
+ case olFolderNotes:
+ type = IPF_STICKYNOTE;
+ break;
+ default:
+ type = IPF_NOTE;
+ }
+
+ vals[0].value.lpszA = type;
+ vals[0].ulPropTag = PR_CONTAINER_CLASS;
+
+ retval = SetProps(&obj_folder, vals, 1);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetProps", GetLastError());
+ goto cleanup;
+ }
+
+ fid = mapi_object_get_id (&obj_folder);
+ g_print("\nFolder %s created with id %016llX ", name, fid);
+
+cleanup:
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_top);
+ mapi_object_release(&obj_store);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ /* Shouldn't we return (ExchangeMAPIFolder *) instead of a plain fid ? */
+ return fid;
+}
+
+gboolean
+exchange_mapi_empty_folder (mapi_id_t fid)
+{
+ enum MAPISTATUS retval;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ gboolean result = FALSE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* Attempt to open the folder to be emptied */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Empty the contents of the folder */
+ retval = EmptyFolder(&obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("EmptyFolder", GetLastError());
+ goto cleanup;
+ }
+
+ g_print("\nFolder with id %016llX was emptied ", fid);
+
+ result = TRUE;
+
+cleanup:
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_store);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return result;
+}
+
+/* FIXME: param olFolder is never used in the routine. Remove it and cleanup at the backends */
+gboolean
+exchange_mapi_remove_folder (uint32_t olFolder, mapi_id_t fid)
+{
+ enum MAPISTATUS retval;
+ mapi_object_t obj_store;
+ mapi_object_t obj_top;
+ mapi_object_t obj_folder;
+ ExchangeMAPIFolder *folder;
+ gboolean result = FALSE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ folder = exchange_mapi_folder_get_folder (fid);
+ g_return_val_if_fail (folder != NULL, FALSE);
+
+ LOCK();
+ LOGALL();
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_top);
+ mapi_object_init(&obj_folder);
+
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* FIXME: If the folder has sub-folders, open each of them in turn, empty them and delete them.
+ * Note that this has to be done recursively, for the sub-folders as well.
+ */
+
+ /* Attempt to open the folder to be removed */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Empty the contents of the folder */
+ retval = EmptyFolder(&obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("EmptyFolder", GetLastError());
+ goto cleanup;
+ }
+
+ g_print("\nFolder with id %016llX was emptied ", fid);
+
+ /* Attempt to open the top/parent folder */
+ retval = OpenFolder(&obj_store, folder->parent_folder_id, &obj_top);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Call DeleteFolder on the folder to be removed */
+ retval = DeleteFolder(&obj_top, fid);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("DeleteFolder", GetLastError());
+ goto cleanup;
+ }
+
+ g_print("\nFolder with id %016llX was deleted ", fid);
+
+ result = TRUE;
+
+cleanup:
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_top);
+ mapi_object_release(&obj_store);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return result;
+}
+
+gboolean
+exchange_mapi_rename_folder (mapi_id_t fid, const char *new_name)
+{
+ enum MAPISTATUS retval;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ struct SPropValue *props = NULL;
+ TALLOC_CTX *mem_ctx;
+ gboolean result = FALSE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_RenameFolder");
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* Open the folder to be renamed */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ props = talloc_zero(mem_ctx, struct SPropValue);
+ set_SPropValue_proptag (props, PR_DISPLAY_NAME, new_name);
+
+ retval = SetProps(&obj_folder, props, 1);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetProps", GetLastError());
+ goto cleanup;
+ }
+
+ result = TRUE;
+
+cleanup:
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_store);
+ talloc_free(mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return result;
+}
+
+struct SPropTagArray *
+exchange_mapi_util_resolve_named_props (uint32_t olFolder, mapi_id_t fid,
+ BuildNameID build_name_id, gpointer ni_data)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ struct mapi_nameid *nameid;
+ struct SPropTagArray *SPropTagArray, *ret_array = NULL;
+ uint32_t i;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_ResolveNamedProps");
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+
+ nameid = mapi_nameid_new(mem_ctx);
+ SPropTagArray = talloc_zero(mem_ctx, struct SPropTagArray);
+
+ /* Open the message store */
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* If fid not present then we'll use olFolder. Document this in API doc. */
+ if (fid == 0) {
+ retval = GetDefaultFolder(&obj_store, &fid, olFolder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetDefaultFolder", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Attempt to open the folder */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Add named props using callback */
+ if (build_name_id) {
+ if (!build_name_id (nameid, ni_data)) {
+ g_warning ("\n%s(%d): (%s): Could not build named props ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ goto cleanup;
+ }
+
+ retval = mapi_nameid_GetIDsFromNames(nameid, &obj_folder, SPropTagArray);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("mapi_nameid_GetIDsFromNames", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ ret_array = g_new0 (struct SPropTagArray, 1);
+ ret_array->aulPropTag = g_new0 (enum MAPITAGS, SPropTagArray->cValues);
+ ret_array->cValues = SPropTagArray->cValues;
+ for (i = 0; i < SPropTagArray->cValues; ++i)
+ ret_array->aulPropTag[i] = SPropTagArray->aulPropTag[i];
+
+cleanup:
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_store);
+ talloc_free(mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return ret_array;
+}
+
+struct SPropTagArray *
+exchange_mapi_util_resolve_named_prop (uint32_t olFolder, mapi_id_t fid, uint16_t lid, const char *OLEGUID)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ struct mapi_nameid *nameid;
+ struct SPropTagArray *SPropTagArray, *ret_array = NULL;
+ uint32_t i;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_ResolveNamedProp");
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+
+ nameid = mapi_nameid_new(mem_ctx);
+ SPropTagArray = talloc_zero(mem_ctx, struct SPropTagArray);
+
+ /* Open the message store */
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* If fid not present then we'll use olFolder. Document this in API doc. */
+ if (fid == 0) {
+ retval = GetDefaultFolder(&obj_store, &fid, olFolder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetDefaultFolder", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Attempt to open the folder */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ mapi_nameid_lid_add (nameid, lid, OLEGUID);
+
+ retval = mapi_nameid_GetIDsFromNames(nameid, &obj_folder, SPropTagArray);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("mapi_nameid_GetIDsFromNames", GetLastError());
+ goto cleanup;
+ }
+
+ ret_array = g_new0 (struct SPropTagArray, 1);
+ ret_array->aulPropTag = g_new0 (enum MAPITAGS, SPropTagArray->cValues);
+ ret_array->cValues = SPropTagArray->cValues;
+ for (i = 0; i < SPropTagArray->cValues; ++i)
+ ret_array->aulPropTag[i] = SPropTagArray->aulPropTag[i];
+
+cleanup:
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_store);
+ talloc_free(mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return ret_array;
+}
+
+uint32_t
+exchange_mapi_util_create_named_prop (uint32_t olFolder, mapi_id_t fid,
+ const char *named_prop_name, uint32_t ptype)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ struct GUID guid;
+ struct MAPINAMEID *nameid;
+ struct SPropTagArray *SPropTagArray;
+ uint32_t propID = 0x00000000;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_CreateNamedProp");
+
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+
+ GUID_from_string(PS_INTERNET_HEADERS, &guid);
+ nameid = talloc_zero(mem_ctx, struct MAPINAMEID);
+ SPropTagArray = talloc_zero(mem_ctx, struct SPropTagArray);
+
+ nameid[0].lpguid = guid;
+ nameid[0].ulKind = MNID_STRING;
+ nameid[0].kind.lpwstr.Name = named_prop_name;
+
+ /* Open the message store */
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* If fid not present then we'll use olFolder. Document this in API doc. */
+ if (fid == 0) {
+ retval = GetDefaultFolder(&obj_store, &fid, olFolder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetDefaultFolder", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Attempt to open the folder */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Fetch an ID from the server */
+ retval = GetIDsFromNames(&obj_folder, 1, &nameid[0], MAPI_CREATE, &SPropTagArray);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetIDsFromNames", GetLastError());
+ goto cleanup;
+ }
+
+ propID = SPropTagArray->aulPropTag[0] | ptype;
+
+cleanup:
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_store);
+ talloc_free(mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return propID;
+}
+
+mapi_id_t
+exchange_mapi_get_default_folder_id (uint32_t olFolder)
+{
+ enum MAPISTATUS retval;
+ mapi_object_t obj_store;
+ mapi_id_t fid = 0;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mapi_object_init(&obj_store);
+
+ /* Open the message store */
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ retval = GetDefaultFolder(&obj_store, &fid, olFolder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetDefaultFolder", GetLastError());
+ goto cleanup;
+ }
+
+cleanup:
+ mapi_object_release(&obj_store);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return (retval == MAPI_E_SUCCESS ? fid : 0);
+}
+
+mapi_id_t
+exchange_mapi_create_item (uint32_t olFolder, mapi_id_t fid,
+ BuildNameID build_name_id, gpointer ni_data,
+ BuildProps build_props, gpointer p_data,
+ GSList *recipients, GSList *attachments, GSList *generic_streams,
+ uint32_t options)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ mapi_object_t obj_message;
+ struct mapi_nameid *nameid;
+ struct SPropTagArray *SPropTagArray;
+ struct SPropValue *props = NULL;
+ gint propslen = 0;
+ mapi_id_t mid = 0;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_CreateItem");
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+ mapi_object_init(&obj_message);
+
+ nameid = mapi_nameid_new(mem_ctx);
+ SPropTagArray = talloc_zero(mem_ctx, struct SPropTagArray);
+
+ /* Open the message store */
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* If fid not present then we'll use olFolder. Document this in API doc. */
+ if (fid == 0) {
+ retval = GetDefaultFolder(&obj_store, &fid, olFolder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetDefaultFolder", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Attempt to open the folder */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Create the item */
+ retval = CreateMessage(&obj_folder, &obj_message);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("CreateMessage", GetLastError());
+ goto cleanup;
+ }
+
+// d(mapi_object_debug (&obj_message));
+
+ /* Add named props using callback */
+ if (build_name_id) {
+ if (!build_name_id (nameid, ni_data)) {
+ g_warning ("\n%s(%d): (%s): Could not build named props ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ goto cleanup;
+ }
+
+ retval = mapi_nameid_GetIDsFromNames(nameid, &obj_folder, SPropTagArray);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("mapi_nameid_GetIDsFromNames", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Add regular props using callback */
+ if (build_props) {
+ propslen = build_props (&props, SPropTagArray, p_data);
+ if (propslen < 1) {
+ g_warning ("\n%s(%d): (%s): build_props failed! propslen = %d ", __FILE__, __LINE__, __PRETTY_FUNCTION__, propslen);
+ goto cleanup;
+ }
+ }
+
+ /* set properties for the item */
+ retval = SetProps(&obj_message, props, propslen);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetProps", GetLastError());
+ goto cleanup;
+ }
+
+ if (generic_streams) {
+ exchange_mapi_util_write_generic_streams (&obj_message, generic_streams);
+ }
+
+ /* Set attachments if any */
+ if (attachments) {
+ exchange_mapi_util_set_attachments (&obj_message, attachments, FALSE);
+ }
+
+ /* Set recipients if any */
+ if (recipients) {
+ exchange_mapi_util_modify_recipients (mem_ctx, &obj_message, recipients, FALSE);
+ }
+
+ /* Finally, save all changes */
+ retval = SaveChangesMessage(&obj_folder, &obj_message);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SaveChangesMessage", GetLastError());
+ goto cleanup;
+ }
+
+ if (recipients && !(options & MAPI_OPTIONS_DONT_SUBMIT)) {
+ /* Mark message as ready to be sent */
+ retval = SubmitMessage(&obj_message);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SubmitMessage", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ mid = mapi_object_get_id (&obj_message);
+
+cleanup:
+ mapi_object_release(&obj_message);
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_store);
+ talloc_free(mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return mid;
+}
+
+gboolean
+exchange_mapi_modify_item (uint32_t olFolder, mapi_id_t fid, mapi_id_t mid,
+ BuildNameID build_name_id, gpointer ni_data,
+ BuildProps build_props, gpointer p_data,
+ GSList *recipients, GSList *attachments, GSList *generic_streams,
+ uint32_t options)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ mapi_object_t obj_message;
+ struct mapi_nameid *nameid;
+ struct SPropTagArray *SPropTagArray;
+ struct SPropValue *props = NULL;
+ gint propslen = 0;
+ gboolean result = FALSE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_ModifyItem");
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+ mapi_object_init(&obj_message);
+
+ nameid = mapi_nameid_new(mem_ctx);
+ SPropTagArray = talloc_zero(mem_ctx, struct SPropTagArray);
+
+ /* Open the message store */
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* If fid not present then we'll use olFolder. Document this in API doc. */
+ if (fid == 0) {
+ retval = GetDefaultFolder(&obj_store, &fid, olFolder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetDefaultFolder", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Attempt to open the folder */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Open the item to be modified */
+ retval = OpenMessage(&obj_folder, fid, mid, &obj_message, MAPI_MODIFY);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMessage", GetLastError());
+ goto cleanup;
+ }
+
+// d(mapi_object_debug (&obj_message));
+
+ /* Add named props using callback */
+ if (build_name_id) {
+ if (!build_name_id (nameid, ni_data)) {
+ g_warning ("\n%s(%d): (%s): Could not build named props ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ goto cleanup;
+ }
+
+ retval = mapi_nameid_GetIDsFromNames(nameid, &obj_folder, SPropTagArray);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("mapi_nameid_GetIDsFromNames", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Add regular props using callback */
+ if (build_props) {
+ propslen = build_props (&props, SPropTagArray, p_data);
+ if (propslen < 1) {
+ g_warning ("\n%s(%d): (%s): Could not build props ", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ goto cleanup;
+ }
+ }
+
+ /* set properties for the item */
+ retval = SetProps(&obj_message, props, propslen);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetProps", GetLastError());
+ goto cleanup;
+ }
+
+ if (generic_streams) {
+ exchange_mapi_util_write_generic_streams (&obj_message, generic_streams);
+ }
+
+ /* Set attachments if any */
+ if (attachments) {
+ exchange_mapi_util_set_attachments (&obj_message, attachments, TRUE);
+ }
+
+ /* Set recipients if any */
+ if (recipients) {
+ exchange_mapi_util_modify_recipients (mem_ctx, &obj_message, recipients, TRUE);
+ }
+
+ /* Finally, save all changes */
+ retval = SaveChangesMessage(&obj_folder, &obj_message);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SaveChangesMessage", GetLastError());
+ goto cleanup;
+ }
+
+ if (recipients && !(options & MAPI_OPTIONS_DONT_SUBMIT)) {
+ /* Mark message as ready to be sent */
+ retval = SubmitMessage(&obj_message);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SubmitMessage", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ result = TRUE;
+
+cleanup:
+ mapi_object_release(&obj_message);
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_store);
+ talloc_free(mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return result;
+}
+
+gboolean
+exchange_mapi_set_flags (uint32_t olFolder, mapi_id_t fid, GSList *mids, uint32_t flag, guint32 options)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ uint32_t i;
+ mapi_id_t *id_messages;
+ GSList *tmp = mids;
+ gboolean result = FALSE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_SetFlags");
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+
+ id_messages = talloc_array(mem_ctx, mapi_id_t, g_slist_length (mids));
+ for (i=0; tmp; tmp=tmp->next, i++)
+ id_messages[i] = *((mapi_id_t *)tmp->data);
+
+ /* Open the message store */
+ retval = ((options & MAPI_OPTIONS_USE_PFSTORE) ? OpenPublicFolder(&obj_store) : OpenMsgStore(&obj_store)) ;
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore / OpenPublicFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Attempt to open the folder */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ retval = SetReadFlags(&obj_folder, flag, i, id_messages);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetReadFlags", GetLastError());
+ goto cleanup;
+ }
+
+ result = TRUE;
+
+cleanup:
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_store);
+ talloc_free(mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return result;
+}
+
+static gboolean
+mapi_move_items (mapi_id_t src_fid, mapi_id_t dest_fid, GSList *mid_list, gboolean do_copy)
+{
+ enum MAPISTATUS retval;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder_src;
+ mapi_object_t obj_folder_dst;
+ mapi_id_array_t msg_id_array;
+ GSList *l;
+ gboolean result = FALSE;
+
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder_src);
+ mapi_object_init(&obj_folder_dst);
+ mapi_id_array_init(&msg_id_array);
+
+ for (l = mid_list; l != NULL; l = g_slist_next (l))
+ mapi_id_array_add_id (&msg_id_array, *((mapi_id_t *)l->data));
+
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ retval = OpenFolder(&obj_store, src_fid, &obj_folder_src);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder - source folder", GetLastError());
+ goto cleanup;
+ }
+
+ retval = OpenFolder(&obj_store, dest_fid, &obj_folder_dst);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder - destination folder", GetLastError());
+ goto cleanup;
+ }
+
+ retval = MoveCopyMessages(&obj_folder_src, &obj_folder_dst, &msg_id_array, do_copy);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("MoveCopyMessages", GetLastError());
+ goto cleanup;
+ }
+
+ result = TRUE;
+
+cleanup:
+ mapi_id_array_release(&msg_id_array);
+ mapi_object_release(&obj_folder_dst);
+ mapi_object_release(&obj_folder_src);
+ mapi_object_release(&obj_store);
+
+ return result;
+}
+
+gboolean
+exchange_mapi_copy_items (mapi_id_t src_fid, mapi_id_t dest_fid, GSList *mids)
+{
+ gboolean result = FALSE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ result = mapi_move_items (src_fid, dest_fid, mids, TRUE);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return result;
+}
+
+gboolean
+exchange_mapi_move_items (mapi_id_t src_fid, mapi_id_t dest_fid, GSList *mids)
+{
+ gboolean result = FALSE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ result = mapi_move_items (src_fid, dest_fid, mids, FALSE);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return result;
+}
+
+gboolean
+exchange_mapi_remove_items (uint32_t olFolder, mapi_id_t fid, GSList *mids)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ mapi_object_t obj_folder;
+ uint32_t i;
+ mapi_id_t *id_messages;
+ GSList *tmp = mids;
+ gboolean result = FALSE;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_RemoveItems");
+ mapi_object_init(&obj_store);
+ mapi_object_init(&obj_folder);
+
+ id_messages = talloc_array(mem_ctx, mapi_id_t, g_slist_length (mids));
+ for (i=0; tmp; tmp=tmp->next, i++) {
+ struct id_list *data = tmp->data;
+ id_messages[i] = data->id;
+ }
+
+ /* Open the message store */
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* If fid not present then we'll use olFolder. Document this in API doc. */
+ if (fid == 0) {
+ retval = GetDefaultFolder(&obj_store, &fid, olFolder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetDefaultFolder", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Attempt to open the folder */
+ retval = OpenFolder(&obj_store, fid, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Delete the messages from the folder */
+ retval = DeleteMessage(&obj_folder, id_messages, i);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("DeleteMessage", GetLastError());
+ goto cleanup;
+ }
+
+ result = TRUE;
+
+cleanup:
+ mapi_object_release(&obj_folder);
+ mapi_object_release(&obj_store);
+ talloc_free(mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return result;
+}
+
+static gboolean
+get_child_folders(TALLOC_CTX *mem_ctx, ExchangeMAPIFolderCategory folder_hier, mapi_object_t *parent, mapi_id_t folder_id, GSList **mapi_folders)
+{
+ enum MAPISTATUS retval;
+ mapi_object_t obj_folder;
+ mapi_object_t obj_table;
+ struct SPropTagArray *SPropTagArray = NULL;
+ struct SRowSet rowset;
+ uint32_t i, row_count = 0;
+ gboolean result = TRUE;
+
+ /* sanity check */
+ g_return_val_if_fail (mem_ctx != NULL, FALSE);
+ g_return_val_if_fail (parent != NULL, FALSE);
+
+ mapi_object_init(&obj_folder);
+ mapi_object_init(&obj_table);
+
+ /* Attempt to open the folder */
+ retval = OpenFolder(parent, folder_id, &obj_folder);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Get the hierarchy table */
+ retval = GetHierarchyTable(&obj_folder, &obj_table, 0, &row_count);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetHierarchyTable", GetLastError());
+ goto cleanup;
+ }
+
+ SPropTagArray = set_SPropTagArray(mem_ctx, 0x6,
+ PR_FID,
+ PR_CONTAINER_CLASS,
+ PR_DISPLAY_NAME,
+ PR_CONTENT_UNREAD,
+ PR_CONTENT_COUNT,
+ PR_FOLDER_CHILD_COUNT);
+
+ retval = SetColumns(&obj_table, SPropTagArray);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("SetColumns", GetLastError());
+ goto cleanup;
+ }
+
+ /* Fill the table columns with data from the rows */
+ retval = QueryRows(&obj_table, row_count, TBL_ADVANCE, &rowset);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("QueryRows", GetLastError());
+ goto cleanup;
+ }
+
+ for (i = 0; i < rowset.cRows; i++) {
+ ExchangeMAPIFolder *folder = NULL;
+ gchar *newname = NULL;
+
+ const mapi_id_t *fid = (const mapi_id_t *)find_SPropValue_data(&rowset.aRow[i], PR_FID);
+ const char *class = (const char *)find_SPropValue_data(&rowset.aRow[i], PR_CONTAINER_CLASS);
+ const char *name = (const char *)find_SPropValue_data(&rowset.aRow[i], PR_DISPLAY_NAME);
+ const uint32_t *unread = (const uint32_t *)find_SPropValue_data(&rowset.aRow[i], PR_CONTENT_UNREAD);
+ const uint32_t *total = (const uint32_t *)find_SPropValue_data(&rowset.aRow[i], PR_CONTENT_COUNT);
+ const uint32_t *child = (const uint32_t *)find_SPropValue_data(&rowset.aRow[i], PR_FOLDER_CHILD_COUNT);
+
+ if (!class)
+ class = IPF_NOTE;
+
+ newname = utf8tolinux (name);
+ g_print("\n|---+ %-15s : (Container class: %s %016llX) UnRead : %d Total : %d ", newname, class, *fid, unread ? *unread : 0, total ? *total : 0);
+
+ folder = exchange_mapi_folder_new (newname, class, folder_hier, *fid, folder_id, child ? *child : 0, unread ? *unread : 0, total ? *total : 0);
+ *mapi_folders = g_slist_prepend (*mapi_folders, folder);
+
+ if (child && *child)
+ result = (result && get_child_folders(mem_ctx, folder_hier, &obj_folder, *fid, mapi_folders));
+
+ g_free (newname);
+ }
+
+cleanup:
+ MAPIFreeBuffer (SPropTagArray);
+ mapi_object_release (&obj_folder);
+ mapi_object_release (&obj_table);
+
+ return result;
+}
+
+static void
+set_default_folders (mapi_object_t *obj_store, GSList **mapi_folders)
+{
+ GSList *folder_list = *mapi_folders;
+
+ while (folder_list = g_slist_next (folder_list)) {
+ ExchangeMAPIFolder *folder = NULL;
+ guint32 default_type = 0;
+ folder = folder_list->data;
+ if (IsMailboxFolder (obj_store,folder->folder_id, &default_type )) {
+ folder->is_default = true; /* TODO : Clean up. Redundant.*/
+ folder->default_type = default_type;
+ }
+ }
+}
+
+static void
+set_owner_name (gpointer data, gpointer user_data)
+{
+ ExchangeMAPIFolder *folder = (ExchangeMAPIFolder *)(data);
+ folder->owner_name = (const gchar *)(user_data);
+}
+
+static void
+set_user_name (gpointer data, gpointer user_data)
+{
+ ExchangeMAPIFolder *folder = (ExchangeMAPIFolder *)(data);
+ folder->user_name = (const gchar *)(user_data);
+}
+
+gboolean
+exchange_mapi_get_folders_list (GSList **mapi_folders)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ struct SPropTagArray *SPropTagArray;
+ struct SPropValue *lpProps;
+ struct SRow aRow;
+ gboolean result = FALSE;
+ mapi_id_t mailbox_id;
+ ExchangeMAPIFolder *folder;
+ uint32_t count = 0;
+ const char *mailbox_name = NULL;
+ char *utf8_mailbox_name = NULL;
+ const char *mailbox_owner_name = NULL;
+ const char *mailbox_user_name = NULL;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_GetFoldersList");
+ mapi_object_init(&obj_store);
+
+ /* Open the message store */
+ retval = OpenMsgStore(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenMsgStore", GetLastError());
+ goto cleanup;
+ }
+
+ /* Build the array of Mailbox properties we want to fetch */
+ SPropTagArray = set_SPropTagArray(mem_ctx, 0x3,
+ PR_DISPLAY_NAME,
+ PR_MAILBOX_OWNER_NAME,
+ PR_USER_NAME);
+
+ lpProps = talloc_zero(mem_ctx, struct SPropValue);
+ retval = GetProps (&obj_store, SPropTagArray, &lpProps, &count);
+ MAPIFreeBuffer(SPropTagArray);
+
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetProps", GetLastError());
+ goto cleanup;
+ }
+
+ /* Build a SRow structure */
+ aRow.ulAdrEntryPad = 0;
+ aRow.cValues = count;
+ aRow.lpProps = lpProps;
+
+ /* betting that these will never fail */
+ mailbox_name = (const char *) find_SPropValue_data(&aRow, PR_DISPLAY_NAME);
+ mailbox_owner_name = (const char *) find_SPropValue_data(&aRow, PR_MAILBOX_OWNER_NAME);
+ mailbox_user_name = (const char *) find_SPropValue_data(&aRow, PR_USER_NAME);
+
+ /* Prepare the directory listing */
+ retval = GetDefaultFolder(&obj_store, &mailbox_id, olFolderTopInformationStore);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetDefaultFolder", GetLastError());
+ goto cleanup;
+ }
+
+ utf8_mailbox_name = utf8tolinux (mailbox_name);
+
+ /* FIXME: May have to get the child folders count? Do we need/use it? */
+ folder = exchange_mapi_folder_new (utf8_mailbox_name, IPF_NOTE, MAPI_PERSONAL_FOLDER, mailbox_id, 0, 0, 0 ,0);
+ *mapi_folders = g_slist_prepend (*mapi_folders, folder);
+
+ /* FIXME: check status of get_child_folders */
+ get_child_folders (mem_ctx, MAPI_PERSONAL_FOLDER, &obj_store, mailbox_id, mapi_folders);
+
+ g_free(utf8_mailbox_name);
+
+ *mapi_folders = g_slist_reverse (*mapi_folders);
+
+ set_default_folders (&obj_store, mapi_folders);
+ g_slist_foreach (*mapi_folders, (GFunc) set_owner_name, (gpointer) mailbox_owner_name);
+ g_slist_foreach (*mapi_folders, (GFunc) set_user_name, (gpointer) mailbox_user_name);
+
+ result = TRUE;
+
+cleanup:
+ mapi_object_release(&obj_store);
+ talloc_free (mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return result;
+}
+
+gboolean
+exchange_mapi_get_pf_folders_list (GSList **mapi_folders)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ mapi_object_t obj_store;
+ gboolean result = FALSE;
+ mapi_id_t mailbox_id;
+ ExchangeMAPIFolder *folder;
+
+ d(g_print("\n%s(%d): Entering %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ LOCK();
+ LOGALL();
+ mem_ctx = talloc_init("ExchangeMAPI_PF_GetFoldersList");
+ mapi_object_init(&obj_store);
+
+ /* Open the PF message store */
+ retval = OpenPublicFolder(&obj_store);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("OpenPublicFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* Prepare the directory listing */
+ retval = GetDefaultPublicFolder(&obj_store, &mailbox_id, olFolderPublicIPMSubtree);
+ if (retval != MAPI_E_SUCCESS) {
+ mapi_errstr("GetDefaultPublicFolder", GetLastError());
+ goto cleanup;
+ }
+
+ /* TODO : Localized string */
+ folder = exchange_mapi_folder_new ("All Public Folders", IPF_NOTE, 0, mailbox_id, 0, 0, 0 ,0);
+
+ *mapi_folders = g_slist_prepend (*mapi_folders, folder);
+
+ /* FIXME: check status of get_child_folders */
+ get_child_folders (mem_ctx, MAPI_FAVOURITE_FOLDER, &obj_store, mailbox_id, mapi_folders);
+
+ result = TRUE;
+
+cleanup:
+ mapi_object_release(&obj_store);
+ talloc_free (mem_ctx);
+ LOGNONE();
+ UNLOCK();
+
+ d(g_print("\n%s(%d): Leaving %s ", __FILE__, __LINE__, __PRETTY_FUNCTION__));
+
+ return result;
+}
+
Added: trunk/src/libexchangemapi/exchange-mapi-connection.h
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-connection.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,190 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan novell com>
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EXCHANGE_MAPI_CONNECTION_H
+#define EXCHANGE_MAPI_CONNECTION_H
+
+#include <glib.h>
+
+#include <libmapi/libmapi.h>
+
+typedef enum {
+ MAPI_OPTIONS_FETCH_ATTACHMENTS = 1<<0,
+ MAPI_OPTIONS_FETCH_RECIPIENTS = 1<<1,
+ MAPI_OPTIONS_FETCH_BODY_STREAM = 1<<2,
+ MAPI_OPTIONS_FETCH_GENERIC_STREAMS = 1<<3,
+ MAPI_OPTIONS_DONT_SUBMIT = 1<<4,
+ MAPI_OPTIONS_GETBESTBODY = 1<<5,
+ MAPI_OPTIONS_USE_PFSTORE = 1<<6
+} ExchangeMAPIOptions;
+
+#define MAPI_OPTIONS_FETCH_ALL MAPI_OPTIONS_FETCH_ATTACHMENTS | \
+ MAPI_OPTIONS_FETCH_RECIPIENTS | \
+ MAPI_OPTIONS_FETCH_BODY_STREAM | \
+ MAPI_OPTIONS_FETCH_GENERIC_STREAMS
+
+typedef struct {
+ GByteArray *value;
+ uint32_t proptag;
+} ExchangeMAPIStream;
+
+typedef struct {
+ GByteArray *value;
+ uint32_t proptag;
+ uint32_t editor_format;
+} ExchangeMAPIBodyStream;
+
+typedef struct {
+ /* MANDATORY */
+ const char *email_id;
+
+ /* It is ideal to set all these properties on all recipients
+ * as we never know if a recipient would be resolved or not. */
+ struct {
+ /* These are properties which would be set on the
+ * recipients regardless if the recipient is resolved or not */
+ uint32_t req_cValues;
+ struct SPropValue *req_lpProps;
+
+ /* These are properties which would be set on the
+ * recipients only if the recipient is MAPI_UNRESOLVED */
+ uint32_t ext_cValues;
+ struct SPropValue *ext_lpProps;
+ } in;
+
+ struct {
+ /* These are properties which would be set on the
+ * recipients after GetRecipientTable() */
+ uint32_t all_cValues;
+ struct SPropValue *all_lpProps;
+ } out;
+} ExchangeMAPIRecipient;
+
+typedef struct {
+ uint32_t cValues;
+ struct SPropValue *lpProps;
+ GSList *streams;
+ GSList *objects;
+} ExchangeMAPIAttachment;
+
+typedef struct {
+ struct mapi_SPropValue_array *properties;
+ mapi_id_t fid;
+ mapi_id_t mid;
+ GSList *attachments;
+ GSList *recipients;
+ GSList *streams;
+ guint total; /*Total number of results*/
+ guint index; /*Index of this Item*/
+} FetchItemsCallbackData;
+
+struct id_list {
+ mapi_id_t id;
+};
+
+typedef gboolean (*FetchCallback) (FetchItemsCallbackData *item_data, gpointer data);
+typedef gboolean (*BuildNameID) (struct mapi_nameid *nameid, gpointer data);
+typedef int (*BuildProps) (struct SPropValue **, struct SPropTagArray *, gpointer data);
+
+gboolean
+exchange_mapi_connection_new (const char *profile, const char *password);
+
+void
+exchange_mapi_connection_close (void);
+
+gboolean
+exchange_mapi_connection_exists (void);
+
+gboolean
+exchange_mapi_connection_fetch_item (mapi_id_t fid, mapi_id_t mid,
+ const uint32_t *GetPropsList, const uint16_t cn_props,
+ BuildNameID build_name_id, gpointer build_name_data,
+ FetchCallback cb, gpointer data,
+ guint32 options);
+gboolean
+exchange_mapi_connection_fetch_items (mapi_id_t fid,
+ struct mapi_SRestriction *res,
+ const uint32_t *GetPropsList, const uint16_t cn_props,
+ BuildNameID build_name_id, gpointer build_name_data,
+ FetchCallback cb, gpointer data,
+ guint32 options);
+
+mapi_id_t
+exchange_mapi_create_folder (uint32_t olFolder, mapi_id_t pfid, const char *name);
+
+gboolean
+exchange_mapi_remove_folder (uint32_t olFolder, mapi_id_t fid);
+
+gboolean
+exchange_mapi_empty_folder (mapi_id_t fid);
+
+gboolean
+exchange_mapi_rename_folder (mapi_id_t fid, const char *new_name);
+
+GSList *
+exchange_mapi_util_check_restriction (mapi_id_t fid, struct mapi_SRestriction *res);
+
+mapi_id_t
+exchange_mapi_get_default_folder_id (uint32_t olFolder);
+
+mapi_id_t
+exchange_mapi_create_item (uint32_t olFolder, mapi_id_t fid,
+ BuildNameID build_name_id, gpointer ni_data,
+ BuildProps build_props, gpointer p_data,
+ GSList *recipients, GSList *attachments, GSList *generic_streams,
+ uint32_t options);
+gboolean
+exchange_mapi_modify_item (uint32_t olFolder, mapi_id_t fid, mapi_id_t mid,
+ BuildNameID build_name_id, gpointer ni_data,
+ BuildProps build_props, gpointer p_data,
+ GSList *recipients, GSList *attachments, GSList *generic_streams,
+ uint32_t options);
+
+gboolean
+exchange_mapi_set_flags (uint32_t olFolder, mapi_id_t fid, GSList *mid_list, uint32_t flag, guint32 options);
+
+gboolean
+exchange_mapi_remove_items (uint32_t olFolder, mapi_id_t fid, GSList *mids);
+
+gboolean
+exchange_mapi_copy_items ( mapi_id_t src_fid, mapi_id_t dest_fid, GSList *mids);
+
+gboolean
+exchange_mapi_move_items ( mapi_id_t src_fid, mapi_id_t dest_fid, GSList *mids);
+
+
+gboolean exchange_mapi_get_folders_list (GSList **mapi_folders);
+gboolean exchange_mapi_get_pf_folders_list (GSList **mapi_folders);
+
+struct SPropTagArray *
+exchange_mapi_util_resolve_named_props (uint32_t olFolder, mapi_id_t fid,
+ BuildNameID build_name_id, gpointer ni_data);
+struct SPropTagArray *
+exchange_mapi_util_resolve_named_prop (uint32_t olFolder, mapi_id_t fid,
+ uint16_t lid, const char *OLEGUID);
+uint32_t
+exchange_mapi_util_create_named_prop (uint32_t olFolder, mapi_id_t fid,
+ const char *named_prop_name, uint32_t ptype);
+
+#endif
Added: trunk/src/libexchangemapi/exchange-mapi-defs.h
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-defs.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,266 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/* Someday, all these definitions should be pushed in libmapi/mapidefs.h */
+/* NOTE: Some of the enumerations are commented out since they conflict with libmapi/mapidefs.h */
+
+#ifndef EXCHANGE_MAPI_DEFS_H
+#define EXCHANGE_MAPI_DEFS_H
+
+G_BEGIN_DECLS
+
+/* GENERAL */
+typedef enum {
+ olSunday = 1,
+ olMonday = 2,
+ olTuesday = 4,
+ olWednesday = 8,
+ olThursday = 16,
+ olFriday = 32,
+ olSaturday = 64
+} OlDaysOfWeek;
+
+typedef enum {
+ olNormal = 0,
+ olPersonal = 1,
+ olPrivate = 2,
+ olConfidential = 3
+} OlSensitivity;
+
+typedef enum {
+ olImportanceLow = 0,
+ olImportanceNormal = 1,
+ olImportanceHigh = 2
+} OlImportance;
+
+typedef enum {
+ olOriginator = 0,
+ olTo = 1,
+ olCC = 2,
+ olBCC = 3
+} OlMailRecipientType;
+
+typedef enum {
+ SingleAppt = 0x0400 ,
+ RecurAppt = 0x0401 ,
+ SingleMeet = 0x0402 ,
+ RecurMeet = 0x0403 ,
+ MeetReq = 0x0404 ,
+ RespAccept = 0x0405 ,
+ RespDecline = 0x0406 ,
+ RespTentAccept = 0x0407 ,
+ MeetCancel = 0x0408 ,
+ MeetInfoUpdate = 0x0409
+} IconIndex;
+
+#if 0
+typedef enum {
+ olEditorText = 1,
+ olEditorHTML = 2,
+ olEditorRTF = 3,
+ olEditorWord = 4
+} OlEditorType; /* PR_MESSAGE_EDITOR_FORMAT type */
+
+typedef enum {
+ olFolderDeletedItems = 3,
+ olFolderOutbox = 4,
+ olFolderSentMail = 5,
+ olFolderInbox = 6,
+ olFolderCalendar = 9,
+ olFolderContacts = 10,
+ olFolderJournal = 11,
+ olFolderNotes = 12,
+ olFolderTasks = 13,
+ olFolderDrafts = 16,
+ olPublicFoldersAllPublicFolders = 18,
+ olFolderConflicts = 19,
+ olFolderSyncIssues = 20,
+ olFolderLocalFailures = 21,
+ olFolderServerFailures = 22,
+ olFolderJunk = 23,
+ olFolderRssFeeds = 25,
+ olFolderToDo = 28,
+ olFolderManagedEmail = 29
+} OlDefaultFolders;
+
+#define olFolderTopInformationStore 1
+#define olFolderCommonView 8
+#define olFolderFinder 24
+#define olFolderPublicRoot 25
+#define olFolderPublicIPMSubtree 26
+#endif
+
+
+/* APPOINTMENTS */
+typedef enum {
+ olFree = 0,
+ olTentative = 1,
+ olBusy = 2,
+ olOutOfOffice = 3
+} OlBusyStatus; /* Appointment flags with PR_APPOINTMENT_BUSY_STATUS */
+
+typedef enum {
+ olOrganizer = 0,
+ olRequired = 1,
+ olOptional = 2,
+ olResource = 3
+} OlMeetingRecipientType;
+
+typedef enum {
+ olMeetingTentative = 2,
+ olMeetingAccepted = 3,
+ olMeetingDeclined = 4
+} OlMeetingResponse;
+
+typedef enum {
+ olResponseNone = 0,
+ olResponseOrganized = 1,
+ olResponseTentative = 2,
+ olResponseAccepted = 3,
+ olResponseDeclined = 4,
+ olResponseNotResponded = 5
+} OlResponseStatus;
+
+typedef enum {
+ mtgEmpty = 0x00000000,
+ mtgRequest = 0x00000001,
+ mtgFull = 0x00010000,
+ mtgInfo = 0x00020000,
+ mtgOutOfDate = 0x00080000,
+ mtgDelegatorCopy = 0x00100000
+} MeetingType;
+
+typedef enum {
+ olNonMeeting = 0,
+ olMeeting = 1,
+ olMeetingReceived = 3,
+ olMeetingCanceled = 5
+} OlMeetingStatus;
+
+typedef enum {
+ asfNone = 0,
+ asfMeeting = 1,
+ asfReceived = 2,
+ asfCanceled = 4
+} AppointmentStateFlags;
+
+typedef enum {
+ olNetMeeting = 0,
+ olNetShow = 1,
+ olChat = 2
+} OlNetMeetingType;
+
+
+/* TASKS */
+typedef enum {
+ olTaskNotDelegated = 0,
+ olTaskDelegationUnknown = 1,
+ olTaskDelegationAccepted = 2,
+ olTaskDelegationDeclined = 3
+} OlTaskDelegationState;
+
+typedef enum {
+ olUpdate = 2,
+ olFinalStatus = 3
+} OlTaskRecipientType;
+
+typedef enum {
+ olTaskSimple = 0,
+ olTaskAssign = 1,
+ olTaskAccept = 2,
+ olTaskDecline = 3
+} OlTaskResponse;
+
+#if 0
+typedef enum {
+ olNewTask = 0,
+ olDelegatedTask = 1,
+ olOwnTask = 2
+} OlTaskOwnership;
+
+typedef enum {
+ olTaskNotStarted = 0,
+ olTaskInProgress = 1,
+ olTaskComplete = 2,
+ olTaskWaiting = 3,
+ olTaskDeferred = 4
+} OlTaskStatus;
+#endif
+
+
+/* NOTES */
+#if 0
+typedef enum {
+ olBlue = 0,
+ olGreen = 1,
+ olPink = 2,
+ olYellow = 3,
+ olWhite = 4
+} OlNoteColor;
+#endif
+
+
+/* RECURRENCE (APPOINTMENTS/MEETINGS/TASKS) */
+typedef enum {
+ rectypeNone = 0,
+ rectypeDaily = 1,
+ rectypeWeekly = 2,
+ rectypeMonthly = 3,
+ rectypeYearly = 4
+} OlRecurrenceType;
+
+typedef enum {
+ olApptNotRecurring = 0,
+ olApptMaster = 1,
+ olApptOccurrence = 2,
+ olApptException = 3
+} OlRecurrenceState;
+
+#if 0
+typedef enum {
+ olRecursDaily = 0,
+ olRecursWeekly = 1,
+ olRecursMonthly = 2,
+ olRecursMonthNth = 3,
+ olRecursYearly = 5,
+ olRecursYearNth = 6
+} OlRecurrencePatternType;
+#endif
+
+
+#define IPM_CONTACT "IPM.Contact"
+#define IPM_APPOINTMENT "IPM.Appointment"
+#define IPM_SCHEDULE_MEETING_PREFIX "IPM.Schedule.Meeting."
+#define IPM_SCHEDULE_MEETING_REQUEST "IPM.Schedule.Meeting.Request"
+#define IPM_SCHEDULE_MEETING_CANCELED "IPM.Schedule.Meeting.Canceled"
+#define IPM_SCHEDULE_MEETING_RESP_PREFIX "IPM.Schedule.Meeting.Resp."
+#define IPM_SCHEDULE_MEETING_RESP_POS "IPM.Schedule.Meeting.Resp.Pos"
+#define IPM_SCHEDULE_MEETING_RESP_TENT "IPM.Schedule.Meeting.Resp.Tent"
+#define IPM_SCHEDULE_MEETING_RESP_NEG "IPM.Schedule.Meeting.Resp.Neg"
+#define IPM_TASK "IPM.Task"
+#define IPM_STICKYNOTE "IPM.StickyNote"
+
+
+G_END_DECLS
+
+#endif
Added: trunk/src/libexchangemapi/exchange-mapi-folder.c
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-folder.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,200 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "exchange-mapi-connection.h"
+#include "exchange-mapi-folder.h"
+
+static GSList *folder_list = NULL;
+
+/* we use a static mutex - even the same thread *may not* use the static vars concurrently */
+static GStaticMutex folder_lock = G_STATIC_MUTEX_INIT;
+
+#define LOCK() g_message("%s(%d): %s: lock(folder_lock)", __FILE__, __LINE__, __PRETTY_FUNCTION__);g_static_mutex_lock(&folder_lock)
+#define UNLOCK() g_message("%s(%d): %s: unlock(folder_lock)", __FILE__, __LINE__, __PRETTY_FUNCTION__);g_static_mutex_unlock(&folder_lock)
+#define d(x) x
+
+static ExchangeMAPIFolderType
+container_class_to_type (const char *type)
+{
+ ExchangeMAPIFolderType folder_type = MAPI_FOLDER_TYPE_UNKNOWN;;
+
+ if (!strcmp (type, IPF_APPOINTMENT))
+ folder_type = MAPI_FOLDER_TYPE_APPOINTMENT;
+ else if (!strcmp (type, IPF_CONTACT))
+ folder_type = MAPI_FOLDER_TYPE_CONTACT;
+ else if (!strcmp (type, IPF_STICKYNOTE))
+ folder_type = MAPI_FOLDER_TYPE_MEMO;
+ else if (!strcmp (type, IPF_TASK))
+ folder_type = MAPI_FOLDER_TYPE_TASK;
+ else if (!strcmp (type, IPF_NOTE))
+ folder_type = MAPI_FOLDER_TYPE_MAIL;
+ /* Fixme : no definition for this is available in mapidef.h */
+ else if (!strcmp (type, "IPF.Note.HomePage"))
+ folder_type = MAPI_FOLDER_TYPE_NOTE_HOMEPAGE;
+ else if (!strcmp (type, IPF_JOURNAL))
+ folder_type = MAPI_FOLDER_TYPE_JOURNAL;
+
+ return folder_type;
+}
+
+ExchangeMAPIFolder *
+exchange_mapi_folder_new (const char *folder_name, const char *container_class, ExchangeMAPIFolderCategory category, mapi_id_t folder_id, mapi_id_t parent_folder_id, uint32_t child_count, uint32_t unread_count, uint32_t total)
+{
+ ExchangeMAPIFolder *folder;
+
+ folder = g_new0 (ExchangeMAPIFolder, 1);
+ folder->is_default = FALSE;
+ folder->folder_name = g_strdup (folder_name);
+ folder->container_class = container_class_to_type (container_class);
+ folder->folder_id = folder_id;
+ folder->parent_folder_id = parent_folder_id;
+ folder->child_count = child_count;
+ folder->unread_count = unread_count;
+ folder->total = total;
+ folder->category = category;
+
+ return folder;
+}
+
+ExchangeMAPIFolderType
+exchange_mapi_container_class (char *type)
+{
+ return container_class_to_type (type);
+}
+
+const gchar*
+exchange_mapi_folder_get_name (ExchangeMAPIFolder *folder)
+{
+ return folder->folder_name;
+}
+
+guint64
+exchange_mapi_folder_get_fid (ExchangeMAPIFolder *folder)
+{
+ return folder->folder_id;
+}
+
+guint64
+exchange_mapi_folder_get_parent_id (ExchangeMAPIFolder *folder)
+{
+ return folder->parent_folder_id;
+}
+
+gboolean
+exchange_mapi_folder_is_root (ExchangeMAPIFolder *folder)
+{
+ return (folder->parent_folder_id == 0);
+}
+
+ExchangeMAPIFolderType
+exchange_mapi_folder_get_type (ExchangeMAPIFolder *folder)
+{
+ return folder->container_class;
+}
+
+guint32
+exchange_mapi_folder_get_unread_count (ExchangeMAPIFolder *folder)
+{
+ return folder->unread_count;
+}
+
+guint32
+exchange_mapi_folder_get_total_count (ExchangeMAPIFolder *folder)
+{
+ return folder->total;
+}
+
+GSList *
+exchange_mapi_peek_folder_list ()
+{
+ LOCK ();
+ if (!folder_list)
+ exchange_mapi_get_folders_list (&folder_list);
+ if (!folder_list)
+ g_warning ("Get folders list call failed \n");
+ UNLOCK ();
+
+ return folder_list;
+}
+
+ExchangeMAPIFolder *
+exchange_mapi_folder_get_folder (mapi_id_t fid)
+{
+ GSList *tmp = folder_list;
+
+ if (!folder_list)
+ exchange_mapi_peek_folder_list ();
+
+ tmp = folder_list;
+ while (tmp) {
+ ExchangeMAPIFolder * folder = tmp->data;
+ g_print ("%016llX %016llX\n", folder->folder_id, fid);
+ if (folder->folder_id == fid)
+ return folder;
+ tmp=tmp->next;
+ }
+
+ return NULL;
+}
+
+void
+exchange_mapi_folder_list_free ()
+{
+ GSList *tmp = folder_list;
+ LOCK ();
+ while (tmp) {
+ ExchangeMAPIFolder *data = tmp->data;
+ g_free (data);
+ data = NULL;
+ tmp = tmp->next;
+ }
+ g_slist_free (folder_list);
+ folder_list = NULL;
+ UNLOCK ();
+
+ d(g_print("Folder list freed\n"));
+ return;
+}
+
+void
+exchange_mapi_folder_list_add (ExchangeMAPIFolder *folder)
+{
+ GSList *tmp = folder_list;
+ LOCK ();
+ while (tmp) {
+ ExchangeMAPIFolder *data = tmp->data;
+ if (data->folder_id == folder->parent_folder_id) {
+ /* Insert it here */
+ d(g_print ("Inserted below the parent\n"));
+ folder_list = g_slist_insert_before (folder_list, tmp->next, folder);
+ UNLOCK ();
+ return;
+ }
+ tmp = tmp->next;
+ }
+
+ /* Append at the end */
+ folder_list = g_slist_append (folder_list, folder);
+ UNLOCK ();
+ d(g_print("Appended folder at the end\n"));
+}
Added: trunk/src/libexchangemapi/exchange-mapi-folder.h
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-folder.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,95 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EXCHANGE_MAPI_FOLDER_H
+#define EXCHANGE_MAPI_FOLDER_H
+
+#include <glib.h>
+
+#include <libmapi/libmapi.h>
+
+typedef enum {
+ MAPI_FOLDER_TYPE_MAIL=1,
+ MAPI_FOLDER_TYPE_APPOINTMENT,
+ MAPI_FOLDER_TYPE_CONTACT,
+ MAPI_FOLDER_TYPE_MEMO,
+ MAPI_FOLDER_TYPE_JOURNAL,
+ MAPI_FOLDER_TYPE_TASK,
+ MAPI_FOLDER_TYPE_NOTE_HOMEPAGE,
+ MAPI_FOLDER_TYPE_UNKNOWN
+} ExchangeMAPIFolderType;
+
+typedef enum {
+ MAPI_PERSONAL_FOLDER,
+ MAPI_FAVOURITE_FOLDER,
+ MAPI_FOREIGN_FOLDER
+} ExchangeMAPIFolderCategory;
+
+typedef struct _ExchangeMAPIFolder {
+ /* We'll need this separation of 'owner' and 'user' when we do delegation */
+ gchar *owner_name;
+ gchar *owner_email;
+ gchar *user_name;
+ gchar *user_email;
+
+ /* Need this info - default calendars/address books/notes folders can't be deleted */
+ gboolean is_default;
+ guint32 default_type;
+
+ gchar *folder_name;
+ ExchangeMAPIFolderType container_class;
+ ExchangeMAPIFolderCategory category;
+ mapi_id_t folder_id;
+ mapi_id_t parent_folder_id;
+ guint32 child_count;
+ guint32 unread_count;
+ guint32 total;
+
+ /* reserved */
+ gpointer reserved1;
+ gpointer reserved2;
+ gpointer reserved3;
+} ExchangeMAPIFolder;
+
+ExchangeMAPIFolder *
+exchange_mapi_folder_new (const char *folder_name, const char *container_class,
+ ExchangeMAPIFolderCategory catgory,
+ mapi_id_t folder_id, mapi_id_t parent_folder_id,
+ uint32_t child_count, uint32_t unread_count, uint32_t total);
+ExchangeMAPIFolderType exchange_mapi_container_class (char *type);
+
+const gchar* exchange_mapi_folder_get_name (ExchangeMAPIFolder *folder);
+guint64 exchange_mapi_folder_get_fid (ExchangeMAPIFolder *folder);
+guint64 exchange_mapi_folder_get_parent_id (ExchangeMAPIFolder *folder);
+ExchangeMAPIFolderType exchange_mapi_folder_get_type (ExchangeMAPIFolder *folder);
+guint32 exchange_mapi_folder_get_unread_count (ExchangeMAPIFolder *folder);
+guint32 exchange_mapi_folder_get_total_count (ExchangeMAPIFolder *folder);
+
+gboolean exchange_mapi_folder_is_root (ExchangeMAPIFolder *folder);
+GSList * exchange_mapi_peek_folder_list (void);
+void exchange_mapi_folder_list_free (void);
+ExchangeMAPIFolder * exchange_mapi_folder_get_folder (mapi_id_t fid);
+void exchange_mapi_folder_list_add (ExchangeMAPIFolder *folder);
+
+#endif
Added: trunk/src/libexchangemapi/exchange-mapi-utils.c
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-utils.c Wed Nov 19 04:28:20 2008
@@ -0,0 +1,606 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "exchange-mapi-utils.h"
+
+#ifdef G_OS_WIN32
+/* Undef the similar macro from pthread.h, it doesn't check if
+ * gmtime() returns NULL.
+ */
+#undef gmtime_r
+
+/* The gmtime() in Microsoft's C library is MT-safe */
+#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
+#endif
+
+/* Converts a string from Windows-UTF8 to classic-UTF8.
+ * NOTE: If the returned value is non-NULL, the caller has to free the newly
+ * allocated string using g_free()
+ */
+gchar *
+utf8tolinux (const char *wstring)
+{
+ TALLOC_CTX *mem_ctx;
+ gchar *newstr, *retval = NULL;
+
+ g_return_val_if_fail (wstring != NULL, NULL);
+
+ mem_ctx = talloc_init ("ExchangeMAPI_utf8tolinux");
+
+ newstr = windows_to_utf8(mem_ctx, wstring);
+
+ if (g_utf8_validate (newstr, -1, NULL))
+ retval = g_strdup (newstr);
+
+ talloc_free (mem_ctx);
+
+ return retval;
+}
+
+inline gchar *
+exchange_mapi_util_mapi_id_to_string (mapi_id_t id)
+{
+ return g_strdup_printf ("%016llX", id);
+}
+
+inline gboolean
+exchange_mapi_util_mapi_id_from_string (const char *str, mapi_id_t *id)
+{
+ gint n = 0;
+
+ if (str && *str)
+ n = sscanf (str, "%016llX", id);
+
+ return (n == 1);
+}
+
+/* NOTE: We use the UID as a combination of the folder-id and the message-id.
+ * Specifically, it is in this format: ("%016llX%016llX", fid, mid).
+ */
+inline gchar *
+exchange_mapi_util_mapi_ids_to_uid (mapi_id_t fid, mapi_id_t mid)
+{
+ return g_strdup_printf ("%016llX%016llX", fid, mid);
+}
+
+inline gboolean
+exchange_mapi_util_mapi_ids_from_uid (const char *str, mapi_id_t *fid, mapi_id_t *mid)
+{
+ gint n = 0;
+
+ if (str && *str)
+ n = sscanf (str, "%016llX%016llX", fid, mid);
+
+ return (n == 2);
+}
+
+/*
+ * Retrieve the property value for a given SPropValue and property tag.
+ *
+ * If the property type is a string: fetch PT_STRING8 then PT_UNICODE
+ * in case the desired property is not available in first choice.
+ *
+ * Fetch property normally for any others properties
+ */
+/* NOTE: For now, since this function has special significance only for
+ * 'string' type properties, callers should (preferably) use it for fetching
+ * such properties alone. If callers are sure that proptag would, for instance,
+ * return an 'int' or a 'systime', they should prefer get_SPropValue.
+ */
+const void *
+exchange_mapi_util_find_SPropVal_array_propval (struct SPropValue *values, uint32_t proptag)
+{
+ if (((proptag & 0xFFFF) == PT_STRING8) ||
+ ((proptag & 0xFFFF) == PT_UNICODE)) {
+ const void *str = NULL;
+
+ proptag = (proptag & 0xFFFF0000) | PT_UNICODE;
+ str = get_SPropValue(values, proptag);
+ if (str)
+ return str;
+
+ proptag = (proptag & 0xFFFF0000) | PT_STRING8;
+ str = get_SPropValue(values, proptag);
+ if (str)
+ return str;
+
+ return NULL;
+ }
+
+ /* NOTE: Similar generalizations (if any) for other property types
+ * can be made here.
+ */
+
+ return (get_SPropValue(values, proptag));
+}
+
+/*
+ * Retrieve the property value for a given SRow and property tag.
+ *
+ * If the property type is a string: fetch PT_STRING8 then PT_UNICODE
+ * in case the desired property is not available in first choice.
+ *
+ * Fetch property normally for any others properties
+ */
+/* NOTE: For now, since this function has special significance only for
+ * 'string' type properties, callers should (preferably) use it for fetching
+ * such properties alone. If callers are sure that proptag would, for instance,
+ * return an 'int' or a 'systime', they should prefer find_SPropValue_data.
+ */
+const void *
+exchange_mapi_util_find_row_propval (struct SRow *aRow, uint32_t proptag)
+{
+ if (((proptag & 0xFFFF) == PT_STRING8) ||
+ ((proptag & 0xFFFF) == PT_UNICODE)) {
+ const void *str = NULL;
+
+ proptag = (proptag & 0xFFFF0000) | PT_UNICODE;
+ str = find_SPropValue_data(aRow, proptag);
+ if (str)
+ return str;
+
+ proptag = (proptag & 0xFFFF0000) | PT_STRING8;
+ str = find_SPropValue_data(aRow, proptag);
+ if (str)
+ return str;
+
+ return NULL;
+ }
+
+ /* NOTE: Similar generalizations (if any) for other property types
+ * can be made here.
+ */
+
+ return (find_SPropValue_data(aRow, proptag));
+}
+
+/*
+ * Retrieve the property value for a given mapi_SPropValue_array and property tag.
+ *
+ * If the property type is a string: fetch PT_STRING8 then PT_UNICODE
+ * in case the desired property is not available in first choice.
+ *
+ * Fetch property normally for any others properties
+ */
+/* NOTE: For now, since this function has special significance only for
+ * 'string' type properties, callers should (preferably) use it for fetching
+ * such properties alone. If callers are sure that proptag would, for instance,
+ * return an 'int' or a 'systime', they should prefer find_mapi_SPropValue_data.
+ */
+const void *
+exchange_mapi_util_find_array_propval (struct mapi_SPropValue_array *properties, uint32_t proptag)
+{
+ if (((proptag & 0xFFFF) == PT_STRING8) ||
+ ((proptag & 0xFFFF) == PT_UNICODE)) {
+ const void *str = NULL;
+
+ proptag = (proptag & 0xFFFF0000) | PT_UNICODE;
+ str = find_mapi_SPropValue_data(properties, proptag);
+ if (str)
+ return str;
+
+ proptag = (proptag & 0xFFFF0000) | PT_STRING8;
+ str = find_mapi_SPropValue_data(properties, proptag);
+ if (str)
+ return str;
+
+ return NULL;
+ }
+
+ /* NOTE: Similar generalizations (if any) for other property types
+ * can be made here.
+ */
+
+ return (find_mapi_SPropValue_data(properties, proptag));
+}
+
+ExchangeMAPIStream *
+exchange_mapi_util_find_stream (GSList *stream_list, uint32_t proptag)
+{
+ GSList *l = stream_list;
+
+ for (; l != NULL; l = l->next) {
+ ExchangeMAPIStream *stream = (ExchangeMAPIStream *) (l->data);
+ if (stream->proptag == proptag)
+ return stream;
+ }
+
+ return NULL;
+}
+
+void
+exchange_mapi_util_free_attachment_list (GSList **attach_list)
+{
+ GSList *l = *attach_list;
+
+ if(!l)
+ return;
+
+ for (; l != NULL; l = l->next) {
+ ExchangeMAPIAttachment *attachment = (ExchangeMAPIAttachment *) (l->data);
+ /* FIXME: more stuff here */
+ g_free (attachment->lpProps);
+ exchange_mapi_util_free_stream_list (&(attachment->streams));
+ g_free (attachment);
+ attachment = NULL;
+ }
+ g_slist_free (l);
+ l = NULL;
+}
+
+void
+exchange_mapi_util_free_recipient_list (GSList **recip_list)
+{
+ GSList *l = *recip_list;
+
+ if(!l)
+ return;
+
+ for (; l != NULL; l = l->next) {
+ ExchangeMAPIRecipient *recipient = (ExchangeMAPIRecipient *) (l->data);
+ if (recipient->in.ext_cValues)
+ g_free (recipient->in.ext_lpProps);
+ if (recipient->in.req_cValues)
+ g_free (recipient->in.req_lpProps);
+/* if (recipient->out.all_cValues)
+ g_free (recipient->out.all_lpProps);
+*/ g_free (recipient);
+ }
+ g_slist_free (l);
+ l = NULL;
+}
+
+void
+exchange_mapi_util_free_stream_list (GSList **stream_list)
+{
+ GSList *l = *stream_list;
+
+ if(!l)
+ return;
+
+ for (; l != NULL; l = l->next) {
+ ExchangeMAPIStream *stream = (ExchangeMAPIStream *) (l->data);
+ g_byte_array_free (stream->value, TRUE);
+ stream->value = NULL;
+ g_free (stream);
+ stream = NULL;
+ }
+ g_slist_free (l);
+ l = NULL;
+}
+
+const gchar *
+exchange_mapi_util_ex_to_smtp (const gchar *ex_address)
+{
+ enum MAPISTATUS retval;
+ TALLOC_CTX *mem_ctx;
+ struct SPropTagArray *SPropTagArray;
+ struct SRowSet *SRowSet = NULL;
+ struct FlagList *flaglist = NULL;
+ const gchar *str_array[2];
+ const gchar *smtp_addr = NULL;
+
+ g_return_val_if_fail (ex_address != NULL, NULL);
+
+ str_array[0] = ex_address;
+ str_array[1] = NULL;
+
+ mem_ctx = talloc_init("ExchangeMAPI_EXtoSMTP");
+
+ SPropTagArray = set_SPropTagArray(mem_ctx, 0x2,
+ PR_SMTP_ADDRESS,
+ PR_SMTP_ADDRESS_UNICODE);
+
+ retval = ResolveNames((const char **)str_array, SPropTagArray, &SRowSet, &flaglist, 0);
+ if (retval != MAPI_E_SUCCESS)
+ retval = ResolveNames((const char **)str_array, SPropTagArray, &SRowSet, &flaglist, MAPI_UNICODE);
+
+ if (retval == MAPI_E_SUCCESS && SRowSet && SRowSet->cRows == 1) {
+ smtp_addr = (const char *) find_SPropValue_data(SRowSet->aRow, PR_SMTP_ADDRESS);
+ if (!smtp_addr)
+ smtp_addr = (const char *) find_SPropValue_data(SRowSet->aRow, PR_SMTP_ADDRESS_UNICODE);
+ }
+
+ talloc_free (mem_ctx);
+
+ return smtp_addr;
+}
+
+void
+exchange_mapi_debug_property_dump (struct mapi_SPropValue_array *properties)
+{
+ gint i = 0;
+
+ for (i = 0; i < properties->cValues; i++) {
+ for (i = 0; i < properties->cValues; i++) {
+ struct mapi_SPropValue *lpProp = &properties->lpProps[i];
+ const char *tmp = get_proptag_name (lpProp->ulPropTag);
+ char t_str[26];
+ gint j = 0;
+ if (tmp && *tmp)
+ g_print("\n%s \t",tmp);
+ else
+ g_print("\n0x%08X \t", lpProp->ulPropTag);
+ switch(lpProp->ulPropTag & 0xFFFF) {
+ case PT_BOOLEAN:
+ g_print(" (bool) - %d", (bool) lpProp->value.b);
+ break;
+ case PT_I2:
+ g_print(" (uint16_t) - %d", lpProp->value.i);
+ break;
+ case PT_LONG:
+ g_print(" (long) - %u", lpProp->value.l);
+ break;
+ case PT_DOUBLE:
+ g_print (" (double) - %lf", (double)lpProp->value.dbl);
+ break;
+ case PT_I8:
+ g_print (" (int) - 0x%016lX", lpProp->value.d);
+ break;
+ case PT_SYSTIME: {
+ struct timeval t;
+ struct tm tm;
+ if (get_mapi_SPropValue_array_date_timeval (&t, properties, lpProp->ulPropTag) == MAPI_E_SUCCESS) {
+ gmtime_r (&(t.tv_sec), &tm);
+ strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
+ g_print (" (struct FILETIME *) - %p\t (struct timeval) %s\t", &lpProp->value.ft, t_str);
+ }
+ }
+ break;
+ case PT_ERROR:
+ g_print (" (error) - "/* , lpProp->value.err */);
+ break;
+ case PT_STRING8:
+ g_print(" (string) - %s", lpProp->value.lpszA ? lpProp->value.lpszA : "null" );
+ break;
+ case PT_UNICODE:
+ if (lpProp)
+ g_print(" (unicodestring) - %s", lpProp->value.lpszW ? lpProp->value.lpszW : lpProp->value.lpszA ? lpProp->value.lpszA : "null");
+ break;
+ case PT_BINARY:
+ g_print(" (struct SBinary_short *) - %p Binary data follows: \n", &lpProp->value.bin);
+ for (j = 0; j < lpProp->value.bin.cb; j++)
+ g_print("0x%02X ", lpProp->value.bin.lpb[j]);
+ break;
+ case PT_MV_STRING8:
+ g_print(" (struct mapi_SLPSTRArray *) - %p", &lpProp->value.MVszA);
+ break;
+ default:
+ g_print(" - NONE NULL");
+ }
+ }
+ }
+}
+
+
+/* Attention: Devs at work ;-) */
+
+static void
+exchange_mapi_util_bin_append_uint16 (TALLOC_CTX *mem_ctx, struct SBinary *bin, const uint16_t val)
+{
+ uint8_t *ptr = NULL;
+
+ bin->lpb = talloc_realloc (mem_ctx, bin->lpb, uint8_t, bin->cb + 2);
+ bin->cb += 2;
+
+ ptr = bin->lpb + bin->cb - 2;
+
+ *ptr++ = ( val & 0xFF);
+ *ptr++ = ((val >> 8) & 0xFF);
+}
+
+static void
+exchange_mapi_util_bin_append_uint32 (TALLOC_CTX *mem_ctx, struct SBinary *bin, const uint32_t val)
+{
+ uint8_t *ptr = NULL;
+
+ bin->lpb = talloc_realloc (mem_ctx, bin->lpb, uint8_t, bin->cb + 4);
+ bin->cb += 4;
+
+ ptr = bin->lpb + bin->cb - 4;
+
+ *ptr++ = ( val & 0xFF);
+ *ptr++ = ((val >> 8) & 0xFF);
+ *ptr++ = ((val >> 16) & 0xFF);
+ *ptr++ = ((val >> 24) & 0xFF);
+}
+
+static void
+exchange_mapi_util_bin_append_string (TALLOC_CTX *mem_ctx, struct SBinary *bin, const char *val)
+{
+ size_t len = strlen (val);
+ char *ptr = NULL;
+
+ bin->lpb = talloc_realloc (mem_ctx, bin->lpb, uint8_t, bin->cb + (len + 1));
+ bin->cb += (len + 1);
+
+ ptr = (char *) bin->lpb + bin->cb - (len + 1);
+
+ strcpy (ptr, val);
+}
+
+static void
+exchange_mapi_util_bin_append_unicode (TALLOC_CTX *mem_ctx, struct SBinary *bin, const char *val)
+{
+ /* WRITE ME */
+}
+
+static void
+exchange_mapi_util_bin_append_val (TALLOC_CTX *mem_ctx, struct SBinary *bin, const uint8_t *val, size_t len)
+{
+ uint8_t *ptr = NULL;
+
+ bin->lpb = talloc_realloc (mem_ctx, bin->lpb, uint8_t, bin->cb + len);
+ bin->cb += len;
+
+ ptr = bin->lpb + bin->cb - len;
+
+ memcpy (ptr, val, len);
+}
+
+static const uint8_t MAPI_ONE_OFF_UID[] = {
+ 0x81, 0x2b, 0x1f, 0xa4, 0xbe, 0xa3, 0x10, 0x19,
+ 0x9d, 0x6e, 0x00, 0xdd, 0x01, 0x0f, 0x54, 0x02
+};
+
+#define MAPI_ONE_OFF_UNICODE 0x8000
+#define MAPI_ONE_OFF_NO_RICH_INFO 0x0001
+#define MAPI_ONE_OFF_MYSTERY_FLAG 0x1000
+
+/**
+ * e2k_entryid_generate_oneoff:
+ * @display_name: the display name of the user
+ * @email: the email address
+ * @unicode: %TRUE to generate a Unicode ENTRYID (in which case
+ * @display_name should be UTF-8), %FALSE for an ASCII ENTRYID.
+ *
+ * Constructs a "one-off" ENTRYID value that can be used as a MAPI
+ * recipient (eg, for a message forwarding server-side rule),
+ * corresponding to @display_name and @email.
+ *
+ * Return value: the recipient ENTRYID
+ **/
+struct SBinary *
+exchange_mapi_util_entryid_generate_oneoff (TALLOC_CTX *mem_ctx, const char *display_name, const char *email, gboolean unicode)
+{
+ struct SBinary *entryid;
+
+ entryid = talloc_zero (mem_ctx, struct SBinary);
+
+ exchange_mapi_util_bin_append_uint32 (mem_ctx, entryid, 0);
+ exchange_mapi_util_bin_append_val (mem_ctx, entryid, MAPI_ONE_OFF_UID, sizeof(MAPI_ONE_OFF_UID));
+ exchange_mapi_util_bin_append_uint16 (mem_ctx, entryid, 0);
+ exchange_mapi_util_bin_append_uint16 (mem_ctx, entryid,
+ MAPI_ONE_OFF_NO_RICH_INFO |
+ MAPI_ONE_OFF_MYSTERY_FLAG |
+ (unicode ? MAPI_ONE_OFF_UNICODE : 0));
+
+ if (unicode) {
+ exchange_mapi_util_bin_append_unicode (mem_ctx, entryid, display_name);
+ exchange_mapi_util_bin_append_unicode (mem_ctx, entryid, "SMTP");
+ exchange_mapi_util_bin_append_unicode (mem_ctx, entryid, email);
+ } else {
+ exchange_mapi_util_bin_append_string (mem_ctx, entryid, display_name);
+ exchange_mapi_util_bin_append_string (mem_ctx, entryid, "SMTP");
+ exchange_mapi_util_bin_append_string (mem_ctx, entryid, email);
+ }
+
+ return entryid;
+}
+
+static const uint8_t MAPI_LOCAL_UID[] = {
+ 0xdc, 0xa7, 0x40, 0xc8, 0xc0, 0x42, 0x10, 0x1a,
+ 0xb4, 0xb9, 0x08, 0x00, 0x2b, 0x2f, 0xe1, 0x82
+};
+
+/**
+ * e2k_entryid_generate_local:
+ * @exchange_dn: the Exchange 5.5-style DN of the local user
+ *
+ * Constructs an ENTRYID value that can be used as a MAPI
+ * recipient (eg, for a message forwarding server-side rule),
+ * corresponding to the local user identified by @exchange_dn.
+ *
+ * Return value: the recipient ENTRYID
+ **/
+struct SBinary *
+exchange_mapi_util_entryid_generate_local (TALLOC_CTX *mem_ctx, const char *exchange_dn)
+{
+ struct SBinary *entryid;
+
+ entryid = talloc_zero (mem_ctx, struct SBinary);
+
+ exchange_mapi_util_bin_append_uint32 (mem_ctx, entryid, 0);
+ exchange_mapi_util_bin_append_val (mem_ctx, entryid, MAPI_LOCAL_UID, sizeof(MAPI_LOCAL_UID));
+ exchange_mapi_util_bin_append_uint16 (mem_ctx, entryid, 1);
+ exchange_mapi_util_bin_append_uint16 (mem_ctx, entryid, 0);
+ exchange_mapi_util_bin_append_string (mem_ctx, entryid, exchange_dn);
+
+ return entryid;
+}
+
+/**
+ * exchange_lf_to_crlf:
+ * @in: input text in UNIX ("\n") format
+ *
+ * Creates a copy of @in with all LFs converted to CRLFs.
+ *
+ * Return value: the converted text, which the caller must free.
+ **/
+char *
+exchange_lf_to_crlf (const char *in)
+{
+ int len;
+ const char *s;
+ char *out, *d;
+
+ g_return_val_if_fail (in != NULL, NULL);
+
+ len = strlen (in);
+ for (s = strchr (in, '\n'); s; s = strchr (s + 1, '\n'))
+ len++;
+
+ out = g_malloc (len + 1);
+ for (s = in, d = out; *s; s++) {
+ if (*s == '\n')
+ *d++ = '\r';
+ *d++ = *s;
+ }
+ *d = '\0';
+
+ return out;
+}
+
+/**
+ * exchange_crlf_to_lf:
+ * @in: input text in network ("\r\n") format
+ *
+ * Creates a copy of @in with all CRLFs converted to LFs. (Actually,
+ * it just strips CRs, so any raw CRs will be removed.)
+ *
+ * Return value: the converted text, which the caller must free.
+ **/
+char *
+exchange_crlf_to_lf (const char *in)
+{
+ int len;
+ const char *s;
+ char *out;
+ GString *str;
+
+ g_return_val_if_fail (in != NULL, NULL);
+
+ str = g_string_new ("");
+
+ len = strlen (in);
+ for (s = in; *s; s++) {
+ if (*s != '\r')
+ str = g_string_append_c (str, *s);
+ }
+
+ out = str->str;
+ g_string_free (str, FALSE);
+
+ return out;
+}
+
Added: trunk/src/libexchangemapi/exchange-mapi-utils.h
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/exchange-mapi-utils.h Wed Nov 19 04:28:20 2008
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Suman Manjunath <msuman novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EXCHANGE_MAPI_UTILS_H
+#define EXCHANGE_MAPI_UTILS_H
+
+#include "exchange-mapi-connection.h"
+
+gchar *
+utf8tolinux (const char *wstring);
+
+gchar *
+exchange_mapi_util_mapi_id_to_string (mapi_id_t id);
+gboolean
+exchange_mapi_util_mapi_id_from_string (const char *str, mapi_id_t *id);
+
+gchar *
+exchange_mapi_util_mapi_ids_to_uid (mapi_id_t fid, mapi_id_t mid);
+gboolean
+exchange_mapi_util_mapi_ids_from_uid (const char *str, mapi_id_t *fid, mapi_id_t *mid);
+
+const void *
+exchange_mapi_util_find_SPropVal_array_propval (struct SPropValue *values, uint32_t proptag);
+const void *
+exchange_mapi_util_find_row_propval (struct SRow *aRow, uint32_t proptag);
+const void *
+exchange_mapi_util_find_array_propval (struct mapi_SPropValue_array *properties, uint32_t proptag);
+
+ExchangeMAPIStream *
+exchange_mapi_util_find_stream (GSList *stream_list, uint32_t proptag);
+
+void
+exchange_mapi_util_free_attachment_list (GSList **attach_list);
+void
+exchange_mapi_util_free_recipient_list (GSList **recip_list);
+void
+exchange_mapi_util_free_stream_list (GSList **stream_list);
+
+const gchar *
+exchange_mapi_util_ex_to_smtp (const gchar *ex_address);
+
+void
+exchange_mapi_debug_property_dump (struct mapi_SPropValue_array *properties);
+
+struct SBinary *
+exchange_mapi_util_entryid_generate_oneoff (TALLOC_CTX *mem_ctx, const char *display_name, const char *email, gboolean unicode);
+struct SBinary *
+exchange_mapi_util_entryid_generate_local (TALLOC_CTX *mem_ctx, const char *exchange_dn);
+
+char *
+exchange_lf_to_crlf (const char *in);
+char *
+exchange_crlf_to_lf (const char *in);
+
+#endif
+
Added: trunk/src/libexchangemapi/libexchangemapi.pc.in
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/libexchangemapi.pc.in Wed Nov 19 04:28:20 2008
@@ -0,0 +1,18 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+datarootdir= datarootdir@
+datadir= datadir@
+
+idldir= idldir@
+IDL_INCLUDES=-I${idldir} @IDL_INCLUDES@
+
+privincludedir= privincludedir@
+
+Name: libexchangemapi
+Description: Client library for accessing Exchange with libmapi
+Version: @VERSION@
+Requires: @LIBMAPI@
+Libs: -L${libdir} -lexchangemapi-1.0 -lmapi
+Cflags: -I${privincludedir}/mapi
Added: trunk/src/libexchangemapi/tz-ical-to-mapi
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/tz-ical-to-mapi Wed Nov 19 04:28:20 2008
@@ -0,0 +1,399 @@
+UTC~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Africa/Abidjan~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Accra~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Addis_Ababa~~~E. Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Algiers~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Asmara~~~E. Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Bamako~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Bangui~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Banjul~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Bissau~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Blantyre~~~South Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Brazzaville~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Bujumbura~~~South Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Cairo~~~Egypt Standard Time
+/softwarestudio.org/Tzfile/Africa/Casablanca~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Ceuta~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Conakry~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Dakar~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Dar_es_Salaam~~~E. Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Djibouti~~~E. Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Douala~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/El_Aaiun~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Freetown~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Gaborone~~~South Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Harare~~~South Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Johannesburg~~~South Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Kampala~~~E. Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Khartoum~~~E. Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Kigali~~~Egypt Standard Time
+/softwarestudio.org/Tzfile/Africa/Kinshasa~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Lagos~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Libreville~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Lome~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Luanda~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Lubumbashi~~~South Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Lusaka~~~South Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Malabo~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Maputo~~~South Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Maseru~~~South Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Mbabane~~~South Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Mogadishu~~~E. Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Monrovia~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Nairobi~~~E. Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Ndjamena~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Niamey~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Nouakchott~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Ouagadougou~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Africa/Porto-Novo~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Sao_Tome~~~Cape Verde Standard Time
+/softwarestudio.org/Tzfile/Africa/Tripoli~~~Egypt Standard Time
+/softwarestudio.org/Tzfile/Africa/Tunis~~~W. Central Africa Standard Time
+/softwarestudio.org/Tzfile/Africa/Windhoek~~~Namibia Standard Time
+/softwarestudio.org/Tzfile/America/Adak~~~Hawaiian Standard Time
+/softwarestudio.org/Tzfile/America/Anchorage~~~Alaskan Standard Time
+/softwarestudio.org/Tzfile/America/Anguilla~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Antigua~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Araguaina~~~E. South America Standard Time
+/softwarestudio.org/Tzfile/America/Argentina/Buenos_Aires~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Argentina/Catamarca~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Argentina/Cordoba~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Argentina/Jujuy~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Argentina/La_Rioja~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Argentina/Mendoza~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Argentina/Rio_Gallegos~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Argentina/San_Juan~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Argentina/Tucuman~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Argentina/Ushuaia~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Aruba~~~Venezuela Standard Time
+/softwarestudio.org/Tzfile/America/Asuncion~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Atikokan~~~US Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Bahia~~~E. South America Standard Time
+/softwarestudio.org/Tzfile/America/Barbados~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Belem~~~E. South America Standard Time
+/softwarestudio.org/Tzfile/America/Belize~~~Mexico Standard Time
+/softwarestudio.org/Tzfile/America/Blanc-Sablon~~~Atlantic Standard Time
+/softwarestudio.org/Tzfile/America/Boa_Vista~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Bogota~~~SA Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Boise~~~US Mountain Standard Time
+/softwarestudio.org/Tzfile/America/Cambridge_Bay~~~Mountain Standard Time
+/softwarestudio.org/Tzfile/America/Campo_Grande~~~E. South America Standard Time
+/softwarestudio.org/Tzfile/America/Cancun~~~Central America Standard Time
+/softwarestudio.org/Tzfile/America/Caracas~~~Venezuela Standard Time
+/softwarestudio.org/Tzfile/America/Cayenne~~~E. South America Standard Time
+/softwarestudio.org/Tzfile/America/Cayman~~~SA Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Chicago~~~Central Standard Time
+/softwarestudio.org/Tzfile/America/Chihuahua~~~Mexico Standard Time 2
+/softwarestudio.org/Tzfile/America/Costa_Rica~~~Central America Standard Time
+/softwarestudio.org/Tzfile/America/Cuiaba~~~E. South America Standard Time
+/softwarestudio.org/Tzfile/America/Curacao~~~Venezuela Standard Time
+/softwarestudio.org/Tzfile/America/Danmarkshavn~~~GMT Standard Time
+/softwarestudio.org/Tzfile/America/Dawson~~~Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Dawson_Creek~~~Mountain Standard Time
+/softwarestudio.org/Tzfile/America/Denver~~~Mountain Standard Time
+/softwarestudio.org/Tzfile/America/Detroit~~~Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Dominica~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Edmonton~~~Mountain Standard Time
+/softwarestudio.org/Tzfile/America/Eirunepe~~~SA Pacific Standard Time
+/softwarestudio.org/Tzfile/America/El_Salvador~~~Central America Standard Time
+/softwarestudio.org/Tzfile/America/Fortaleza~~~E. South America Standard Time
+/softwarestudio.org/Tzfile/America/Glace_Bay~~~Atlantic Standard Time
+/softwarestudio.org/Tzfile/America/Godthab~~~Greenland Standard Time
+/softwarestudio.org/Tzfile/America/Goose_Bay~~~Atlantic Standard Time
+/softwarestudio.org/Tzfile/America/Grand_Turk~~~SA Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Grenada~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Guadeloupe~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Guatemala~~~Central America Standard Time
+/softwarestudio.org/Tzfile/America/Guayaquil~~~SA Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Guyana~~~Venezuela Standard Time
+/softwarestudio.org/Tzfile/America/Halifax~~~Atlantic Standard Time
+/softwarestudio.org/Tzfile/America/Havana~~~SA Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Hermosillo~~~Mexico Standard Time 2
+/softwarestudio.org/Tzfile/America/Indiana/Indianapolis~~~US Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Indiana/Knox~~~Canada Central Standard Time
+/softwarestudio.org/Tzfile/America/Indiana/Marengo~~~US Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Indiana/Petersburg~~~US Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Indiana/Tell_City~~~Canada Central Standard Time
+/softwarestudio.org/Tzfile/America/Indiana/Vevay~~~US Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Indiana/Vincennes~~~US Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Indiana/Winamac~~~US Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Inuvik~~~Mountain Standard Time
+/softwarestudio.org/Tzfile/America/Iqaluit~~~Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Jamaica~~~SA Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Juneau~~~Alaskan Standard Time
+/softwarestudio.org/Tzfile/America/Kentucky/Louisville~~~US Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Kentucky/Monticello~~~US Eastern Standard Time
+/softwarestudio.org/Tzfile/America/La_Paz~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Lima~~~SA Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Los_Angeles~~~Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Maceio~~~E. South America Standard Time
+/softwarestudio.org/Tzfile/America/Managua~~~Central America Standard Time
+/softwarestudio.org/Tzfile/America/Manaus~~~Central Brazilian Standard Time
+/softwarestudio.org/Tzfile/America/Martinique~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Mazatlan~~~Mountain Standard Time (Mexico)
+/softwarestudio.org/Tzfile/America/Menominee~~~Central Standard Time
+/softwarestudio.org/Tzfile/America/Merida~~~Central America Standard Time
+/softwarestudio.org/Tzfile/America/Mexico_City~~~Mexico Standard Time
+/softwarestudio.org/Tzfile/America/Miquelon~~~Greenland Standard Time
+/softwarestudio.org/Tzfile/America/Moncton~~~Atlantic Standard Time
+/softwarestudio.org/Tzfile/America/Monterrey~~~Central Standard Time (Mexico)
+/softwarestudio.org/Tzfile/America/Montevideo~~~Montevideo Standard Time
+/softwarestudio.org/Tzfile/America/Montreal~~~Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Montserrat~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Nassau~~~Eastern Standard Time
+/softwarestudio.org/Tzfile/America/New_York~~~Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Nipigon~~~Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Nome~~~Alaskan Standard Time
+/softwarestudio.org/Tzfile/America/Noronha~~~Mid-Atlantic Standard Time
+/softwarestudio.org/Tzfile/America/North_Dakota/Center~~~Central Standard Time
+/softwarestudio.org/Tzfile/America/North_Dakota/New_Salem~~~Central Standard Time
+/softwarestudio.org/Tzfile/America/Panama~~~SA Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Pangnirtung~~~Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Paramaribo~~~Venezuela Standard Time
+/softwarestudio.org/Tzfile/America/Phoenix~~~US Mountain Standard Time
+/softwarestudio.org/Tzfile/America/Port-au-Prince~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Port_of_Spain~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Porto_Velho~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Puerto_Rico~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Rainy_River~~~Canada Central Standard Time
+/softwarestudio.org/Tzfile/America/Rankin_Inlet~~~Canada Central Standard Time
+/softwarestudio.org/Tzfile/America/Recife~~~E. South America Standard Time
+/softwarestudio.org/Tzfile/America/Regina~~~Central America Standard Time
+/softwarestudio.org/Tzfile/America/Resolute~~~Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Rio_Branco~~~SA Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Santiago~~~Pacific SA Standard Time
+/softwarestudio.org/Tzfile/America/Santo_Domingo~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Sao_Paulo~~~Mid-Atlantic Standard Time
+/softwarestudio.org/Tzfile/America/Scoresbysund~~~Azores Standard Time
+/softwarestudio.org/Tzfile/America/Shiprock~~~US Mountain Standard Time
+/softwarestudio.org/Tzfile/America/St_Johns~~~Newfoundland Standard Time
+/softwarestudio.org/Tzfile/America/St_Kitts~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/St_Lucia~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/St_Thomas~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/St_Vincent~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Swift_Current~~~Central Standard Time
+/softwarestudio.org/Tzfile/America/Tegucigalpa~~~Central America Standard Time
+/softwarestudio.org/Tzfile/America/Thule~~~Atlantic Standard Time
+/softwarestudio.org/Tzfile/America/Thunder_Bay~~~Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Tijuana~~~Pacific Standard Time (Mexico)
+/softwarestudio.org/Tzfile/America/Toronto~~~Eastern Standard Time
+/softwarestudio.org/Tzfile/America/Tortola~~~SA Western Standard Time
+/softwarestudio.org/Tzfile/America/Vancouver~~~Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Whitehorse~~~Pacific Standard Time
+/softwarestudio.org/Tzfile/America/Winnipeg~~~Canada Central Standard Time
+/softwarestudio.org/Tzfile/America/Yakutat~~~Alaskan Standard Time
+/softwarestudio.org/Tzfile/America/Yellowknife~~~Mountain Standard Time
+/softwarestudio.org/Tzfile/Antarctica/Casey~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Antarctica/Davis~~~SE Asia Standard Time
+/softwarestudio.org/Tzfile/Antarctica/DumontDUrville~~~West Pacific Standard Time
+/softwarestudio.org/Tzfile/Antarctica/Mawson~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Antarctica/McMurdo~~~Tonga Standard Time
+/softwarestudio.org/Tzfile/Antarctica/Palmer~~~Greenland Standard Time
+/softwarestudio.org/Tzfile/Antarctica/Rothera~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Antarctica/South_Pole~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Antarctica/Syowa~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Antarctica/Vostok~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Arctic/Longyearbyen~~~Central Europe Standard Time
+/softwarestudio.org/Tzfile/Asia/Aden~~~Arab Standard Time
+/softwarestudio.org/Tzfile/Asia/Almaty~~~N. Central Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Amman~~~Jordan Standard Time
+/softwarestudio.org/Tzfile/Asia/Anadyr~~~Fiji Standard Time
+/softwarestudio.org/Tzfile/Asia/Aqtau~~~Ekaterinburg Standard Time
+/softwarestudio.org/Tzfile/Asia/Aqtobe~~~Ekaterinburg Standard Time
+/softwarestudio.org/Tzfile/Asia/Ashgabat~~~Ekaterinburg Standard Time
+/softwarestudio.org/Tzfile/Asia/Baghdad~~~Arabic Standard Time
+/softwarestudio.org/Tzfile/Asia/Bahrain~~~Arab Standard Time
+/softwarestudio.org/Tzfile/Asia/Baku~~~Azerbaijan Standard Time
+/softwarestudio.org/Tzfile/Asia/Bangkok~~~SE Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Beijing~~~China Standard Time
+/softwarestudio.org/Tzfile/Asia/Beirut~~~Middle East Standard Time
+/softwarestudio.org/Tzfile/Asia/Bishkek~~~Central Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Brunei~~~Taipei Standard Time
+/softwarestudio.org/Tzfile/Asia/Kolkata~~~India Standard Time
+/softwarestudio.org/Tzfile/Asia/Choibalsan~~~Yakutsk Standard Time
+/softwarestudio.org/Tzfile/Asia/Chongqing~~~China Standard Time
+/softwarestudio.org/Tzfile/Asia/Colombo~~~India Standard Time
+/softwarestudio.org/Tzfile/Asia/Damascus~~~Israel Standard Time
+/softwarestudio.org/Tzfile/Asia/Dhaka~~~Central Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Dili~~~Yakutsk Standard Time
+/softwarestudio.org/Tzfile/Asia/Dubai~~~Iran Standard Time
+/softwarestudio.org/Tzfile/Asia/Dushanbe~~~West Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Gaza~~~Israel Standard Time
+/softwarestudio.org/Tzfile/Asia/Harbin~~~China Standard Time
+/softwarestudio.org/Tzfile/Asia/Hong_Kong~~~China Standard Time
+/softwarestudio.org/Tzfile/Asia/Hovd~~~North Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Irkutsk~~~North Asia East Standard Time
+/softwarestudio.org/Tzfile/Asia/Jakarta~~~SE Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Jayapura~~~Yakutsk Standard Time
+/softwarestudio.org/Tzfile/Asia/Jerusalem~~~Israel Standard Time
+/softwarestudio.org/Tzfile/Asia/Kabul~~~Afghanistan Standard Time
+/softwarestudio.org/Tzfile/Asia/Kamchatka~~~Fiji Standard Time
+/softwarestudio.org/Tzfile/Asia/Karachi~~~West Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Kashgar~~~China Standard Time
+/softwarestudio.org/Tzfile/Asia/Katmandu~~~Nepal Standard Time
+/softwarestudio.org/Tzfile/Asia/Krasnoyarsk~~~North Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Kuala_Lumpur~~~Singapore Standard Time
+/softwarestudio.org/Tzfile/Asia/Kuching~~~Taipei Standard Time
+/softwarestudio.org/Tzfile/Asia/Kuwait~~~Arab Standard Time
+/softwarestudio.org/Tzfile/Asia/Macau~~~China Standard Time
+/softwarestudio.org/Tzfile/Asia/Magadan~~~Central Pacific Standard Time
+/softwarestudio.org/Tzfile/Asia/Makassar~~~Taipei Standard Time
+/softwarestudio.org/Tzfile/Asia/Manila~~~Taipei Standard Time
+/softwarestudio.org/Tzfile/Asia/Muscat~~~Arabian Standard Time
+/softwarestudio.org/Tzfile/Asia/Nicosia~~~Israel Standard Time
+/softwarestudio.org/Tzfile/Asia/Novosibirsk~~~N. Central Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Omsk~~~N. Central Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Oral~~~Ekaterinburg Standard Time
+/softwarestudio.org/Tzfile/Asia/Phnom_Penh~~~SE Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Pontianak~~~SE Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Pyongyang~~~Korea Standard Time
+/softwarestudio.org/Tzfile/Asia/Qatar~~~Arab Standard Time
+/softwarestudio.org/Tzfile/Asia/Qyzylorda~~~Central Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Rangoon~~~Myanmar Standard Time
+/softwarestudio.org/Tzfile/Asia/Riyadh~~~Arab Standard Time
+/softwarestudio.org/Tzfile/Asia/Saigon~~~SE Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Sakhalin~~~Vladivostok Standard Time
+/softwarestudio.org/Tzfile/Asia/Samarkand~~~West Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Seoul~~~Korea Standard Time
+/softwarestudio.org/Tzfile/Asia/Shanghai~~~China Standard Time
+/softwarestudio.org/Tzfile/Asia/Singapore~~~Singapore Standard Time
+/softwarestudio.org/Tzfile/Asia/Taipei~~~Taipei Standard Time
+/softwarestudio.org/Tzfile/Asia/Tashkent~~~West Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Tbilisi~~~Georgian Standard Time
+/softwarestudio.org/Tzfile/Asia/Tehran~~~Iran Standard Time
+/softwarestudio.org/Tzfile/Asia/Thimphu~~~Central Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Tokyo~~~Tokyo Standard Time
+/softwarestudio.org/Tzfile/Asia/Ulaanbaatar~~~North Asia East Standard Time
+/softwarestudio.org/Tzfile/Asia/Urumqi~~~China Standard Time
+/softwarestudio.org/Tzfile/Asia/Vientiane~~~SE Asia Standard Time
+/softwarestudio.org/Tzfile/Asia/Vladivostok~~~Vladivostok Standard Time
+/softwarestudio.org/Tzfile/Asia/Yakutsk~~~Yakutsk Standard Time
+/softwarestudio.org/Tzfile/Asia/Yekaterinburg~~~Ekaterinburg Standard Time
+/softwarestudio.org/Tzfile/Asia/Yerevan~~~Armenian Standard Time
+/softwarestudio.org/Tzfile/Atlantic/Azores~~~Azores Standard Time
+/softwarestudio.org/Tzfile/Atlantic/Bermuda~~~Atlantic Standard Time
+/softwarestudio.org/Tzfile/Atlantic/Canary~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Atlantic/Cape_Verde~~~Cape Verde Standard Time
+/softwarestudio.org/Tzfile/Atlantic/Faroe~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Atlantic/Jan_Mayen~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Atlantic/Madeira~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Atlantic/Reykjavik~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Atlantic/South_Georgia~~~Mid-Atlantic Standard Time
+/softwarestudio.org/Tzfile/Atlantic/Stanley~~~SA Eastern Standard Time
+/softwarestudio.org/Tzfile/Atlantic/St_Helena~~~Greenwich Standard Time
+/softwarestudio.org/Tzfile/Australia/Adelaide~~~Cen. Australia Standard Time
+/softwarestudio.org/Tzfile/Australia/Brisbane~~~E. Australia Standard Time
+/softwarestudio.org/Tzfile/Australia/Broken_Hill~~~Cen. Australia Standard Time
+/softwarestudio.org/Tzfile/Australia/Currie~~~AUS Eastern Standard Time
+/softwarestudio.org/Tzfile/Australia/Darwin~~~AUS Central Standard Time
+/softwarestudio.org/Tzfile/Australia/Eucla~~~AUS Central Standard Time
+/softwarestudio.org/Tzfile/Australia/Hobart~~~Tasmania Standard Time
+/softwarestudio.org/Tzfile/Australia/Lindeman~~~E. Australia Standard Time
+/softwarestudio.org/Tzfile/Australia/Lord_Howe~~~AUS Eastern Standard Time
+/softwarestudio.org/Tzfile/Australia/Melbourne~~~AUS Eastern Standard Time
+/softwarestudio.org/Tzfile/Australia/Perth~~~W. Australia Standard Time
+/softwarestudio.org/Tzfile/Australia/Sydney~~~AUS Eastern Standard Time
+/softwarestudio.org/Tzfile/Europe/Amsterdam~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Andorra~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Athens~~~GTB Standard Time
+/softwarestudio.org/Tzfile/Europe/Belgrade~~~Central European Standard Time
+/softwarestudio.org/Tzfile/Europe/Berlin~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Bratislava~~~Central Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Brussels~~~Romance Standard Time
+/softwarestudio.org/Tzfile/Europe/Bucharest~~~E. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Budapest~~~Central Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Chisinau~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Copenhagen~~~Romance Standard Time
+/softwarestudio.org/Tzfile/Europe/Dublin~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Europe/Gibraltar~~~Romance Standard Time
+/softwarestudio.org/Tzfile/Europe/Guernsey~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Europe/Helsinki~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Isle_of_Man~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Europe/Istanbul~~~GTB Standard Time
+/softwarestudio.org/Tzfile/Europe/Jersey~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Europe/Kaliningrad~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Kiev~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Lisbon~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Europe/Ljubljana~~~Central Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/London~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Europe/Luxembourg~~~Romance Standard Time
+/softwarestudio.org/Tzfile/Europe/Madrid~~~Romance Standard Time
+/softwarestudio.org/Tzfile/Europe/Malta~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Mariehamn~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Minsk~~~E. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Monaco~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Moscow~~~Russian Standard Time
+/softwarestudio.org/Tzfile/Europe/Oslo~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Paris~~~Romance Standard Time
+/softwarestudio.org/Tzfile/Europe/Podgorica~~~Central European Standard Time
+/softwarestudio.org/Tzfile/Europe/Prague~~~Central Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Riga~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Rome~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Samara~~~Caucasus Standard Time
+/softwarestudio.org/Tzfile/Europe/San_Marino~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Sarajevo~~~Central European Standard Time
+/softwarestudio.org/Tzfile/Europe/Simferopol~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Skopje~~~Central European Standard Time
+/softwarestudio.org/Tzfile/Europe/Sofia~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Stockholm~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Tallinn~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Tirane~~~Central European Standard Time
+/softwarestudio.org/Tzfile/Europe/Uzhgorod~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Vaduz~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Vatican~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Vienna~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Europe/Vilnius~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Volgograd~~~Russian Standard Time
+/softwarestudio.org/Tzfile/Europe/Warsaw~~~Central European Standard Time
+/softwarestudio.org/Tzfile/Europe/Zagreb~~~Central European Standard Time
+/softwarestudio.org/Tzfile/Europe/Zaporozhye~~~FLE Standard Time
+/softwarestudio.org/Tzfile/Europe/Zurich~~~W. Europe Standard Time
+/softwarestudio.org/Tzfile/Indian/Antananarivo~~~E. Africa Standard Time
+/softwarestudio.org/Tzfile/Indian/Chagos~~~Sri Lanka Standard Time
+/softwarestudio.org/Tzfile/Indian/Christmas~~~SE Asia Standard Time
+/softwarestudio.org/Tzfile/Indian/Cocos~~~Myanmar Standard Time
+/softwarestudio.org/Tzfile/Indian/Comoro~~~E. Africa Standard Time
+/softwarestudio.org/Tzfile/Indian/Kerguelen~~~GMT Standard Time
+/softwarestudio.org/Tzfile/Indian/Mahe~~~Iran Standard Time
+/softwarestudio.org/Tzfile/Indian/Maldives~~~West Asia Standard Time
+/softwarestudio.org/Tzfile/Indian/Mauritius~~~Arabian Standard Time
+/softwarestudio.org/Tzfile/Indian/Mayotte~~~E. Africa Standard Time
+/softwarestudio.org/Tzfile/Indian/Reunion~~~Iran Standard Time
+/softwarestudio.org/Tzfile/Pacific/Apia~~~Dateline Standard Time
+/softwarestudio.org/Tzfile/Pacific/Auckland~~~New Zealand Standard Time
+/softwarestudio.org/Tzfile/Pacific/Chatham~~~Tonga Standard Time
+/softwarestudio.org/Tzfile/Pacific/Easter~~~SA Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Efate~~~Central Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Enderbury~~~Tonga Standard Time
+/softwarestudio.org/Tzfile/Pacific/Fakaofo~~~Hawaiian Standard Time
+/softwarestudio.org/Tzfile/Pacific/Fiji~~~Fiji Standard Time
+/softwarestudio.org/Tzfile/Pacific/Funafuti~~~Fiji Standard Time
+/softwarestudio.org/Tzfile/Pacific/Galapagos~~~Mexico Standard Time
+/softwarestudio.org/Tzfile/Pacific/Gambier~~~Alaskan Standard Time
+/softwarestudio.org/Tzfile/Pacific/Guadalcanal~~~Central Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Guam~~~West Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Honolulu~~~Hawaiian Standard Time
+/softwarestudio.org/Tzfile/Pacific/Johnston~~~Hawaiian Standard Time
+/softwarestudio.org/Tzfile/Pacific/Kiritimati~~~Tonga Standard Time
+/softwarestudio.org/Tzfile/Pacific/Kosrae~~~Central Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Kwajalein~~~Fiji Standard Time
+/softwarestudio.org/Tzfile/Pacific/Majuro~~~Central Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Marquesas~~~Alaskan Standard Time
+/softwarestudio.org/Tzfile/Pacific/Midway~~~Samoa Standard Time
+/softwarestudio.org/Tzfile/Pacific/Nauru~~~Fiji Standard Time
+/softwarestudio.org/Tzfile/Pacific/Niue~~~Samoa Standard Time
+/softwarestudio.org/Tzfile/Pacific/Norfolk~~~Central Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Noumea~~~Central Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Pago_Pago~~~Samoa Standard Time
+/softwarestudio.org/Tzfile/Pacific/Palau~~~Yakutsk Standard Time
+/softwarestudio.org/Tzfile/Pacific/Pitcairn~~~Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Ponape~~~Central Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Port_Moresby~~~West Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Rarotonga~~~Hawaiian Standard Time
+/softwarestudio.org/Tzfile/Pacific/Saipan~~~West Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Tahiti~~~Hawaiian Standard Time
+/softwarestudio.org/Tzfile/Pacific/Tarawa~~~Fiji Standard Time
+/softwarestudio.org/Tzfile/Pacific/Tongatapu~~~Tonga Standard Time
+/softwarestudio.org/Tzfile/Pacific/Truk~~~West Pacific Standard Time
+/softwarestudio.org/Tzfile/Pacific/Wake~~~Fiji Standard Time
+/softwarestudio.org/Tzfile/Pacific/Wallis~~~Fiji Standard Time
Added: trunk/src/libexchangemapi/tz-mapi-to-ical
==============================================================================
--- (empty file)
+++ trunk/src/libexchangemapi/tz-mapi-to-ical Wed Nov 19 04:28:20 2008
@@ -0,0 +1,87 @@
+Dateline Standard Time~~~/softwarestudio.org/Tzfile/Pacific/Apia
+Samoa Standard Time~~~/softwarestudio.org/Tzfile/Pacific/Midway
+Hawaiian Standard Time~~~/softwarestudio.org/Tzfile/Pacific/Honolulu
+Alaskan Standard Time~~~/softwarestudio.org/Tzfile/America/Anchorage
+Pacific Standard Time~~~/softwarestudio.org/Tzfile/America/Los_Angeles
+Pacific Standard Time (Mexico)~~~/softwarestudio.org/Tzfile/America/Tijuana
+US Mountain Standard Time~~~/softwarestudio.org/Tzfile/America/Phoenix
+Mountain Standard Time (Mexico)~~~/softwarestudio.org/Tzfile/America/Mazatlan
+Mexico Standard Time 2~~~/softwarestudio.org/Tzfile/America/Chihuahua
+Mountain Standard Time~~~/softwarestudio.org/Tzfile/America/Denver
+Central America Standard Time~~~/softwarestudio.org/Tzfile/America/Costa_Rica
+Central Standard Time~~~/softwarestudio.org/Tzfile/America/Chicago
+Central Standard Time (Mexico)~~~/softwarestudio.org/Tzfile/America/Monterrey
+Mexico Standard Time~~~/softwarestudio.org/Tzfile/America/Mexico_City
+Canada Central Standard Time~~~/softwarestudio.org/Tzfile/America/Winnipeg
+SA Pacific Standard Time~~~/softwarestudio.org/Tzfile/America/Bogota
+Eastern Standard Time~~~/softwarestudio.org/Tzfile/America/New_York
+US Eastern Standard Time~~~/softwarestudio.org/Tzfile/America/Indiana/Indianapolis
+Venezuela Standard Time~~~/softwarestudio.org/Tzfile/America/Caracas
+Atlantic Standard Time~~~/softwarestudio.org/Tzfile/America/Halifax
+SA Western Standard Time~~~/softwarestudio.org/Tzfile/America/La_Paz
+Central Brazilian Standard Time~~~/softwarestudio.org/Tzfile/America/Manaus
+Pacific SA Standard Time~~~/softwarestudio.org/Tzfile/America/La_Paz
+Newfoundland Standard Time~~~/softwarestudio.org/Tzfile/America/St_Johns
+E. South America Standard Time~~~/softwarestudio.org/Tzfile/America/Bahia
+SA Eastern Standard Time~~~/softwarestudio.org/Tzfile/America/Argentina/Buenos_Aires
+Greenland Standard Time~~~/softwarestudio.org/Tzfile/America/Godthab
+Montevideo Standard Time~~~/softwarestudio.org/Tzfile/America/Montevideo
+Mid-Atlantic Standard Time~~~/softwarestudio.org/Tzfile/Atlantic/South_Georgia
+Azores Standard Time~~~/softwarestudio.org/Tzfile/Atlantic/Azores
+Cape Verde Standard Time~~~/softwarestudio.org/Tzfile/Atlantic/Cape_Verde
+Greenwich Standard Time~~~/softwarestudio.org/Tzfile/Africa/Casablanca
+GMT Standard Time~~~UTC
+W. Europe Standard Time~~~/softwarestudio.org/Tzfile/Europe/Berlin
+Central Europe Standard Time~~~/softwarestudio.org/Tzfile/Europe/Prague
+Romance Standard Time~~~/softwarestudio.org/Tzfile/Europe/Paris
+Central European Standard Time~~~/softwarestudio.org/Tzfile/Europe/Belgrade
+W. Central Africa Standard Time~~~/softwarestudio.org/Tzfile/Africa/Luanda
+Jordan Standard Time~~~/softwarestudio.org/Tzfile/Asia/Amman
+GTB Standard Time~~~/softwarestudio.org/Tzfile/Europe/Athens
+Middle East Standard Time~~~/softwarestudio.org/Tzfile/Asia/Beirut
+Egypt Standard Time~~~/softwarestudio.org/Tzfile/Africa/Cairo
+South Africa Standard Time~~~/softwarestudio.org/Tzfile/Africa/Harare
+FLE Standard Time~~~/softwarestudio.org/Tzfile/Europe/Helsinki
+Israel Standard Time~~~/softwarestudio.org/Tzfile/Asia/Jerusalem
+E. Europe Standard Time~~~/softwarestudio.org/Tzfile/Europe/Minsk
+Namibia Standard Time~~~/softwarestudio.org/Tzfile/Africa/Windhoek
+Arabic Standard Time~~~/softwarestudio.org/Tzfile/Asia/Baghdad
+Arab Standard Time~~~/softwarestudio.org/Tzfile/Asia/Qatar
+Russian Standard Time~~~/softwarestudio.org/Tzfile/Europe/Moscow
+E. Africa Standard Time~~~/softwarestudio.org/Tzfile/Africa/Nairobi
+Georgian Standard Time~~~/softwarestudio.org/Tzfile/Asia/Tbilisi
+Iran Standard Time~~~/softwarestudio.org/Tzfile/Asia/Tehran
+Arabian Standard Time~~~/softwarestudio.org/Tzfile/Asia/Muscat
+Azerbaijan Standard Time~~~/softwarestudio.org/Tzfile/Asia/Baku
+Caucasus Standard Time~~~/softwarestudio.org/Tzfile/Asia/Yerevan
+Armenian Standard Time~~~/softwarestudio.org/Tzfile/Asia/Yerevan
+Afghanistan Standard Time~~~/softwarestudio.org/Tzfile/Asia/Kabul
+Ekaterinburg Standard Time~~~/softwarestudio.org/Tzfile/Asia/Yekaterinburg
+West Asia Standard Time~~~/softwarestudio.org/Tzfile/Asia/Karachi
+India Standard Time~~~/softwarestudio.org/Tzfile/Asia/Kolkata
+Sri Lanka Standard Time~~~/softwarestudio.org/Tzfile/Asia/Colombo
+Nepal Standard Time~~~/softwarestudio.org/Tzfile/Asia/Katmandu
+N. Central Asia Standard Time~~~/softwarestudio.org/Tzfile/Asia/Novosibirsk
+Central Asia Standard Time~~~/softwarestudio.org/Tzfile/Asia/Dhaka
+Myanmar Standard Time~~~/softwarestudio.org/Tzfile/Asia/Rangoon
+SE Asia Standard Time~~~/softwarestudio.org/Tzfile/Asia/Bangkok
+North Asia Standard Time~~~/softwarestudio.org/Tzfile/Asia/Krasnoyarsk
+China Standard Time~~~/softwarestudio.org/Tzfile/Asia/Shanghai
+North Asia East Standard Time~~~/softwarestudio.org/Tzfile/Asia/Ulaanbaatar
+Singapore Standard Time~~~/softwarestudio.org/Tzfile/Asia/Singapore
+W. Australia Standard Time~~~/softwarestudio.org/Tzfile/Australia/Perth
+Taipei Standard Time~~~/softwarestudio.org/Tzfile/Asia/Taipei
+Tokyo Standard Time~~~/softwarestudio.org/Tzfile/Asia/Tokyo
+Korea Standard Time~~~/softwarestudio.org/Tzfile/Asia/Seoul
+Yakutsk Standard Time~~~/softwarestudio.org/Tzfile/Asia/Yakutsk
+Cen. Australia Standard Time~~~/softwarestudio.org/Tzfile/Australia/Adelaide
+AUS Central Standard Time~~~/softwarestudio.org/Tzfile/Australia/Darwin
+E. Australia Standard Time~~~/softwarestudio.org/Tzfile/Australia/Brisbane
+AUS Eastern Standard Time~~~/softwarestudio.org/Tzfile/Australia/Sydney
+West Pacific Standard Time~~~/softwarestudio.org/Tzfile/Pacific/Guam
+Tasmania Standard Time~~~/softwarestudio.org/Tzfile/Australia/Hobart
+Vladivostok Standard Time~~~/softwarestudio.org/Tzfile/Asia/Vladivostok
+Central Pacific Standard Time~~~/softwarestudio.org/Tzfile/Asia/Magadan
+New Zealand Standard Time~~~/softwarestudio.org/Tzfile/Pacific/Auckland
+Fiji Standard Time~~~/softwarestudio.org/Tzfile/Pacific/Fiji
+Tonga Standard Time~~~/softwarestudio.org/Tzfile/Pacific/Tongatapu
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]