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>
34 #include <libytnef/tnef-types.h>
35 #include <libytnef/ytnef.h>
36 #include <libytnef/mapi.h>
37 #include <libytnef/mapidefs.h>
39 #include <tnef-types.h>
45 #include "common/claws.h"
46 #include "common/version.h"
51 #include "file-utils.h"
53 #include "tnef_dump.h"
55 static MimeParser *tnef_parser = NULL;
57 static MimeInfo *tnef_broken_mimeinfo(const gchar *reason)
59 MimeInfo *sub_info = NULL;
60 gchar *tmpfilename = NULL;
61 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
68 sub_info = procmime_mimeinfo_new();
69 sub_info->content = MIMECONTENT_FILE;
70 sub_info->data.filename = tmpfilename;
71 sub_info->type = MIMETYPE_TEXT;
72 sub_info->subtype = g_strdup("plain");
75 "Claws Mail TNEF parser:\n\n"
76 "%s\n"), reason?reason:_("Unknown error"));
79 if (g_stat(tmpfilename, &statbuf) < 0) {
80 claws_unlink(tmpfilename);
81 procmime_mimeinfo_free_all(&sub_info);
87 sub_info->length = statbuf.st_size;
88 sub_info->encoding_type = ENC_BINARY;
94 static MimeInfo *tnef_dump_file(const gchar *filename, char *data, size_t size)
96 MimeInfo *sub_info = NULL;
97 gchar *tmpfilename = NULL;
98 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
100 gchar *content_type = NULL;
105 sub_info = procmime_mimeinfo_new();
106 sub_info->content = MIMECONTENT_FILE;
107 sub_info->data.filename = tmpfilename;
108 sub_info->type = MIMETYPE_APPLICATION;
109 sub_info->subtype = g_strdup("octet-stream");
112 g_hash_table_insert(sub_info->typeparameters,
113 g_strdup("filename"),
116 content_type = procmime_get_mime_type(filename);
117 if (content_type && strchr(content_type, '/')) {
118 g_free(sub_info->subtype);
119 sub_info->subtype = g_strdup(strchr(content_type, '/')+1);
120 *(strchr(content_type, '/')) = '\0';
121 sub_info->type = procmime_get_media_type(content_type);
122 g_free(content_type);
126 if (claws_fwrite(data, 1, size, fp) < size) {
127 FILE_OP_ERROR(tmpfilename, "claws_fwrite");
129 claws_unlink(tmpfilename);
130 procmime_mimeinfo_free_all(&sub_info);
131 return tnef_broken_mimeinfo(_("Failed to write the part data."));
135 if (g_stat(tmpfilename, &statbuf) < 0) {
136 claws_unlink(tmpfilename);
137 procmime_mimeinfo_free_all(&sub_info);
138 return tnef_broken_mimeinfo(_("Failed to write the part data."));
140 sub_info->tmp = TRUE;
141 sub_info->length = statbuf.st_size;
142 sub_info->encoding_type = ENC_BINARY;
148 MimeInfo *tnef_parse_vcal(TNEFStruct *tnef)
150 MimeInfo *sub_info = NULL;
151 gchar *tmpfilename = NULL;
152 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
154 gboolean result = FALSE;
159 sub_info = procmime_mimeinfo_new();
160 sub_info->content = MIMECONTENT_FILE;
161 sub_info->data.filename = tmpfilename;
162 sub_info->type = MIMETYPE_TEXT;
163 sub_info->subtype = g_strdup("calendar");
164 g_hash_table_insert(sub_info->typeparameters,
165 g_strdup("filename"),
166 g_strdup("calendar.ics"));
168 result = SaveVCalendar(fp, tnef);
172 if (g_stat(tmpfilename, &statbuf) < 0) {
175 sub_info->tmp = TRUE;
176 sub_info->length = statbuf.st_size;
177 sub_info->encoding_type = ENC_BINARY;
181 claws_unlink(tmpfilename);
182 procmime_mimeinfo_free_all(&sub_info);
183 return tnef_broken_mimeinfo(_("Failed to parse VCalendar data."));
188 MimeInfo *tnef_parse_vtask(TNEFStruct *tnef)
190 MimeInfo *sub_info = NULL;
191 gchar *tmpfilename = NULL;
192 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
194 gboolean result = FALSE;
199 sub_info = procmime_mimeinfo_new();
200 sub_info->content = MIMECONTENT_FILE;
201 sub_info->data.filename = tmpfilename;
202 sub_info->type = MIMETYPE_TEXT;
203 sub_info->subtype = g_strdup("calendar");
204 g_hash_table_insert(sub_info->typeparameters,
205 g_strdup("filename"),
206 g_strdup("task.ics"));
208 result = SaveVTask(fp, tnef);
212 if (g_stat(tmpfilename, &statbuf) < 0) {
215 sub_info->tmp = TRUE;
216 sub_info->length = statbuf.st_size;
217 sub_info->encoding_type = ENC_BINARY;
220 claws_unlink(tmpfilename);
221 procmime_mimeinfo_free_all(&sub_info);
222 return tnef_broken_mimeinfo(_("Failed to parse VTask data."));
227 MimeInfo *tnef_parse_rtf(TNEFStruct *tnef, variableLength *tmp_var)
230 MimeInfo *info = NULL;
231 buf.data = DecompressRTF(tmp_var, &(buf.size));
233 info = tnef_dump_file("message.rtf", buf.data, buf.size);
241 MimeInfo *tnef_parse_vcard(TNEFStruct *tnef)
243 MimeInfo *sub_info = NULL;
244 gchar *tmpfilename = NULL;
245 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
247 gboolean result = FALSE;
253 sub_info = procmime_mimeinfo_new();
254 sub_info->content = MIMECONTENT_FILE;
255 sub_info->data.filename = tmpfilename;
256 sub_info->type = MIMETYPE_TEXT;
257 sub_info->subtype = g_strdup("x-vcard");
258 g_hash_table_insert(sub_info->typeparameters,
259 g_strdup("filename"),
260 g_strdup("contact.vcf"));
262 result = SaveVCard(fp, tnef);
266 ret = g_stat(tmpfilename, &statbuf);
268 debug_print("couldn't stat tmpfilename '%s'\n", tmpfilename);
271 if ((ret == -1) || !result) {
272 claws_unlink(tmpfilename);
273 procmime_mimeinfo_free_all(&sub_info);
274 return tnef_broken_mimeinfo(_("Failed to parse VCard data."));
277 sub_info->tmp = TRUE;
278 sub_info->length = statbuf.st_size;
279 sub_info->encoding_type = ENC_BINARY;
283 static gboolean tnef_parse (MimeParser *parser, MimeInfo *mimeinfo)
286 MimeInfo *sub_info = NULL;
287 variableLength *tmp_var;
289 int parse_result = 0;
290 gboolean cal_done = FALSE;
292 if (!procmime_decode_content(mimeinfo)) {
293 debug_print("error decoding\n");
296 debug_print("Tnef parser parsing part (%d).\n", mimeinfo->length);
297 if (mimeinfo->content == MIMECONTENT_FILE)
298 debug_print("content: %s\n", mimeinfo->data.filename);
300 debug_print("contents in memory (len %zd)\n",
301 strlen(mimeinfo->data.mem));
303 tnef = g_new0(TNEFStruct, 1);
304 TNEFInitialize(tnef);
306 tnef->Debug = debug_get_mode();
308 if (mimeinfo->content == MIMECONTENT_MEM)
309 parse_result = TNEFParseMemory(mimeinfo->data.mem, mimeinfo->length, tnef);
311 parse_result = TNEFParseFile(mimeinfo->data.filename, tnef);
313 mimeinfo->type = MIMETYPE_MULTIPART;
314 mimeinfo->subtype = g_strdup("mixed");
315 g_hash_table_insert(mimeinfo->typeparameters,
316 g_strdup("description"),
317 g_strdup("Parsed from MS-TNEF"));
319 if (parse_result != 0) {
320 g_warning("Failed to parse TNEF data.");
326 if (tnef->messageClass[0] != '\0') {
327 if (strcmp(tnef->messageClass, "IPM.Contact") == 0)
328 sub_info = tnef_parse_vcard(tnef);
329 else if (strcmp(tnef->messageClass, "IPM.Task") == 0)
330 sub_info = tnef_parse_vtask(tnef);
331 else if (strcmp(tnef->messageClass, "IPM.Appointment") == 0) {
332 sub_info = tnef_parse_vcal(tnef);
338 g_node_append(mimeinfo->node, sub_info->node);
341 if (tnef->MapiProperties.count > 0) {
342 tmp_var = MAPIFindProperty (&(tnef->MapiProperties), PROP_TAG(PT_BINARY,PR_RTF_COMPRESSED));
343 if (tmp_var != MAPI_UNDEFINED) {
344 sub_info = tnef_parse_rtf(tnef, tmp_var);
349 g_node_append(mimeinfo->node, sub_info->node);
352 tmp_var = MAPIFindUserProp(&(tnef->MapiProperties), PROP_TAG(PT_STRING8,0x24));
353 if (tmp_var != MAPI_UNDEFINED) {
354 if (!cal_done && strcmp(tmp_var->data, "IPM.Appointment") == 0) {
355 sub_info = tnef_parse_vcal(tnef);
360 g_node_append(mimeinfo->node, sub_info->node);
363 att = tnef->starting_attach.next;
365 gchar *filename = NULL;
366 gboolean is_object = TRUE;
369 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(30,0x3707));
370 if (tmp_var == MAPI_UNDEFINED)
371 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(30,0x3001));
372 if (tmp_var == MAPI_UNDEFINED)
373 tmp_var = &(att->Title);
376 filename = g_strdup(tmp_var->data);
378 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(PT_OBJECT, PR_ATTACH_DATA_OBJ));
379 if (tmp_var == MAPI_UNDEFINED)
380 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(PT_BINARY, PR_ATTACH_DATA_OBJ));
381 if (tmp_var == MAPI_UNDEFINED) {
382 tmp_var = &(att->FileData);
386 sub_info = tnef_dump_file(filename,
387 tmp_var->data + (is_object ? 16:0),
388 tmp_var->size - (is_object ? 16:0));
391 g_node_append(mimeinfo->node, sub_info->node);
393 memcpy(&signature, tmp_var->data+(is_object ? 16:0), sizeof(DWORD));
395 if (TNEFCheckForSignature(signature) == 0) {
396 debug_print("that's TNEF stuff, process it\n");
397 tnef_parse(parser, sub_info);
411 gint plugin_init(gchar **error)
413 if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
414 VERSION_NUMERIC, _("TNEF Parser"), error))
417 tnef_parser = g_new0(MimeParser, 1);
418 tnef_parser->type = MIMETYPE_APPLICATION;
419 tnef_parser->sub_type = "ms-tnef";
420 tnef_parser->parse = tnef_parse;
422 procmime_mimeparser_register(tnef_parser);
427 gboolean plugin_done(void)
429 procmime_mimeparser_unregister(tnef_parser);
436 const gchar *plugin_name(void)
438 return _("TNEF Parser");
441 const gchar *plugin_desc(void)
443 return _("This Claws Mail plugin allows you to read application/ms-tnef attachments.\n\n"
444 "The plugin uses the Ytnef library, which is copyright 2002-2007 by "
445 "Randall Hand <yerase@yerot.com>");
448 const gchar *plugin_type(void)
453 const gchar *plugin_licence(void)
458 const gchar *plugin_version(void)
463 struct PluginFeature *plugin_provides(void)
465 static struct PluginFeature features[] =
466 { {PLUGIN_MIMEPARSER, "application/ms-tnef"},
467 {PLUGIN_NOTHING, NULL}};