diff options
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | composer/ChangeLog | 6 | ||||
-rw-r--r-- | composer/e-msg-composer.c | 27 | ||||
-rw-r--r-- | configure.in | 22 | ||||
-rw-r--r-- | mail/ChangeLog | 5 | ||||
-rw-r--r-- | mail/em-composer-utils.c | 21 | ||||
-rw-r--r-- | plugins/attachment-reminder/ChangeLog | 9 | ||||
-rw-r--r-- | plugins/attachment-reminder/Makefile.am | 46 | ||||
-rw-r--r-- | plugins/attachment-reminder/apps-evolution-attachment-reminder.schemas.in.in | 41 | ||||
-rw-r--r-- | plugins/attachment-reminder/attachment-reminder.c | 418 | ||||
-rw-r--r-- | plugins/attachment-reminder/attachment-reminder.glade | 180 | ||||
-rw-r--r-- | plugins/attachment-reminder/org-gnome-attachment-reminder.errors.xml | 10 | ||||
-rw-r--r-- | plugins/attachment-reminder/org-gnome-evolution-attachment-reminder.eplug.xml | 28 | ||||
-rw-r--r-- | plugins/tnef-attachments/ChangeLog | 4 | ||||
-rw-r--r-- | plugins/tnef-attachments/Makefile.am | 18 | ||||
-rw-r--r-- | plugins/tnef-attachments/org-gnome-tnef-attachments.eplug.xml | 25 | ||||
-rw-r--r-- | plugins/tnef-attachments/tnef-plugin.c | 1320 |
17 files changed, 2183 insertions, 4 deletions
@@ -1,3 +1,10 @@ +2007-07-09 Srinivasa Ragavan <sragavan@novell.com> + + ** Added Attachment reminder plugin from Johhny + ** Added initial tnef attachment plugin Lakke + + * configure.in: + 2007-07-03 Gilles Dartiguelongue <gdartigu@svn.gnome.org> * iconv-detect.c: fix iconv-detect.c, second part of bugĀ #444882 diff --git a/composer/ChangeLog b/composer/ChangeLog index b5d50def62..c7e20a0e83 100644 --- a/composer/ChangeLog +++ b/composer/ChangeLog @@ -1,3 +1,9 @@ +2007-07-08 Johnny Jacob <jjohnny@novell.com> + + * e-msg-composer.c : (e_msg_composer_get_raw_message_text ) Added. + (e_msg_composer_get_attachment_bar) : Added. Returns the reference + to the attachment bar. + 2007-06-18 Srinivasa Ragavan <sragavan@novell.com> ** Fix for bug #444107 diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c index 63052a3ff2..53f6ce70e9 100644 --- a/composer/e-msg-composer.c +++ b/composer/e-msg-composer.c @@ -6224,6 +6224,33 @@ e_msg_composer_unset_autosaved (EMsgComposer *composer) p->autosaved = FALSE; } +/** + * e_msg_composer_get_raw_message_text: + * + * Returns the text/plain of the message from composer + **/ +const char * +e_msg_composer_get_raw_message_text (EMsgComposer *composer) +{ + GByteArray *data = NULL; + EMsgComposerPrivate *p = composer->priv; + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); + + data = get_text (p->persist_stream_interface, "text/plain"); + if (data) + return data->data; + + return NULL; +} + +EAttachmentBar* +e_msg_composer_get_attachment_bar (EMsgComposer *composer) +{ + EMsgComposerPrivate *p = composer->priv; + + return (EAttachmentBar*) p->attachment_bar; +} + gboolean e_msg_composer_is_autosaved (EMsgComposer *composer) { diff --git a/configure.in b/configure.in index ded8dccd93..7432362366 100644 --- a/configure.in +++ b/configure.in @@ -1262,6 +1262,20 @@ else IPOD_SYNC="" fi +AC_MSG_CHECKING([for yTNEF]) +AC_TRY_COMPILE([#include <stdio.h> + #include <ytnef.h>], + [TNEFStruct *tnef;], tnef_ok=yes, tnef_ok=no) +if test "$tnef_ok" = "yes"; then + AC_MSG_RESULT([yes]) + TNEF_ATTACHMENTS="tnef-attachments" +else + AC_MSG_RESULT(no) + TNEF_ATTACHMENTS="" +fi + +echo "TNEF is "$TNEF_ATTACHMENTS + dnl --- Flags for the various libraries we build EVO_SET_COMPILE_FLAGS(CAMEL, camel-provider-$EDS_PACKAGE) @@ -1541,13 +1555,13 @@ plugins_base_always="calendar-file calendar-http calendar-weather itip-formatter plugins_base="$plugins_base_always $SA_JUNK_PLUGIN $BF_JUNK_PLUGIN $EXCHANGE_PLUGIN $MONO_PLUGIN" all_plugins_base="$plugins_base_always sa-junk-plugin bogo-junk-plugin exchange-operations mono" -plugins_standard_always="bbdb subject-thread save-calendar select-one-source copy-tool mail-to-task mark-calendar-offline audio-inline mailing-list-actions new-mail-notify default-mailer import-ics-attachments prefer-plain mail-notification" +plugins_standard_always="bbdb subject-thread save-calendar select-one-source copy-tool mail-to-task mark-calendar-offline audio-inline mailing-list-actions new-mail-notify default-mailer import-ics-attachments prefer-plain mail-notification attachment-reminder " plugins_standard="$plugins_standard_always" all_plugins_standard="$plugins_standard" plugins_experimental_always="backup-restore folder-unsubscribe mail-to-meeting mail-remote save-attachments" -plugins_experimental="$plugins_experimental_always $IPOD_SYNC" -all_plugins_experimental="$plugins_experimental_always ipod-sync" +plugins_experimental="$plugins_experimental_always $IPOD_SYNC $TNEF_ATTACHMENTS" +all_plugins_experimental="$plugins_experimental_always ipod-sync $TNEF_ATTACHMENTS" case x"$enable_plugins" in xno) @@ -1827,6 +1841,7 @@ plugins/calendar-weather/Makefile plugins/plugin-manager/Makefile plugins/bbdb/Makefile plugins/audio-inline/Makefile +plugins/attachment-reminder/Makefile plugins/mail-notification/Makefile plugins/mail-to-meeting/Makefile plugins/mail-to-task/Makefile @@ -1861,6 +1876,7 @@ plugins/ipod-sync/Makefile plugins/publish-calendar/Makefile plugins/import-ics-attachments/Makefile plugins/imap-features/Makefile +plugins/tnef-attachments/Makefile smime/Makefile smime/lib/Makefile smime/gui/Makefile diff --git a/mail/ChangeLog b/mail/ChangeLog index 7a19402b3f..f1c7ced0d7 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,3 +1,8 @@ +2007-07-08 Johnny Jacob <jjohnny@novell.com> + + * em-composer-utils.c : Added composer.presendcheck event. Fixes + #334118. + 2007-07-07 Srinivasa Ragavan <sragavan@novell.com> ** Fix for bug #317281 from Michael P. Lepore diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c index 942ac1e897..ca24ac3db0 100644 --- a/mail/em-composer-utils.c +++ b/mail/em-composer-utils.c @@ -256,6 +256,8 @@ composer_get_message (EMsgComposer *composer, gboolean save_html_object_data) EAccount *account; int i; GList *postlist; + EMEvent *eme; + EMEventTargetMessage *target; gconf = mail_config_get_gconf_client (); @@ -355,11 +357,28 @@ composer_get_message (EMsgComposer *composer, gboolean save_html_object_data) goto finished; } + /** @Event: composer.presendchecks + * @Title: Composer PreSend Checks + * @Target: EMEventTargetMessage + * + * composer.presendchecks is emitted during pre-checks for the message just before sending. + * Since the e-plugin framework doesn't provide a way to return a value from the plugin, + * use 'presend_check_status' to set whether the check passed / failed. + */ + eme = em_event_peek(); + target = em_event_target_new_composer (eme, composer, 0); + g_object_set_data (composer, "presend_check_status", GINT_TO_POINTER(0)); + + e_event_emit((EEvent *)eme, "composer.presendchecks", (EEventTarget *)target); + + if (GPOINTER_TO_INT (g_object_get_data (composer, "presend_check_status"))) + goto finished; + /* actually get the message now, this will sign/encrypt etc */ message = e_msg_composer_get_message (composer, save_html_object_data); if (message == NULL) goto finished; - + /* Add info about the sending account */ account = e_msg_composer_get_preferred_account (composer); diff --git a/plugins/attachment-reminder/ChangeLog b/plugins/attachment-reminder/ChangeLog new file mode 100644 index 0000000000..c1b47e9e58 --- /dev/null +++ b/plugins/attachment-reminder/ChangeLog @@ -0,0 +1,9 @@ +2007-07-09 Johnny Jacob <jjohnny@novell.com> + + ** Added attachment Reminder plugins + * apps-evolution-attachment-reminder.schemas.in.in: + * org-gnome-evolution-attachment-reminder.eplug.xml: + * attachment-reminder.c: + * Makefile.am: + * attachment-reminder.glade: + * org-gnome-attachment-reminder.errors.xml: diff --git a/plugins/attachment-reminder/Makefile.am b/plugins/attachment-reminder/Makefile.am new file mode 100644 index 0000000000..46eb95ad9f --- /dev/null +++ b/plugins/attachment-reminder/Makefile.am @@ -0,0 +1,46 @@ +INCLUDES = \ + -I$(top_builddir)/composer \ + -I$(top_srcdir) \ + $(EVOLUTION_MAIL_CFLAGS) \ + -DEVOLUTION_PLUGINDIR="\"$(plugindir)\"" + +error_DATA = \ + org-gnome-attachment-reminder.errors.xml + + +errordir = $(privdatadir)/errors + + +@EVO_PLUGIN_RULE@ + +plugin_DATA = \ + org-gnome-evolution-attachment-reminder.eplug \ + attachment-reminder.glade + +plugin_LTLIBRARIES = liborg-gnome-evolution-attachment-reminder.la + +liborg_gnome_evolution_attachment_reminder_la_SOURCES = attachment-reminder.c +liborg_gnome_evolution_attachment_reminder_la_LDFLAGS = -module -avoid-version + +schemadir = $(GCONF_SCHEMA_FILE_DIR) +schema_in_files = apps-evolution-attachment-reminder.schemas.in.in +schema_DATA = $(schema_in_files:.schemas.in.in=-$(BASE_VERSION).schemas) + +%-$(BASE_VERSION).schemas.in: %.schemas.in.in + cp $< $@ + +@INTLTOOL_SCHEMAS_RULE@ + +install-data-local: + if test -z "$(DESTDIR)" ; then \ + for p in $(schema_DATA) ; do \ + GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $$p; \ + done \ + fi + +EXTRA_DIST = org-gnome-evolution-attachment-reminder.eplug.xml \ + $(error_DATA) + +CLEANFILES = org-gnome-evolution-attachment-reminder.eplug + +DISTCLEANFILES = $(schema_DATA) diff --git a/plugins/attachment-reminder/apps-evolution-attachment-reminder.schemas.in.in b/plugins/attachment-reminder/apps-evolution-attachment-reminder.schemas.in.in new file mode 100644 index 0000000000..e8b62241e4 --- /dev/null +++ b/plugins/attachment-reminder/apps-evolution-attachment-reminder.schemas.in.in @@ -0,0 +1,41 @@ +<gconfschemafile> + <schemalist> + <schema> + <key>/schemas/apps/evolution/mail/prompts/attachment_presend_check</key> + <applyto>/apps/evolution/mail/prompts/attachment_presend_check</applyto> + <owner>evolution-mail</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Enable attachment reminder plugin</short> + <long>Enable attachment reminder plugin</long> + </locale> + </schema> + + + <!-- Labels and Colours --> + + <schema> + <key>/schemas/apps/evolution/mail/attachment_reminder_clues</key> + <applyto>/apps/evolution/mail/attachment_reminder_clues</applyto> + <owner>evolution-mail</owner> + <type>list</type> + <list_type>string</list_type> + + <!-- The following are the keywords used by the plugin to understand + whether the user wanted to send a attachment. The list can have any + number of strings.--> + + <default>[attachment,attaching,attached,enclosed]</default> + <locale name="C"> + <short>List of clues for the attachment reminder plugin to look for + in a message body</short> + <long> + List of clues for the attachment reminder plugin to look for + in a message body + </long> + </locale> + </schema> + </schemalist> +</gconfschemafile> + diff --git a/plugins/attachment-reminder/attachment-reminder.c b/plugins/attachment-reminder/attachment-reminder.c new file mode 100644 index 0000000000..562aa08f71 --- /dev/null +++ b/plugins/attachment-reminder/attachment-reminder.c @@ -0,0 +1,418 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Author: Johnny Jacob <jjohnny@novell.com> + * + * Copyright 2007 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <string.h> + +#include <gconf/gconf-client.h> + +#include <e-util/e-config.h> +#include <mail/em-config.h> +#include <mail/em-event.h> +#include <camel/camel-mime-message.h> +#include <camel/camel-medium.h> +#include <camel/camel-mime-message.h> +#include <camel/camel-multipart.h> +#include <camel/camel-stream.h> +#include <camel/camel-stream-mem.h> +#include <camel/camel-mime-message.h> +#include <camel/camel-multipart.h> + +#include <e-util/e-error.h> +#include <mail/em-utils.h> + +#include "composer/e-msg-composer.h" +#include "composer/e-msg-composer-attachment-bar.h" +#include "widgets/misc/e-attachment-bar.h" + +#define GCONF_KEY_ATTACHMENT_REMINDER "/apps/evolution/mail/prompts/attachment_presend_check" +#define GCONF_KEY_ATTACH_REMINDER_CLUES "/apps/evolution/mail/attachment_reminder_clues" + +typedef struct { + GConfClient *gconf; + GtkWidget *treeview; + GtkWidget *clue_add; + GtkWidget *clue_edit; + GtkWidget *clue_remove; + GtkWidget *clue_container; +} UIData; + + +enum { + CLUE_KEYWORD_COLUMN, + CLUE_N_COLUMNS, +}; + +int e_plugin_lib_enable (EPluginLib *ep, int enable); +void org_gnome_evolution_attachment_reminder (EPlugin *ep, EMEventTargetComposer *t); +static gboolean ask_for_missing_attachment (GtkWindow *widget); +static gboolean check_for_attachment_clues (gchar *msg); +static gboolean check_for_attachment (EMsgComposer *composer); +static gchar* strip_text_msg (gchar *msg); +static void toggle_cb (GtkWidget *widget, UIData *ui); +static void commit_changes (UIData *ui); + +static void cell_edited_callback (GtkCellRendererText *cell, gchar *path_string, + gchar *new_text,UIData *ui); + +static GtkListStore *store = NULL; + +int +e_plugin_lib_enable (EPluginLib *ep, int enable) +{ + return 0; +} + +void org_gnome_evolution_attachment_reminder (EPlugin *ep, EMEventTargetComposer *t) +{ + GConfClient *gconf; + char *rawstr = NULL, *filtered_str = NULL; + gint parts = 2; + + gconf = gconf_client_get_default (); + if (!gconf_client_get_bool (gconf, GCONF_KEY_ATTACHMENT_REMINDER, NULL)) + { + g_object_unref (gconf); + return; + } + else + g_object_unref (gconf); + + rawstr = g_strdup (e_msg_composer_get_raw_message_text (t->composer)); + + filtered_str = strip_text_msg (rawstr); + + g_free (rawstr); + + /* Set presend_check_status for the composer*/ + if (check_for_attachment_clues (filtered_str) && !check_for_attachment (t->composer)) + if (!ask_for_missing_attachment ((GtkWindow *)t->composer)) + g_object_set_data ((GObject *) t->composer, "presend_check_status", GINT_TO_POINTER(1)); + + g_free (filtered_str); + + return ; +} + +static gboolean ask_for_missing_attachment (GtkWindow *window) +{ + return em_utils_prompt_user(window, GCONF_KEY_ATTACHMENT_REMINDER ,"org.gnome.evolution.plugins.attachment_reminder:attachment-reminder", NULL); +} + +/* check for the clues */ +static gboolean check_for_attachment_clues (gchar *msg) +{ + //TODO : Add more strings. RegEx ??? + + GConfClient *gconf; + GSList *clue_list = NULL; + + gconf = gconf_client_get_default (); + + //Get the list from gconf + clue_list = gconf_client_get_list ( gconf, GCONF_KEY_ATTACH_REMINDER_CLUES, GCONF_VALUE_STRING, NULL ); + + guint msg_length = strlen (msg); + + for (;clue_list;clue_list=g_slist_next(clue_list)) { + if (g_strstr_len (msg, msg_length, g_utf8_strdown (clue_list->data, g_utf8_strlen (clue_list->data, -1) ) )) + return TRUE; + } + + return FALSE; +} + +/* check for the any attachment */ +static gboolean check_for_attachment (EMsgComposer *composer) +{ + EAttachmentBar* bar = e_msg_composer_get_attachment_bar (composer); + + if (e_attachment_bar_get_num_attachments (bar)) + return TRUE; + + return FALSE; +} + +static gchar* strip_text_msg (gchar *msg) +{ + gchar **lines = g_strsplit ( msg, "\n", -1); + gchar *stripped_msg = g_strdup (" "); + + guint i=0; + + while (lines [i]) + { + if (lines [i] != NULL && !g_str_has_prefix (g_strstrip(lines[i]), ">")) + { + gchar *temp = stripped_msg; + stripped_msg = g_strconcat (" ", stripped_msg, lines[i], NULL); + + g_free (temp); + } + i++; + } + + g_strfreev (lines); + + return g_utf8_strdown (stripped_msg, g_utf8_strlen (stripped_msg, -1)); +} + +static void +commit_changes (UIData *ui) +{ + GtkTreeModel *model = NULL; + GSList *clue_list = NULL; + GtkTreeIter iter; + gboolean valid; + GConfClient *client; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview)); + valid = gtk_tree_model_get_iter_first (model, &iter); + + while (valid) { + char *keyword; + + gtk_tree_model_get (model, &iter, CLUE_KEYWORD_COLUMN, &keyword, -1); + clue_list = g_slist_append (clue_list, keyword); + valid = gtk_tree_model_iter_next (model, &iter); + } + + client = gconf_client_get_default (); + gconf_client_set_list (client, GCONF_KEY_ATTACH_REMINDER_CLUES, GCONF_VALUE_STRING, clue_list, NULL); + + g_slist_foreach (clue_list, (GFunc) g_free, NULL); + g_slist_free (clue_list); +} + +static void cell_edited_callback (GtkCellRendererText *cell, + gchar *path_string, + gchar *new_text, + UIData *ui) +{ + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview)); + + gtk_tree_model_get_iter_from_string (model, &iter, path_string); + + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + CLUE_KEYWORD_COLUMN, new_text, -1); + + commit_changes (ui); +} + +static void +clue_add_clicked (GtkButton *button, UIData *ui) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gchar *new_clue = NULL; + GtkTreeViewColumn *focus_col; + GtkTreePath *path; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview)); + + //TODO : Trim and check for blank strings + new_clue = g_strdup (""); + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + CLUE_KEYWORD_COLUMN, new_clue, -1); + + focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (ui->treeview), CLUE_KEYWORD_COLUMN); + path = gtk_tree_model_get_path (model, &iter); + + if (path) { + gtk_tree_view_set_cursor (GTK_TREE_VIEW (ui->treeview), path, focus_col, TRUE); + + } + +} + +static void +clue_remove_clicked (GtkButton *button, UIData *ui) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + gint len; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview)); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return; + + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + + len = gtk_tree_model_iter_n_children (model, NULL); + if (len > 0) { + gtk_tree_selection_select_iter (selection, &iter); + } else { + gtk_widget_set_sensitive (ui->clue_edit, FALSE); + gtk_widget_set_sensitive (ui->clue_remove, FALSE); + } + + commit_changes (ui); +} + +static void +clue_edit_clicked (GtkButton *button, UIData *ui) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + GtkTreeViewColumn *focus_col; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview)); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return; + + focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (ui->treeview), CLUE_KEYWORD_COLUMN); + path = gtk_tree_model_get_path (model, &iter); + + if (path) { + gtk_tree_view_set_cursor (GTK_TREE_VIEW (ui->treeview), path, focus_col, TRUE); + + gtk_tree_path_free (path); + } +} + + +static void +toggle_cb (GtkWidget *widget, UIData *ui) +{ + + gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + GConfClient *gconf = gconf_client_get_default(); + + gconf_client_set_bool (gconf, GCONF_KEY_ATTACHMENT_REMINDER, active, NULL); + gtk_widget_set_sensitive (ui->clue_container, active); +} + +static void +selection_changed (GtkTreeSelection *selection, UIData *ui) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gtk_widget_set_sensitive (ui->clue_edit, TRUE); + gtk_widget_set_sensitive (ui->clue_remove, TRUE); + } else { + gtk_widget_set_sensitive (ui->clue_edit, FALSE); + gtk_widget_set_sensitive (ui->clue_remove, FALSE); + } +} + +/* Configuration in Mail Prefs Page goes here */ + +GtkWidget * +org_gnome_attachment_reminder_config_option (struct _EPlugin *epl, struct _EConfigHookItemFactoryData *data) +{ + GtkWidget *check; + GtkVBox *parent_container = (GtkVBox *) (data->parent); + GladeXML *xml; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + GtkTreeIter iter; + GConfClient *gconf = gconf_client_get_default(); + GtkWidget *hbox, *vbox, *button; + GSList *clue_list = NULL; + gboolean enable_ui; + + UIData *ui = g_new0 (UIData, 1); + + char *gladefile; + + gladefile = g_build_filename (EVOLUTION_PLUGINDIR, + "attachment-reminder.glade", + NULL); + xml = glade_xml_new (gladefile, "reminder_configuration_box", NULL); + g_free (gladefile); + + if (data->old) + return data->old; + + + ui->gconf = gconf_client_get_default (); + enable_ui = gconf_client_get_bool (ui->gconf, GCONF_KEY_ATTACHMENT_REMINDER, NULL); + + ui->treeview = glade_xml_get_widget (xml, "clue_treeview"); + + if (store == NULL) + store = gtk_list_store_new (CLUE_N_COLUMNS, G_TYPE_STRING); + else + gtk_list_store_clear (store); + + gtk_tree_view_set_model (GTK_TREE_VIEW (ui->treeview), GTK_TREE_MODEL (store)); + + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (ui->treeview), -1, _("Keywords"), + renderer, "text", CLUE_KEYWORD_COLUMN, NULL); + g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL); + g_signal_connect(renderer, "edited", (GCallback) cell_edited_callback, ui); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (selection_changed), ui); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ui->treeview), TRUE); + + ui->clue_add = glade_xml_get_widget (xml, "clue_add"); + g_signal_connect (G_OBJECT (ui->clue_add), "clicked", G_CALLBACK (clue_add_clicked), ui); + + ui->clue_remove = glade_xml_get_widget (xml, "clue_remove"); + g_signal_connect (G_OBJECT (ui->clue_remove), "clicked", G_CALLBACK (clue_remove_clicked), ui); + gtk_widget_set_sensitive (ui->clue_remove, FALSE); + + ui->clue_edit = glade_xml_get_widget (xml, "clue_edit"); + g_signal_connect (G_OBJECT (ui->clue_edit), "clicked", G_CALLBACK (clue_edit_clicked), ui); + gtk_widget_set_sensitive (ui->clue_edit, FALSE); + + /* Populate tree view with values from gconf */ + clue_list = gconf_client_get_list ( gconf, GCONF_KEY_ATTACH_REMINDER_CLUES, GCONF_VALUE_STRING, NULL ); + + while (clue_list){ + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + CLUE_KEYWORD_COLUMN, clue_list->data, -1); + + clue_list = g_slist_next (clue_list); + } + + /* Enable / Disable */ + gconf = gconf_client_get_default (); + button = glade_xml_get_widget (xml, "reminder_enable_check"); + gtk_toggle_button_set_active (button , enable_ui); + g_signal_connect (G_OBJECT (button), "toggled", toggle_cb, ui); + + /* Add the list here */ + ui->clue_container = glade_xml_get_widget (xml, "clue_container"); + gtk_widget_set_sensitive (ui->clue_container, enable_ui); + + hbox = glade_xml_get_widget (xml, "reminder_configuration_box"); + gtk_box_pack_start (parent_container, hbox, FALSE, FALSE, 0); + gtk_widget_show_all (hbox); + + return (GtkWidget *)check; +} + diff --git a/plugins/attachment-reminder/attachment-reminder.glade b/plugins/attachment-reminder/attachment-reminder.glade new file mode 100644 index 0000000000..82d905685d --- /dev/null +++ b/plugins/attachment-reminder/attachment-reminder.glade @@ -0,0 +1,180 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="window1"> + <property name="visible">True</property> + <property name="title" translatable="yes">window1</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="reminder_configuration_box"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkCheckButton" id="reminder_enable_check"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Remind missing attachments</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="clue_container"> + <property name="visible">True</property> + <property name="n_rows">1</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">7</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="clue_treeview"> + <property name="border_width">1</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkVButtonBox" id="vbuttonbox2"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_START</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkButton" id="clue_add"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-add</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="clue_edit"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-edit</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="clue_remove"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-remove</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_padding">12</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/plugins/attachment-reminder/org-gnome-attachment-reminder.errors.xml b/plugins/attachment-reminder/org-gnome-attachment-reminder.errors.xml new file mode 100644 index 0000000000..f27f71b12e --- /dev/null +++ b/plugins/attachment-reminder/org-gnome-attachment-reminder.errors.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<error-list domain="org.gnome.evolution.plugins.attachment_reminder"> + <error id="attachment-reminder" type="info"> + <primary>Attachment Reminder</primary> + <secondary>Looks like you have missed the attachments</secondary> + <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/> + <button response="GTK_RESPONSE_YES" label="_Send"></button> + </error> +</error-list> diff --git a/plugins/attachment-reminder/org-gnome-evolution-attachment-reminder.eplug.xml b/plugins/attachment-reminder/org-gnome-evolution-attachment-reminder.eplug.xml new file mode 100644 index 0000000000..6d6a3e4d82 --- /dev/null +++ b/plugins/attachment-reminder/org-gnome-evolution-attachment-reminder.eplug.xml @@ -0,0 +1,28 @@ +<e-plugin-list> + <e-plugin + type="shlib" + id="org.gnome.evolution.attachment-reminder" + _name="Attachment Reminder" + location="@PLUGINDIR@/liborg-gnome-evolution-attachment-reminder@SOEXT@"> + + <_description>Looks for clues in a message for mention of + attachments and warns if the attachment is missing</_description> + + <author name="Johnny Jacob" email="jjohnny@novell.com"/> + + <hook class="org.gnome.evolution.mail.events:1.0"> + <event + id="composer.presendchecks" + handle="org_gnome_evolution_attachment_reminder" + target="message" + /> + </hook> + + <hook class="org.gnome.evolution.mail.config:1.0"> + <group target="prefs" id="org.gnome.evolution.mail.composerPrefs"> + <item type="section" path="00.general/10.alerts/" factory="org_gnome_attachment_reminder_config_option"/> + </group> + </hook> + + </e-plugin> +</e-plugin-list> diff --git a/plugins/tnef-attachments/ChangeLog b/plugins/tnef-attachments/ChangeLog new file mode 100644 index 0000000000..6008e4b513 --- /dev/null +++ b/plugins/tnef-attachments/ChangeLog @@ -0,0 +1,4 @@ +2007-06-07 Lucky Wankhede <wlakke@novell.com> + * New implementation for tnef + * For reading "winmail.dat" kind of attachements + * using libytnef diff --git a/plugins/tnef-attachments/Makefile.am b/plugins/tnef-attachments/Makefile.am new file mode 100644 index 0000000000..5f9c56b242 --- /dev/null +++ b/plugins/tnef-attachments/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -DGETTEXT_PACKAGE="\"$(GETTEXT_PACKAGE)\"" \ + -DLOCALEDIR="\"$(LOCALEDIR)\"" \ + $(EVOLUTION_MAIL_CFLAGS) + +@EVO_PLUGIN_RULE@ + +plugin_DATA = org-gnome-tnef-attachments.eplug +plugin_LTLIBRARIES = liborg-gnome-tnef-attachments.la + +liborg_gnome_tnef_attachments_la_SOURCES = tnef-plugin.c +liborg_gnome_tnef_attachments_la_LDFLAGS = -module -avoid-version -lytnef + +EXTRA_DIST = org-gnome-tnef-attachments.eplug.xml + +BUILT_SOURCES = $(plugin_DATA) +CLEANFILES = $(BUILT_SOURCES) diff --git a/plugins/tnef-attachments/org-gnome-tnef-attachments.eplug.xml b/plugins/tnef-attachments/org-gnome-tnef-attachments.eplug.xml new file mode 100644 index 0000000000..1315f25e2f --- /dev/null +++ b/plugins/tnef-attachments/org-gnome-tnef-attachments.eplug.xml @@ -0,0 +1,25 @@ +<?xml version="1.0"?> +<e-plugin-list> + <e-plugin + type="shlib" + id="org.gnome.evolution.mail.tnefattachments" + location="+PLUGIN_INSTALL_DIR+/liborg-gnome-tnef-attachments.so" + domain="+GETTEXT_PACKAGE+" + localedir="+LOCALEDIR+" + _name="yTNEF decoder"> + <_description>A simple plugin which uses ytnef to decode tnef attachments.</_description> + <author name="Lucky Wankhede" email="wlakke@novell.com"/> + <author name="Michael Zucchi" email="notzed@ximian.com"/> + + <hook class="org.gnome.evolution.mail.format:1.0"> + <group id="EMFormatHTML"> + <item flags="inline_disposition" mime_type="application/vnd.ms-tnef" + format="org_gnome_notzed_format_tnef"/> + </group> + <group id="EMFormatHTMLDisplay"> + <item flags="inline_disposition" mime_type="application/vnd.ms-tnef" + format="org_gnome_notzed_format_tnef"/> + </group> + </hook> + </e-plugin> +</e-plugin-list> diff --git a/plugins/tnef-attachments/tnef-plugin.c b/plugins/tnef-attachments/tnef-plugin.c new file mode 100644 index 0000000000..3b134c0565 --- /dev/null +++ b/plugins/tnef-attachments/tnef-plugin.c @@ -0,0 +1,1320 @@ +/* Copyright (C) 2005 Novell Inc. */ +/* This file is licensed under the GNU GPL v2 */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* We include gi18n-lib.h so that we have strings translated directly for this package */ +#include <glib/gi18n-lib.h> +#include <string.h> +#include <stdio.h> + +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <stdlib.h> +#include <ytnef.h> + +#include <camel/camel-mime-part.h> +#include <camel/camel-folder.h> +#include <camel/camel-exception.h> +#include <camel/camel-multipart.h> +#include <camel/camel-stream-fs.h> + +#include <mail/em-format.h> +#include <mail/em-format-hook.h> +#include <mail/em-utils.h> +#include <e-util/e-error.h> +#include <e-util/e-mktemp.h> + +int verbose = 0; +int saveRTF = 0; +int saveintermediate = 0; +char *filepath = NULL; +void processTnef(TNEFStruct tnef); +void saveVCalendar(TNEFStruct tnef); +void saveVCard(TNEFStruct tnef); +void saveVTask(TNEFStruct tnef); + +void org_gnome_notzed_format_tnef(void *ep, EMFormatHookTarget *t); + +void +org_gnome_notzed_format_tnef(void *ep, EMFormatHookTarget *t) +{ + char *tmpdir = NULL, *name = NULL, *cmd; + CamelStream *out; + struct dirent *d; + DIR *dir; + CamelMultipart *mp; + CamelMimePart *mainpart; + CamelDataWrapper *content; + int len; + TNEFStruct tnef; + + tmpdir = e_mkdtemp("tnef-attachment-XXXXXX"); + if (tmpdir == NULL) + return; + + filepath = tmpdir; + + name = g_build_filename(tmpdir, ".evo-attachment.tnef", NULL); + + out = camel_stream_fs_new_with_name(name, O_RDWR|O_CREAT, 0666); + if (out == NULL) + goto fail; + content = camel_medium_get_content_object((CamelMedium *)t->part); + if (content == NULL) + goto fail; + if (camel_data_wrapper_decode_to_stream(content, out) == -1 + || camel_stream_close(out) == -1) { + camel_object_unref(out); + goto fail; + } + camel_object_unref(out); + + /* Extracting the winmail.dat */ + TNEFInitialize(&tnef); + tnef.Debug = verbose; + if (TNEFParseFile(name, &tnef) == -1) { + printf("ERROR processing file\n"); + } + processTnef(tnef); + + TNEFFree(&tnef); + /* Extraction done */ + + dir = opendir(tmpdir); + if (dir == NULL) + goto fail; + + mainpart = camel_mime_part_new(); + + mp = camel_multipart_new(); + camel_data_wrapper_set_mime_type((CamelDataWrapper *)mp, "multipart/mixed"); + camel_multipart_set_boundary(mp, NULL); + + camel_medium_set_content_object((CamelMedium *)mainpart, (CamelDataWrapper *)mp); + + while ((d = readdir(dir))) { + CamelMimePart *part; + CamelDataWrapper *content; + CamelStream *stream; + char *path; + const char *type; + + if (!strcmp(d->d_name, ".") + || !strcmp(d->d_name, "..") + || !strcmp(d->d_name, ".evo-attachment.tnef")) + continue; + + path = g_build_filename(tmpdir, d->d_name, NULL); + + stream = camel_stream_fs_new_with_name(path, O_RDONLY, 0); + content = camel_data_wrapper_new(); + camel_data_wrapper_construct_from_stream(content, stream); + camel_object_unref(stream); + + part = camel_mime_part_new(); + camel_mime_part_set_encoding(part, CAMEL_TRANSFER_ENCODING_BINARY); + + camel_medium_set_content_object((CamelMedium *)part, content); + camel_object_unref(content); + + type = em_utils_snoop_type(part); + if (type) + camel_data_wrapper_set_mime_type((CamelDataWrapper *)part, type); + + camel_mime_part_set_filename(part, d->d_name); + + g_free(path); + + camel_multipart_add_part(mp, part); + } + + closedir(dir); + + len = t->format->part_id->len; + g_string_append_printf(t->format->part_id, ".tnef"); + + if (camel_multipart_get_number(mp) > 0) + em_format_part_as(t->format, t->stream, mainpart, "multipart/mixed"); + else if (t->item->handler.old) + t->item->handler.old->handler(t->format, t->stream, t->part, t->item->handler.old); + + g_string_truncate(t->format->part_id, len); + + camel_object_unref(mainpart); + + goto ok; + fail: + if (t->item->handler.old) + t->item->handler.old->handler(t->format, t->stream, t->part, t->item->handler.old); + ok: + g_free(name); + g_free(tmpdir); +} + + +int e_plugin_lib_enable(EPluginLib *ep, int enable); + +int +e_plugin_lib_enable(EPluginLib *ep, int enable) +{ + if (enable) { + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + } + return 0; +} + +void processTnef(TNEFStruct tnef) { + char *astring; + variableLength *filename; + variableLength *filedata; + Attachment *p; + int RealAttachment; + int object; + char ifilename[256]; + int i, count; + int foundCal=0; + + FILE *fptr; + + /* First see if this requires special processing. */ + /* ie: it's a Contact Card, Task, or Meeting request (vCal/vCard) */ + if (tnef.messageClass[0] != 0) { + if (strcmp(tnef.messageClass, "IPM.Contact") == 0) { + saveVCard(tnef ); + } + if (strcmp(tnef.messageClass, "IPM.Task") == 0) { + saveVTask(tnef); + } + if (strcmp(tnef.messageClass, "IPM.Appointment") == 0) { + saveVCalendar(tnef); + foundCal = 1; + } + } + + if ((filename = MAPIFindUserProp(&(tnef.MapiProperties), + PROP_TAG(PT_STRING8,0x24))) != MAPI_UNDEFINED) { + if (strcmp(filename->data, "IPM.Appointment") == 0) { + /* If it's "indicated" twice, we don't want to save 2 calendar entries. */ + if (foundCal == 0) { + saveVCalendar(tnef); + } + } + } + + if (strcmp(tnef.messageClass, "IPM.Microsoft Mail.Note") == 0) { + if ((saveRTF == 1) && (tnef.subject.size > 0)) { + /* Description */ + if ((filename=MAPIFindProperty(&(tnef.MapiProperties), + PROP_TAG(PT_BINARY, PR_RTF_COMPRESSED))) + != MAPI_UNDEFINED) { + int size; + variableLength buf; + buf.data=""; + buf.size=0; + if ((buf.data = DecompressRTF(filename, &(buf.size))) != NULL) { + if (filepath == NULL) { + sprintf(ifilename, "%s.rtf", tnef.subject.data); + } else { + sprintf(ifilename, "%s/%s.rtf", filepath, tnef.subject.data); + } + for(i=0; i<strlen(ifilename); i++) + if (ifilename[i] == ' ') + ifilename[i] = '_'; + + if ((fptr = fopen(ifilename, "wb"))==NULL) { + printf("ERROR: Error writing file to disk!"); + } else { + fwrite(buf.data, + sizeof(BYTE), + buf.size, + fptr); + fclose(fptr); + } + free(buf.data); + buf.data=""; + buf.size=0; + } + } + } + } + + /* Now process each attachment */ + p = tnef.starting_attach.next; + count = 0; + while (p != NULL) { + count++; + /* Make sure it has a size. */ + if (p->FileData.size > 0) { + object = 1; + + /* See if the contents are stored as "attached data" */ + /* Inside the MAPI blocks. */ + if((filedata = MAPIFindProperty(&(p->MAPI), + PROP_TAG(PT_OBJECT, PR_ATTACH_DATA_OBJ))) + == MAPI_UNDEFINED) { + if((filedata = MAPIFindProperty(&(p->MAPI), + PROP_TAG(PT_BINARY, PR_ATTACH_DATA_OBJ))) + == MAPI_UNDEFINED) { + /* Nope, standard TNEF stuff. */ + filedata = &(p->FileData); + object = 0; + } + } + /* See if this is an embedded TNEF stream. */ + RealAttachment = 1; + if (object == 1) { + /* This is an "embedded object", so skip the */ + /* 16-byte identifier first. */ + TNEFStruct emb_tnef; + DWORD signature; + memcpy(&signature, filedata->data+16, sizeof(DWORD)); + if (TNEFCheckForSignature(signature) == 0) { + /* Has a TNEF signature, so process it. */ + TNEFInitialize(&emb_tnef); + emb_tnef.Debug = tnef.Debug; + if (TNEFParseMemory(filedata->data+16, + filedata->size-16, &emb_tnef) != -1) { + preocessTnef(emb_tnef); + RealAttachment = 0; + } + TNEFFree(&emb_tnef); + } + } else { + TNEFStruct emb_tnef; + DWORD signature; + memcpy(&signature, filedata->data, sizeof(DWORD)); + if (TNEFCheckForSignature(signature) == 0) { + /* Has a TNEF signature, so process it. */ + TNEFInitialize(&emb_tnef); + emb_tnef.Debug = tnef.Debug; + if (TNEFParseMemory(filedata->data, + filedata->size, &emb_tnef) != -1) { + preocessTnef(emb_tnef); + RealAttachment = 0; + } + TNEFFree(&emb_tnef); + } + } + if ((RealAttachment == 1) || (saveintermediate == 1)) { + /* Ok, it's not an embedded stream, so now we */ + /* process it. */ + if ((filename = MAPIFindProperty(&(p->MAPI), + PROP_TAG(30,0x3707))) + == MAPI_UNDEFINED) { + if ((filename = MAPIFindProperty(&(p->MAPI), + PROP_TAG(30,0x3001))) + == MAPI_UNDEFINED) { + filename = &(p->Title); + } + } + if (filename->size == 1) { + filename = (variableLength*)malloc(sizeof(variableLength)); + filename->size = 20; + filename->data = (char*)malloc(20); + sprintf(filename->data, "file_%03i.dat", count); + } + if (filepath == NULL) { + sprintf(ifilename, "%s", filename->data); + } else { + sprintf(ifilename, "%s/%s", filepath, filename->data); + } + for(i=0; i<strlen(ifilename); i++) + if (ifilename[i] == ' ') + ifilename[i] = '_'; + + if ((fptr = fopen(ifilename, "wb"))==NULL) { + printf("ERROR: Error writing file to disk!"); + } else { + if (object == 1) { + fwrite(filedata->data + 16, + sizeof(BYTE), + filedata->size - 16, + fptr); + } else { + fwrite(filedata->data, + sizeof(BYTE), + filedata->size, + fptr); + } + fclose(fptr); + } + } + } /* if size>0 */ + p=p->next; + } /* while p!= null */ +} + +void saveVCard(TNEFStruct tnef) { + char ifilename[512]; + FILE *fptr; + variableLength *vl; + variableLength *pobox, *street, *city, *state, *zip, *country; + dtr thedate; + int boolean,index,i; + + if ((vl = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_DISPLAY_NAME))) == MAPI_UNDEFINED) { + if ((vl=MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_COMPANY_NAME))) == MAPI_UNDEFINED) { + if (tnef.subject.size > 0) { + if (filepath == NULL) { + sprintf(ifilename, "%s.vcard", tnef.subject.data); + } else { + sprintf(ifilename, "%s/%s.vcard", filepath, tnef.subject.data); + } + } else { + if (filepath == NULL) { + sprintf(ifilename, "unknown.vcard"); + } else { + sprintf(ifilename, "%s/unknown.vcard", filepath); + } + } + } else { + if (filepath == NULL) { + sprintf(ifilename, "%s.vcard", vl->data); + } else { + sprintf(ifilename, "%s/%s.vcard", filepath, vl->data); + } + } + } else { + if (filepath == NULL) { + sprintf(ifilename, "%s.vcard", vl->data); + } else { + sprintf(ifilename, "%s/%s.vcard", filepath, vl->data); + } + } + for(i=0; i<strlen(ifilename); i++) + if (ifilename[i] == ' ') + ifilename[i] = '_'; + printf("%s\n", ifilename); + + if ((fptr = fopen(ifilename, "wb"))==NULL) { + printf("Error writing file to disk!"); + } else { + fprintf(fptr, "BEGIN:VCARD\n"); + fprintf(fptr, "VERSION:2.1\n"); + if (vl != MAPI_UNDEFINED) { + fprintf(fptr, "FN:%s\n", vl->data); + } + fprintProperty(tnef, fptr, PT_STRING8, PR_NICKNAME, "NICKNAME:%s\n"); + fprintUserProp(tnef, fptr, PT_STRING8, 0x8554, "MAILER:Microsoft Outlook %s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_SPOUSE_NAME, "X-EVOLUTION-SPOUSE:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_MANAGER_NAME, "X-EVOLUTION-MANAGER:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_ASSISTANT, "X-EVOLUTION-ASSISTANT:%s\n"); + + /* Organizational */ + if ((vl=MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_COMPANY_NAME))) != MAPI_UNDEFINED) { + if (vl->size > 0) { + if ((vl->size == 1) && (vl->data[0] == 0)) { + } else { + fprintf(fptr,"ORG:%s", vl->data); + if ((vl=MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_DEPARTMENT_NAME))) != MAPI_UNDEFINED) { + fprintf(fptr,";%s", vl->data); + } + fprintf(fptr, "\n"); + } + } + } + + fprintProperty(tnef, fptr, PT_STRING8, PR_OFFICE_LOCATION, "X-EVOLUTION-OFFICE:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_TITLE, "TITLE:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_PROFESSION, "ROLE:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_BODY, "NOTE:%s\n"); + if (tnef.body.size > 0) { + fprintf(fptr, "NOTE;QUOTED-PRINTABLE:"); + quotedfprint(fptr, &(tnef.body)); + fprintf(fptr,"\n"); + } + + + /* Business Address */ + boolean = 0; + if ((pobox = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_POST_OFFICE_BOX))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((street = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_STREET_ADDRESS))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((city = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_LOCALITY))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((state = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_STATE_OR_PROVINCE))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((zip = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_POSTAL_CODE))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((country = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_COUNTRY))) != MAPI_UNDEFINED) { + boolean = 1; + } + if (boolean == 1) { + fprintf(fptr, "ADR;QUOTED-PRINTABLE;WORK:"); + if (pobox != MAPI_UNDEFINED) { + quotedfprint(fptr, pobox); + } + fprintf(fptr, ";;"); + if (street != MAPI_UNDEFINED) { + quotedfprint(fptr, street); + } + fprintf(fptr, ";"); + if (city != MAPI_UNDEFINED) { + quotedfprint(fptr, city); + } + fprintf(fptr, ";"); + if (state != MAPI_UNDEFINED) { + quotedfprint(fptr, state); + } + fprintf(fptr, ";"); + if (zip != MAPI_UNDEFINED) { + quotedfprint(fptr, zip); + } + fprintf(fptr, ";"); + if (country != MAPI_UNDEFINED) { + quotedfprint(fptr, country); + } + fprintf(fptr,"\n"); + if ((vl = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x801b))) != MAPI_UNDEFINED) { + fprintf(fptr, "LABEL;QUOTED-PRINTABLE;WORK:"); + quotedfprint(fptr, vl); + fprintf(fptr,"\n"); + } + } + + /* Home Address */ + boolean = 0; + if ((pobox = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_HOME_ADDRESS_POST_OFFICE_BOX))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((street = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_HOME_ADDRESS_STREET))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((city = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_HOME_ADDRESS_CITY))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((state = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_HOME_ADDRESS_STATE_OR_PROVINCE))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((zip = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_HOME_ADDRESS_POSTAL_CODE))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((country = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_HOME_ADDRESS_COUNTRY))) != MAPI_UNDEFINED) { + boolean = 1; + } + if (boolean == 1) { + fprintf(fptr, "ADR;QUOTED-PRINTABLE;HOME:"); + if (pobox != MAPI_UNDEFINED) { + quotedfprint(fptr, pobox); + } + fprintf(fptr, ";;"); + if (street != MAPI_UNDEFINED) { + quotedfprint(fptr, street); + } + fprintf(fptr, ";"); + if (city != MAPI_UNDEFINED) { + quotedfprint(fptr, city); + } + fprintf(fptr, ";"); + if (state != MAPI_UNDEFINED) { + quotedfprint(fptr, state); + } + fprintf(fptr, ";"); + if (zip != MAPI_UNDEFINED) { + quotedfprint(fptr, zip); + } + fprintf(fptr, ";"); + if (country != MAPI_UNDEFINED) { + quotedfprint(fptr, country); + } + fprintf(fptr,"\n"); + if ((vl = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x801a))) != MAPI_UNDEFINED) { + fprintf(fptr, "LABEL;QUOTED-PRINTABLE;WORK:"); + quotedfprint(fptr, vl); + fprintf(fptr,"\n"); + } + } + + /* Other Address */ + boolean = 0; + if ((pobox = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_OTHER_ADDRESS_POST_OFFICE_BOX))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((street = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_OTHER_ADDRESS_STREET))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((city = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_OTHER_ADDRESS_CITY))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((state = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_OTHER_ADDRESS_STATE_OR_PROVINCE))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((zip = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_OTHER_ADDRESS_POSTAL_CODE))) != MAPI_UNDEFINED) { + boolean = 1; + } + if ((country = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_OTHER_ADDRESS_COUNTRY))) != MAPI_UNDEFINED) { + boolean = 1; + } + if (boolean == 1) { + fprintf(fptr, "ADR;QUOTED-PRINTABLE;OTHER:"); + if (pobox != MAPI_UNDEFINED) { + quotedfprint(fptr, pobox); + } + fprintf(fptr, ";;"); + if (street != MAPI_UNDEFINED) { + quotedfprint(fptr, street); + } + fprintf(fptr, ";"); + if (city != MAPI_UNDEFINED) { + quotedfprint(fptr, city); + } + fprintf(fptr, ";"); + if (state != MAPI_UNDEFINED) { + quotedfprint(fptr, state); + } + fprintf(fptr, ";"); + if (zip != MAPI_UNDEFINED) { + quotedfprint(fptr, zip); + } + fprintf(fptr, ";"); + if (country != MAPI_UNDEFINED) { + quotedfprint(fptr, country); + } + fprintf(fptr,"\n"); + } + + + fprintProperty(tnef, fptr, PT_STRING8, PR_CALLBACK_TELEPHONE_NUMBER, "TEL;X-EVOLUTION-CALLBACK:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_PRIMARY_TELEPHONE_NUMBER, "TEL;PREF:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_MOBILE_TELEPHONE_NUMBER, "TEL;CELL:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_RADIO_TELEPHONE_NUMBER, "TEL;X-EVOLUTION-RADIO:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_CAR_TELEPHONE_NUMBER, "TEL;CAR:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_OTHER_TELEPHONE_NUMBER, "TEL;VOICE:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_PAGER_TELEPHONE_NUMBER, "TEL;PAGER:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_TELEX_NUMBER, "TEL;X-EVOLUTION-TELEX:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_ISDN_NUMBER, "TEL;ISDN:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_HOME2_TELEPHONE_NUMBER, "TEL;HOME:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_TTYTDD_PHONE_NUMBER, "TEL;X-EVOLUTION-TTYTDD:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_HOME_TELEPHONE_NUMBER, "TEL;HOME;VOICE:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_ASSISTANT_TELEPHONE_NUMBER, "TEL;X-EVOLUTION-ASSISTANT:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_COMPANY_MAIN_PHONE_NUMBER, "TEL;WORK:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_BUSINESS_TELEPHONE_NUMBER, "TEL;WORK:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_BUSINESS2_TELEPHONE_NUMBER, "TEL;WORK;VOICE:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_PRIMARY_FAX_NUMBER, "TEL;PREF;FAX:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_BUSINESS_FAX_NUMBER, "TEL;WORK;FAX:%s\n"); + fprintProperty(tnef, fptr, PT_STRING8, PR_HOME_FAX_NUMBER, "TEL;HOME;FAX:%s\n"); + + + /* Email addresses */ + if ((vl=MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x8083))) == MAPI_UNDEFINED) { + vl=MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x8084)); + } + if (vl != MAPI_UNDEFINED) { + if (vl->size > 0) + fprintf(fptr, "EMAIL:%s\n", vl->data); + } + if ((vl=MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x8093))) == MAPI_UNDEFINED) { + vl=MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x8094)); + } + if (vl != MAPI_UNDEFINED) { + if (vl->size > 0) + fprintf(fptr, "EMAIL:%s\n", vl->data); + } + if ((vl=MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x80a3))) == MAPI_UNDEFINED) { + vl=MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x80a4)); + } + if (vl != MAPI_UNDEFINED) { + if (vl->size > 0) + fprintf(fptr, "EMAIL:%s\n", vl->data); + } + + fprintProperty(tnef, fptr, PT_STRING8, PR_BUSINESS_HOME_PAGE, "URL:%s\n"); + fprintUserProp(tnef, fptr, PT_STRING8, 0x80d8, "FBURL:%s\n"); + + + + /* Birthday */ + if ((vl=MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_SYSTIME, PR_BIRTHDAY))) != MAPI_UNDEFINED) { + fprintf(fptr, "BDAY:"); + MAPISysTimetoDTR(vl->data, &thedate); + fprintf(fptr, "%i-%02i-%02i\n", thedate.wYear, thedate.wMonth, thedate.wDay); + } + + /* Anniversary */ + if ((vl=MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_SYSTIME, PR_WEDDING_ANNIVERSARY))) != MAPI_UNDEFINED) { + fprintf(fptr, "X-EVOLUTION-ANNIVERSARY:"); + MAPISysTimetoDTR(vl->data, &thedate); + fprintf(fptr, "%i-%02i-%02i\n", thedate.wYear, thedate.wMonth, thedate.wDay); + } + fprintf(fptr, "END:VCARD\n"); + } +} + +unsigned char getRruleCount(unsigned char a, unsigned char b) { + return ((a << 8) | b); +} + +unsigned char getRruleMonthNum(unsigned char a, unsigned char b) { + switch (a) { + case 0x00: + switch (b) { + case 0x00: + /* Jan */ + return(1); + case 0xA3: + /* May */ + return(5); + case 0xAE: + /* Nov */ + return(11); + } + break; + case 0x60: + switch (b) { + case 0xAE: + /* Feb */ + return(2); + case 0x51: + /* Jun */ + return(6); + } + break; + case 0xE0: + switch (b) { + case 0x4B: + /* Mar */ + return(3); + case 0x56: + /* Sep */ + return(9); + } + break; + case 0x40: + switch (b) { + case 0xFA: + /* Apr */ + return(4); + } + break; + case 0x20: + if (b == 0xFA) { + /* Jul */ + return(7); + } + break; + case 0x80: + if (b == 0xA8) { + /* Aug */ + return(8); + } + break; + case 0xA0: + if (b == 0xFF) { + /* Oct */ + return(10); + } + break; + case 0xC0: + if (b == 0x56) { + return(12); + } + } + + /* Error */ + return(0); +} + +char * getRruleDayname(unsigned char a) { + static char daystring[25]; + + *daystring = 0; + + if (a & 0x01) { + strcat(daystring, "SU,"); + } + if (a & 0x02) { + strcat(daystring, "MO,"); + } + if (a & 0x04) { + strcat(daystring, "TU,"); + } + if (a & 0x08) { + strcat(daystring, "WE,"); + } + if (a & 0x10) { + strcat(daystring, "TH,"); + } + if (a & 0x20) { + strcat(daystring, "FR,"); + } + if (a & 0x40) { + strcat(daystring, "SA,"); + } + + if (strlen(daystring)) { + daystring[strlen(daystring) - 1] = 0; + } + + return(daystring); +} + +void printRrule(FILE *fptr, char *recur_data, int size, TNEFStruct TNEF) { + variableLength *filename; + + if (size < 0x1F) { + return; + } + + fprintf(fptr, "RRULE:FREQ="); + + if (recur_data[0x04] == 0x0A) { + fprintf(fptr, "DAILY"); + + if (recur_data[0x16] == 0x23 || recur_data[0x16] == 0x22 || + recur_data[0x16] == 0x21) { + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_I2, 0x0011))) != MAPI_UNDEFINED) { + fprintf(fptr, ";INTERVAL=%d", *(filename->data)); + } + if (recur_data[0x16] == 0x22 || recur_data[0x16] == 0x21) { + fprintf(fptr, ";COUNT=%d", + getRruleCount(recur_data[0x1B], recur_data[0x1A])); + } + } else if (recur_data[0x16] == 0x3E) { + fprintf(fptr, ";BYDAY=MO,TU,WE,TH,FR"); + if (recur_data[0x1A] == 0x22 || recur_data[0x1A] == 0x21) { + fprintf(fptr, ";COUNT=%d", + getRruleCount(recur_data[0x1F], recur_data[0x1E])); + } + } + } else if (recur_data[0x04] == 0x0B) { + fprintf(fptr, "WEEKLY;INTERVAL=%d;BYDAY=%s", + recur_data[0x0E], getRruleDayname(recur_data[0x16])); + if (recur_data[0x1A] == 0x22 || recur_data[0x1A] == 0x21) { + fprintf(fptr, ";COUNT=%d", + getRruleCount(recur_data[0x1F], recur_data[0x1E])); + } + } else if (recur_data[0x04] == 0x0C) { + fprintf(fptr, "MONTHLY"); + if (recur_data[0x06] == 0x02) { + fprintf(fptr, ";INTERVAL=%d;BYMONTHDAY=%d", recur_data[0x0E], + recur_data[0x16]); + if (recur_data[0x1A] == 0x22 || recur_data[0x1A] == 0x21) { + fprintf(fptr, ";COUNT=%d", getRruleCount(recur_data[0x1F], + recur_data[0x1E])); + } + } else if (recur_data[0x06] == 0x03) { + fprintf(fptr, ";BYDAY=%s;BYSETPOS=%d;INTERVAL=%d", + getRruleDayname(recur_data[0x16]), + recur_data[0x1A] == 0x05 ? -1 : recur_data[0x1A], + recur_data[0x0E]); + if (recur_data[0x1E] == 0x22 || recur_data[0x1E] == 0x21) { + fprintf(fptr, ";COUNT=%d", getRruleCount(recur_data[0x23], + recur_data[0x22])); + } + } + } else if (recur_data[0x04] == 0x0D) { + fprintf(fptr, "YEARLY;BYMONTH=%d", + getRruleMonthNum(recur_data[0x0A], recur_data[0x0B])); + if (recur_data[0x06] == 0x02) { + fprintf(fptr, ";BYMONTHDAY=%d", recur_data[0x16]); + } else if (recur_data[0x06] == 0x03) { + fprintf(fptr, ";BYDAY=%s;BYSETPOS=%d", + getRruleDayname(recur_data[0x16]), + recur_data[0x1A] == 0x05 ? -1 : recur_data[0x1A]); + } + if (recur_data[0x1E] == 0x22 || recur_data[0x1E] == 0x21) { + fprintf(fptr, ";COUNT=%d", getRruleCount(recur_data[0x23], + recur_data[0x22])); + } + } + fprintf(fptr, "\n"); +} + +void saveVCalendar(TNEFStruct TNEF) { + char ifilename[256]; + variableLength *filename; + char *charptr, *charptr2; + FILE *fptr; + int index; + DDWORD *ddword_ptr; + DDWORD ddword_val; + dtr thedate; + + if (filepath == NULL) { + sprintf(ifilename, "calendar.vcf"); + } else { + sprintf(ifilename, "%s/calendar.vcf", filepath); + } + printf("%s\n", ifilename); + + if ((fptr = fopen(ifilename, "wb"))==NULL) { + printf("Error writing file to disk!"); + } else { + fprintf(fptr, "BEGIN:VCALENDAR\n"); + if (TNEF.messageClass[0] != 0) { + charptr2=TNEF.messageClass; + charptr=charptr2; + while (*charptr != 0) { + if (*charptr == '.') { + charptr2 = charptr; + } + charptr++; + } + if (strcmp(charptr2, ".MtgCncl") == 0) { + fprintf(fptr, "METHOD:CANCEL\n"); + } else { + fprintf(fptr, "METHOD:REQUEST\n"); + } + } else { + fprintf(fptr, "METHOD:REQUEST\n"); + } + fprintf(fptr, "VERSION:2.0\n"); + fprintf(fptr, "BEGIN:VEVENT\n"); + + /* UID + After alot of comparisons, I'm reasonably sure this is totally + wrong. But it's not really necessary. */ + + /* I think it only exists to connect future modification entries to + this entry. so as long as it's incorrectly interpreted the same way + every time, it should be ok :) */ + filename = NULL; + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_BINARY, 0x3))) == MAPI_UNDEFINED) { + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_BINARY, 0x23))) == MAPI_UNDEFINED) { + filename = NULL; + } + } + if (filename!=NULL) { + fprintf(fptr, "UID:"); + for(index=0;index<filename->size;index++) { + fprintf(fptr,"%02X", (unsigned char)filename->data[index]); + } + fprintf(fptr,"\n"); + } + + /* Sequence */ + filename = NULL; + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_LONG, 0x8201))) != MAPI_UNDEFINED) { + ddword_ptr = (DDWORD*)filename->data; + fprintf(fptr, "SEQUENCE:%i\n", (int) *ddword_ptr); + } + if ((filename=MAPIFindProperty(&(TNEF.MapiProperties), + PROP_TAG(PT_BINARY, PR_SENDER_SEARCH_KEY))) + != MAPI_UNDEFINED) { + charptr = filename->data; + charptr2 = strstr(charptr, ":"); + if (charptr2 == NULL) + charptr2 = charptr; + else + charptr2++; + fprintf(fptr, "ORGANIZER;CN=\"%s\":MAILTO:%s\n", + charptr2, charptr2); + } + + /* Required Attendees */ + if ((filename = MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_STRING8, 0x823b))) != MAPI_UNDEFINED) { + /* We have a list of required participants, so + write them out. */ + if (filename->size > 1) { + charptr = filename->data-1; + charptr2=strstr(charptr+1, ";"); + while (charptr != NULL) { + charptr++; + charptr2 = strstr(charptr, ";"); + if (charptr2 != NULL) { + *charptr2 = 0; + } + while (*charptr == ' ') + charptr++; + fprintf(fptr, "ATTENDEE;PARTSTAT=NEEDS-ACTION;"); + fprintf(fptr, "ROLE=REQ-PARTICIPANT;RSVP=TRUE;"); + fprintf(fptr, "CN=\"%s\":MAILTO:%s\n", + charptr, charptr); + charptr = charptr2; + } + } + /* Optional attendees */ + if ((filename = MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_STRING8, 0x823c))) != MAPI_UNDEFINED) { + /* The list of optional participants */ + if (filename->size > 1) { + charptr = filename->data-1; + charptr2=strstr(charptr+1, ";"); + while (charptr != NULL) { + charptr++; + charptr2 = strstr(charptr, ";"); + if (charptr2 != NULL) { + *charptr2 = 0; + } + while (*charptr == ' ') + charptr++; + fprintf(fptr, "ATTENDEE;PARTSTAT=NEEDS-ACTION;"); + fprintf(fptr, "ROLE=OPT-PARTICIPANT;RSVP=TRUE;"); + fprintf(fptr, "CN=\"%s\":MAILTO:%s\n", + charptr, charptr); + charptr = charptr2; + } + } + } + } else if ((filename = MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_STRING8, 0x8238))) != MAPI_UNDEFINED) { + if (filename->size > 1) { + charptr = filename->data-1; + charptr2=strstr(charptr+1, ";"); + while (charptr != NULL) { + charptr++; + charptr2 = strstr(charptr, ";"); + if (charptr2 != NULL) { + *charptr2 = 0; + } + while (*charptr == ' ') + charptr++; + fprintf(fptr, "ATTENDEE;PARTSTAT=NEEDS-ACTION;"); + fprintf(fptr, "ROLE=REQ-PARTICIPANT;RSVP=TRUE;"); + fprintf(fptr, "CN=\"%s\":MAILTO:%s\n", + charptr, charptr); + charptr = charptr2; + } + } + + } + /* Summary */ + filename = NULL; + if((filename=MAPIFindProperty(&(TNEF.MapiProperties), + PROP_TAG(PT_STRING8, PR_CONVERSATION_TOPIC))) + != MAPI_UNDEFINED) { + fprintf(fptr, "SUMMARY:"); + cstylefprint(fptr, filename); + fprintf(fptr, "\n"); + } + + /* Description */ + if ((filename=MAPIFindProperty(&(TNEF.MapiProperties), + PROP_TAG(PT_BINARY, PR_RTF_COMPRESSED))) + != MAPI_UNDEFINED) { + variableLength buf; + if ((buf.data = DecompressRTF(filename, &(buf.size))) != NULL) { + fprintf(fptr, "DESCRIPTION:"); + printRtf(fptr, &buf); + free(buf.data); + } + + } + + /* Location */ + filename = NULL; + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_STRING8, 0x0002))) == MAPI_UNDEFINED) { + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_STRING8, 0x8208))) == MAPI_UNDEFINED) { + filename = NULL; + } + } + if (filename != NULL) { + fprintf(fptr,"LOCATION: %s\n", filename->data); + } + /* Date Start */ + filename = NULL; + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_SYSTIME, 0x820d))) == MAPI_UNDEFINED) { + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_SYSTIME, 0x8516))) == MAPI_UNDEFINED) { + filename=NULL; + } + } + if (filename != NULL) { + fprintf(fptr, "DTSTART:"); + MAPISysTimetoDTR(filename->data, &thedate); + fprintf(fptr,"%04i%02i%02iT%02i%02i%02iZ\n", + thedate.wYear, thedate.wMonth, thedate.wDay, + thedate.wHour, thedate.wMinute, thedate.wSecond); + } + /* Date End */ + filename = NULL; + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_SYSTIME, 0x820e))) == MAPI_UNDEFINED) { + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_SYSTIME, 0x8517))) == MAPI_UNDEFINED) { + filename=NULL; + } + } + if (filename != NULL) { + fprintf(fptr, "DTEND:"); + MAPISysTimetoDTR(filename->data, &thedate); + fprintf(fptr,"%04i%02i%02iT%02i%02i%02iZ\n", + thedate.wYear, thedate.wMonth, thedate.wDay, + thedate.wHour, thedate.wMinute, thedate.wSecond); + } + /* Date Stamp */ + filename = NULL; + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_SYSTIME, 0x8202))) != MAPI_UNDEFINED) { + fprintf(fptr, "CREATED:"); + MAPISysTimetoDTR(filename->data, &thedate); + fprintf(fptr,"%04i%02i%02iT%02i%02i%02iZ\n", + thedate.wYear, thedate.wMonth, thedate.wDay, + thedate.wHour, thedate.wMinute, thedate.wSecond); + } + /* Class */ + filename = NULL; + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_BOOLEAN, 0x8506))) != MAPI_UNDEFINED) { + ddword_ptr = (DDWORD*)filename->data; + ddword_val = SwapDDWord((BYTE*)ddword_ptr); + fprintf(fptr, "CLASS:" ); + if (*ddword_ptr == 1) { + fprintf(fptr,"PRIVATE\n"); + } else { + fprintf(fptr,"PUBLIC\n"); + } + } + /* Recurrence */ + filename = NULL; + if ((filename=MAPIFindUserProp(&(TNEF.MapiProperties), + PROP_TAG(PT_BINARY, 0x8216))) != MAPI_UNDEFINED) { + PrintRrule(fptr, filename->data, filename->size, TNEF); + } + + /* Wrap it up */ + fprintf(fptr, "END:VEVENT\n"); + fprintf(fptr, "END:VCALENDAR\n"); + fclose(fptr); + } +} + +void saveVTask(TNEFStruct tnef) { + variableLength *vl; + variableLength *filename; + int index,i; + char ifilename[256]; + char *charptr, *charptr2; + dtr thedate; + FILE *fptr; + DDWORD *ddword_ptr; + DDWORD ddword_val; + + vl = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_CONVERSATION_TOPIC)); + + if (vl == MAPI_UNDEFINED) { + return; + } + + index = strlen(vl->data); + while (vl->data[index] == ' ') + vl->data[index--] = 0; + + if (filepath == NULL) { + sprintf(ifilename, "%s.vcf", vl->data); + } else { + sprintf(ifilename, "%s/%s.vcf", filepath, vl->data); + } + for(i=0; i<strlen(ifilename); i++) + if (ifilename[i] == ' ') + ifilename[i] = '_'; + printf("%s\n", ifilename); + + if ((fptr = fopen(ifilename, "wb"))==NULL) { + printf("Error writing file to disk!"); + } else { + fprintf(fptr, "BEGIN:VCALENDAR\n"); + fprintf(fptr, "VERSION:2.0\n"); + fprintf(fptr, "METHOD:PUBLISH\n"); + filename = NULL; + + fprintf(fptr, "BEGIN:VTODO\n"); + if (tnef.messageID[0] != 0) { + fprintf(fptr,"UID:%s\n", tnef.messageID); + } + filename = MAPIFindUserProp(&(tnef.MapiProperties), \ + PROP_TAG(PT_STRING8, 0x8122)); + if (filename != MAPI_UNDEFINED) { + fprintf(fptr, "ORGANIZER:%s\n", filename->data); + } + + + if ((filename = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_DISPLAY_TO))) != MAPI_UNDEFINED) { + filename = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x811f)); + } + if ((filename != MAPI_UNDEFINED) && (filename->size > 1)) { + charptr = filename->data-1; + charptr2=strstr(charptr+1, ";"); + while (charptr != NULL) { + charptr++; + charptr2 = strstr(charptr, ";"); + if (charptr2 != NULL) { + *charptr2 = 0; + } + while (*charptr == ' ') + charptr++; + fprintf(fptr, "ATTENDEE;CN=%s;ROLE=REQ-PARTICIPANT:%s\n", charptr, charptr); + charptr = charptr2; + } + } + + if (tnef.subject.size > 0) { + fprintf(fptr,"SUMMARY:"); + cstylefprint(fptr,&(tnef.subject)); + fprintf(fptr,"\n"); + } + + if (tnef.body.size > 0) { + fprintf(fptr,"DESCRIPTION:"); + cstylefprint(fptr,&(tnef.body)); + fprintf(fptr,"\n"); + } + + filename = MAPIFindProperty(&(tnef.MapiProperties), \ + PROP_TAG(PT_SYSTIME, PR_CREATION_TIME)); + if (filename != MAPI_UNDEFINED) { + fprintf(fptr, "DTSTAMP:"); + MAPISysTimetoDTR(filename->data, &thedate); + fprintf(fptr,"%04i%02i%02iT%02i%02i%02iZ\n", + thedate.wYear, thedate.wMonth, thedate.wDay, + thedate.wHour, thedate.wMinute, thedate.wSecond); + } + + filename = MAPIFindUserProp(&(tnef.MapiProperties), \ + PROP_TAG(PT_SYSTIME, 0x8517)); + if (filename != MAPI_UNDEFINED) { + fprintf(fptr, "DUE:"); + MAPISysTimetoDTR(filename->data, &thedate); + fprintf(fptr,"%04i%02i%02iT%02i%02i%02iZ\n", + thedate.wYear, thedate.wMonth, thedate.wDay, + thedate.wHour, thedate.wMinute, thedate.wSecond); + } + filename = MAPIFindProperty(&(tnef.MapiProperties), \ + PROP_TAG(PT_SYSTIME, PR_LAST_MODIFICATION_TIME)); + if (filename != MAPI_UNDEFINED) { + fprintf(fptr, "LAST-MODIFIED:"); + MAPISysTimetoDTR(filename->data, &thedate); + fprintf(fptr,"%04i%02i%02iT%02i%02i%02iZ\n", + thedate.wYear, thedate.wMonth, thedate.wDay, + thedate.wHour, thedate.wMinute, thedate.wSecond); + } + /* Class */ + filename = MAPIFindUserProp(&(tnef.MapiProperties), \ + PROP_TAG(PT_BOOLEAN, 0x8506)); + if (filename != MAPI_UNDEFINED) { + ddword_ptr = (DDWORD*)filename->data; + ddword_val = SwapDDWord((BYTE*)ddword_ptr); + fprintf(fptr, "CLASS:" ); + if (*ddword_ptr == 1) { + fprintf(fptr,"PRIVATE\n"); + } else { + fprintf(fptr,"PUBLIC\n"); + } + } + fprintf(fptr, "END:VTODO\n"); + fprintf(fptr, "END:VCALENDAR\n"); + fclose(fptr); + } + +} + +void fprintProperty(TNEFStruct tnef, FILE *fptr, DWORD proptype, DWORD propid, char text[]) { + variableLength *vl; + if ((vl=MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(proptype, propid))) != MAPI_UNDEFINED) { + if (vl->size > 0) + if ((vl->size == 1) && (vl->data[0] == 0)) { + } else { + fprintf(fptr, text, vl->data); + } + } +} + +void fprintUserProp(TNEFStruct tnef, FILE *fptr, DWORD proptype, DWORD propid, char text[]) { + variableLength *vl; + if ((vl=MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(proptype, propid))) != MAPI_UNDEFINED) { + if (vl->size > 0) + if ((vl->size == 1) && (vl->data[0] == 0)) { + } else { + fprintf(fptr, text, vl->data); + } + } +} + +void quotedfprint(FILE *fptr, variableLength *vl) { + int index; + + for (index=0;index<vl->size-1; index++) { + if (vl->data[index] == '\n') { + fprintf(fptr, "=0A"); + } else if (vl->data[index] == '\r') { + } else { + fprintf(fptr, "%c", vl->data[index]); + } + } +} + +void cstylefprint(FILE *fptr, variableLength *vl) { + int index; + + for (index=0;index<vl->size-1; index++) { + if (vl->data[index] == '\n') { + fprintf(fptr, "\\n"); + } else if (vl->data[index] == '\r') { + /* Print nothing. */ + } else if (vl->data[index] == ';') { + fprintf(fptr, "\\;"); + } else if (vl->data[index] == ',') { + fprintf(fptr, "\\,"); + } else if (vl->data[index] == '\\') { + fprintf(fptr, "\\"); + } else { + fprintf(fptr, "%c", vl->data[index]); + } + } +} + +void printRtf(FILE *fptr, variableLength *vl) { + int index; + char *byte; + int brace_ct; + int key; + + key = 0; + brace_ct = 0; + + for(index = 0, byte=vl->data; index < vl->size; index++, byte++) { + if (*byte == '}') { + brace_ct--; + key = 0; + continue; + } + if (*byte == '{') { + brace_ct++; + continue; + } + if (*byte == '\\') { + key = 1; + } + if (isspace(*byte)) { + key = 0; + } + if ((brace_ct == 1) && (key == 0)) { + if (*byte == '\n') { + fprintf(fptr, "\\n"); + } else if (*byte == '\r') { + /* Print nothing. */ + } else if (*byte == ';') { + fprintf(fptr, "\\;"); + } else if (*byte == ',') { + fprintf(fptr, "\\,"); + } else if (*byte == '\\') { + fprintf(fptr, "\\"); + } else { + fprintf(fptr, "%c", *byte); + } + } + } + fprintf(fptr, "\n"); +} + |