aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSrinivasa Ragavan <sragavan@src.gnome.org>2007-07-09 04:06:54 +0800
committerSrinivasa Ragavan <sragavan@src.gnome.org>2007-07-09 04:06:54 +0800
commit301528d84bf582d956c0e9b768e4f60259f0cf88 (patch)
tree805da1fd93810067b918d8467f4b753cf9c68180
parent3615c9dbb4749849de6ea7d913b5c9bc73ba70ce (diff)
downloadgsoc2013-evolution-301528d84bf582d956c0e9b768e4f60259f0cf88.tar
gsoc2013-evolution-301528d84bf582d956c0e9b768e4f60259f0cf88.tar.gz
gsoc2013-evolution-301528d84bf582d956c0e9b768e4f60259f0cf88.tar.bz2
gsoc2013-evolution-301528d84bf582d956c0e9b768e4f60259f0cf88.tar.lz
gsoc2013-evolution-301528d84bf582d956c0e9b768e4f60259f0cf88.tar.xz
gsoc2013-evolution-301528d84bf582d956c0e9b768e4f60259f0cf88.tar.zst
gsoc2013-evolution-301528d84bf582d956c0e9b768e4f60259f0cf88.zip
Commit for tnef attachment and attachment reminder
svn path=/trunk/; revision=33769
-rw-r--r--ChangeLog7
-rw-r--r--composer/ChangeLog6
-rw-r--r--composer/e-msg-composer.c27
-rw-r--r--configure.in22
-rw-r--r--mail/ChangeLog5
-rw-r--r--mail/em-composer-utils.c21
-rw-r--r--plugins/attachment-reminder/ChangeLog9
-rw-r--r--plugins/attachment-reminder/Makefile.am46
-rw-r--r--plugins/attachment-reminder/apps-evolution-attachment-reminder.schemas.in.in41
-rw-r--r--plugins/attachment-reminder/attachment-reminder.c418
-rw-r--r--plugins/attachment-reminder/attachment-reminder.glade180
-rw-r--r--plugins/attachment-reminder/org-gnome-attachment-reminder.errors.xml10
-rw-r--r--plugins/attachment-reminder/org-gnome-evolution-attachment-reminder.eplug.xml28
-rw-r--r--plugins/tnef-attachments/ChangeLog4
-rw-r--r--plugins/tnef-attachments/Makefile.am18
-rw-r--r--plugins/tnef-attachments/org-gnome-tnef-attachments.eplug.xml25
-rw-r--r--plugins/tnef-attachments/tnef-plugin.c1320
17 files changed, 2183 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 409fb21eb4..558c19ac51 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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");
+}
+