2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Colin Leroy <colin@colino.net>
4 * and the Claws Mail Team
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "claws-features.h"
30 #include <glib/gi18n.h>
32 #include "common/claws.h"
33 #include "common/version.h"
39 #include <tnef-types.h>
44 #include "tnef_dump.h"
46 static MimeParser *tnef_parser = NULL;
48 static MimeInfo *tnef_broken_mimeinfo(const gchar *reason)
50 MimeInfo *sub_info = NULL;
51 gchar *tmpfilename = NULL;
52 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
59 sub_info = procmime_mimeinfo_new();
60 sub_info->content = MIMECONTENT_FILE;
61 sub_info->data.filename = tmpfilename;
62 sub_info->type = MIMETYPE_TEXT;
63 sub_info->subtype = g_strdup("plain");
66 "Claws Mail TNEF parser:\n\n"
67 "%s\n"), reason?reason:_("Unknown error"));
70 g_stat(tmpfilename, &statbuf);
72 sub_info->length = statbuf.st_size;
73 sub_info->encoding_type = ENC_BINARY;
79 static MimeInfo *tnef_dump_file(const gchar *filename, char *data, size_t size)
81 MimeInfo *sub_info = NULL;
82 gchar *tmpfilename = NULL;
83 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
85 gchar *content_type = NULL;
90 sub_info = procmime_mimeinfo_new();
91 sub_info->content = MIMECONTENT_FILE;
92 sub_info->data.filename = tmpfilename;
93 sub_info->type = MIMETYPE_APPLICATION;
94 sub_info->subtype = g_strdup("octet-stream");
97 g_hash_table_insert(sub_info->typeparameters,
101 content_type = procmime_get_mime_type(filename);
102 if (content_type && strchr(content_type, '/')) {
103 g_free(sub_info->subtype);
104 sub_info->subtype = g_strdup(strchr(content_type, '/')+1);
105 *(strchr(content_type, '/')) = '\0';
106 sub_info->type = procmime_get_media_type(content_type);
107 g_free(content_type);
111 if (fwrite(data, 1, size, fp) < size) {
112 FILE_OP_ERROR(tmpfilename, "fwrite");
114 claws_unlink(tmpfilename);
115 procmime_mimeinfo_free_all(sub_info);
116 return tnef_broken_mimeinfo(_("Failed to write the part data."));
119 g_stat(tmpfilename, &statbuf);
120 sub_info->tmp = TRUE;
121 sub_info->length = statbuf.st_size;
122 sub_info->encoding_type = ENC_BINARY;
127 MimeInfo *tnef_parse_vcal(TNEFStruct tnef)
129 MimeInfo *sub_info = NULL;
130 gchar *tmpfilename = NULL;
131 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
133 gboolean result = FALSE;
138 sub_info = procmime_mimeinfo_new();
139 sub_info->content = MIMECONTENT_FILE;
140 sub_info->data.filename = tmpfilename;
141 sub_info->type = MIMETYPE_TEXT;
142 sub_info->subtype = g_strdup("calendar");
143 g_hash_table_insert(sub_info->typeparameters,
144 g_strdup("filename"),
145 g_strdup("calendar.ics"));
147 result = SaveVCalendar(fp, tnef);
150 g_stat(tmpfilename, &statbuf);
151 sub_info->tmp = TRUE;
152 sub_info->length = statbuf.st_size;
153 sub_info->encoding_type = ENC_BINARY;
156 claws_unlink(tmpfilename);
157 procmime_mimeinfo_free_all(sub_info);
158 return tnef_broken_mimeinfo(_("Failed to parse VCalendar data."));
163 MimeInfo *tnef_parse_vtask(TNEFStruct tnef)
165 MimeInfo *sub_info = NULL;
166 gchar *tmpfilename = NULL;
167 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
169 gboolean result = FALSE;
174 sub_info = procmime_mimeinfo_new();
175 sub_info->content = MIMECONTENT_FILE;
176 sub_info->data.filename = tmpfilename;
177 sub_info->type = MIMETYPE_TEXT;
178 sub_info->subtype = g_strdup("calendar");
179 g_hash_table_insert(sub_info->typeparameters,
180 g_strdup("filename"),
181 g_strdup("task.ics"));
183 result = SaveVTask(fp, tnef);
186 g_stat(tmpfilename, &statbuf);
187 sub_info->tmp = TRUE;
188 sub_info->length = statbuf.st_size;
189 sub_info->encoding_type = ENC_BINARY;
192 claws_unlink(tmpfilename);
193 procmime_mimeinfo_free_all(sub_info);
194 return tnef_broken_mimeinfo(_("Failed to parse VTask data."));
199 MimeInfo *tnef_parse_rtf(TNEFStruct tnef, variableLength *tmp_var)
202 MimeInfo *info = NULL;
203 buf.data = DecompressRTF(tmp_var, &(buf.size));
205 info = tnef_dump_file("message.rtf", buf.data, buf.size);
213 MimeInfo *tnef_parse_vcard(TNEFStruct tnef)
215 MimeInfo *sub_info = NULL;
216 gchar *tmpfilename = NULL;
217 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
219 gboolean result = FALSE;
224 sub_info = procmime_mimeinfo_new();
225 sub_info->content = MIMECONTENT_FILE;
226 sub_info->data.filename = tmpfilename;
227 sub_info->type = MIMETYPE_TEXT;
228 sub_info->subtype = g_strdup("x-vcard");
229 g_hash_table_insert(sub_info->typeparameters,
230 g_strdup("filename"),
231 g_strdup("contact.vcf"));
233 result = SaveVCard(fp, tnef);
236 g_stat(tmpfilename, &statbuf);
237 sub_info->tmp = TRUE;
238 sub_info->length = statbuf.st_size;
239 sub_info->encoding_type = ENC_BINARY;
242 claws_unlink(tmpfilename);
243 procmime_mimeinfo_free_all(sub_info);
244 return tnef_broken_mimeinfo(_("Failed to parse VCard data."));
249 static gboolean tnef_parse (MimeParser *parser, MimeInfo *mimeinfo)
252 MimeInfo *sub_info = NULL;
253 variableLength *tmp_var;
255 int parse_result = 0;
256 gboolean cal_done = FALSE;
258 if (!procmime_decode_content(mimeinfo)) {
259 debug_print("error decoding\n");
262 debug_print("Tnef parser parsing part (%d).\n", mimeinfo->length);
263 if (mimeinfo->content == MIMECONTENT_FILE)
264 debug_print("content: %s\n", mimeinfo->data.filename);
266 debug_print("contents in memory (len %zd)\n",
267 strlen(mimeinfo->data.mem));
269 TNEFInitialize(&tnef);
271 if (!debug_get_mode())
274 if (mimeinfo->content == MIMECONTENT_MEM)
275 parse_result = TNEFParseMemory(mimeinfo->data.mem, mimeinfo->length, &tnef);
277 parse_result = TNEFParseFile(mimeinfo->data.filename, &tnef);
279 mimeinfo->type = MIMETYPE_MULTIPART;
280 mimeinfo->subtype = g_strdup("mixed");
281 g_hash_table_insert(mimeinfo->typeparameters,
282 g_strdup("description"),
283 g_strdup("Parsed from MS-TNEF"));
285 if (parse_result != 0) {
286 g_warning("Failed to parse TNEF data.");
292 if (tnef.messageClass != NULL && tnef.messageClass[0] != '\0') {
293 if (strcmp(tnef.messageClass, "IPM.Contact") == 0)
294 sub_info = tnef_parse_vcard(tnef);
295 else if (strcmp(tnef.messageClass, "IPM.Task") == 0)
296 sub_info = tnef_parse_vtask(tnef);
297 else if (strcmp(tnef.messageClass, "IPM.Appointment") == 0) {
298 sub_info = tnef_parse_vcal(tnef);
304 g_node_append(mimeinfo->node, sub_info->node);
307 if (tnef.MapiProperties.count > 0) {
308 tmp_var = MAPIFindProperty (&(tnef.MapiProperties), PROP_TAG(PT_BINARY,PR_RTF_COMPRESSED));
309 if (tmp_var != MAPI_UNDEFINED) {
310 sub_info = tnef_parse_rtf(tnef, tmp_var);
315 g_node_append(mimeinfo->node, sub_info->node);
318 tmp_var = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8,0x24));
319 if (tmp_var != MAPI_UNDEFINED) {
320 if (!cal_done && strcmp(tmp_var->data, "IPM.Appointment") == 0) {
321 sub_info = tnef_parse_vcal(tnef);
326 g_node_append(mimeinfo->node, sub_info->node);
329 att = tnef.starting_attach.next;
331 gchar *filename = NULL;
332 gboolean is_object = TRUE;
335 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(30,0x3707));
336 if (tmp_var == MAPI_UNDEFINED)
337 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(30,0x3001));
338 if (tmp_var == MAPI_UNDEFINED)
339 tmp_var = &(att->Title);
342 filename = g_strdup(tmp_var->data);
344 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(PT_OBJECT, PR_ATTACH_DATA_OBJ));
345 if (tmp_var == MAPI_UNDEFINED)
346 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(PT_BINARY, PR_ATTACH_DATA_OBJ));
347 if (tmp_var == MAPI_UNDEFINED) {
348 tmp_var = &(att->FileData);
352 sub_info = tnef_dump_file(filename,
353 tmp_var->data + (is_object ? 16:0),
354 tmp_var->size - (is_object ? 16:0));
357 g_node_append(mimeinfo->node, sub_info->node);
359 memcpy(&signature, tmp_var->data+(is_object ? 16:0), sizeof(DWORD));
361 if (TNEFCheckForSignature(signature) == 0) {
362 debug_print("that's TNEF stuff, process it\n");
363 tnef_parse(parser, sub_info);
377 gint plugin_init(gchar **error)
379 if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
380 VERSION_NUMERIC, _("TNEF Parser"), error))
383 tnef_parser = g_new0(MimeParser, 1);
384 tnef_parser->type = MIMETYPE_APPLICATION;
385 tnef_parser->sub_type = "ms-tnef";
386 tnef_parser->parse = tnef_parse;
388 procmime_mimeparser_register(tnef_parser);
393 gboolean plugin_done(void)
395 procmime_mimeparser_unregister(tnef_parser);
402 const gchar *plugin_name(void)
404 return _("TNEF Parser");
407 const gchar *plugin_desc(void)
409 return _("This Claws Mail plugin allows you to read application/ms-tnef attachments.\n\n"
410 "The plugin uses the Ytnef library, which is copyright 2002-2007 by "
411 "Randall Hand <yerase@yerot.com>");
414 const gchar *plugin_type(void)
419 const gchar *plugin_licence(void)
424 const gchar *plugin_version(void)
429 struct PluginFeature *plugin_provides(void)
431 static struct PluginFeature features[] =
432 { {PLUGIN_MIMEPARSER, "application/ms-tnef"},
433 {PLUGIN_NOTHING, NULL}};