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 if (g_stat(tmpfilename, &statbuf) < 0) {
71 claws_unlink(tmpfilename);
72 procmime_mimeinfo_free_all(sub_info);
78 sub_info->length = statbuf.st_size;
79 sub_info->encoding_type = ENC_BINARY;
85 static MimeInfo *tnef_dump_file(const gchar *filename, char *data, size_t size)
87 MimeInfo *sub_info = NULL;
88 gchar *tmpfilename = NULL;
89 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
91 gchar *content_type = NULL;
96 sub_info = procmime_mimeinfo_new();
97 sub_info->content = MIMECONTENT_FILE;
98 sub_info->data.filename = tmpfilename;
99 sub_info->type = MIMETYPE_APPLICATION;
100 sub_info->subtype = g_strdup("octet-stream");
103 g_hash_table_insert(sub_info->typeparameters,
104 g_strdup("filename"),
107 content_type = procmime_get_mime_type(filename);
108 if (content_type && strchr(content_type, '/')) {
109 g_free(sub_info->subtype);
110 sub_info->subtype = g_strdup(strchr(content_type, '/')+1);
111 *(strchr(content_type, '/')) = '\0';
112 sub_info->type = procmime_get_media_type(content_type);
113 g_free(content_type);
117 if (fwrite(data, 1, size, fp) < size) {
118 FILE_OP_ERROR(tmpfilename, "fwrite");
120 claws_unlink(tmpfilename);
121 procmime_mimeinfo_free_all(sub_info);
122 return tnef_broken_mimeinfo(_("Failed to write the part data."));
126 if (g_stat(tmpfilename, &statbuf) < 0) {
127 claws_unlink(tmpfilename);
128 procmime_mimeinfo_free_all(sub_info);
129 return tnef_broken_mimeinfo(_("Failed to write the part data."));
131 sub_info->tmp = TRUE;
132 sub_info->length = statbuf.st_size;
133 sub_info->encoding_type = ENC_BINARY;
139 MimeInfo *tnef_parse_vcal(TNEFStruct *tnef)
141 MimeInfo *sub_info = NULL;
142 gchar *tmpfilename = NULL;
143 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
145 gboolean result = FALSE;
150 sub_info = procmime_mimeinfo_new();
151 sub_info->content = MIMECONTENT_FILE;
152 sub_info->data.filename = tmpfilename;
153 sub_info->type = MIMETYPE_TEXT;
154 sub_info->subtype = g_strdup("calendar");
155 g_hash_table_insert(sub_info->typeparameters,
156 g_strdup("filename"),
157 g_strdup("calendar.ics"));
159 result = SaveVCalendar(fp, tnef);
163 if (g_stat(tmpfilename, &statbuf) < 0) {
166 sub_info->tmp = TRUE;
167 sub_info->length = statbuf.st_size;
168 sub_info->encoding_type = ENC_BINARY;
172 claws_unlink(tmpfilename);
173 procmime_mimeinfo_free_all(sub_info);
174 return tnef_broken_mimeinfo(_("Failed to parse VCalendar data."));
179 MimeInfo *tnef_parse_vtask(TNEFStruct *tnef)
181 MimeInfo *sub_info = NULL;
182 gchar *tmpfilename = NULL;
183 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
185 gboolean result = FALSE;
190 sub_info = procmime_mimeinfo_new();
191 sub_info->content = MIMECONTENT_FILE;
192 sub_info->data.filename = tmpfilename;
193 sub_info->type = MIMETYPE_TEXT;
194 sub_info->subtype = g_strdup("calendar");
195 g_hash_table_insert(sub_info->typeparameters,
196 g_strdup("filename"),
197 g_strdup("task.ics"));
199 result = SaveVTask(fp, tnef);
203 if (g_stat(tmpfilename, &statbuf) < 0) {
206 sub_info->tmp = TRUE;
207 sub_info->length = statbuf.st_size;
208 sub_info->encoding_type = ENC_BINARY;
211 claws_unlink(tmpfilename);
212 procmime_mimeinfo_free_all(sub_info);
213 return tnef_broken_mimeinfo(_("Failed to parse VTask data."));
218 MimeInfo *tnef_parse_rtf(TNEFStruct *tnef, variableLength *tmp_var)
221 MimeInfo *info = NULL;
222 buf.data = DecompressRTF(tmp_var, &(buf.size));
224 info = tnef_dump_file("message.rtf", buf.data, buf.size);
232 MimeInfo *tnef_parse_vcard(TNEFStruct *tnef)
234 MimeInfo *sub_info = NULL;
235 gchar *tmpfilename = NULL;
236 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
238 gboolean result = FALSE;
243 sub_info = procmime_mimeinfo_new();
244 sub_info->content = MIMECONTENT_FILE;
245 sub_info->data.filename = tmpfilename;
246 sub_info->type = MIMETYPE_TEXT;
247 sub_info->subtype = g_strdup("x-vcard");
248 g_hash_table_insert(sub_info->typeparameters,
249 g_strdup("filename"),
250 g_strdup("contact.vcf"));
252 result = SaveVCard(fp, tnef);
255 g_stat(tmpfilename, &statbuf);
256 sub_info->tmp = TRUE;
257 sub_info->length = statbuf.st_size;
258 sub_info->encoding_type = ENC_BINARY;
261 claws_unlink(tmpfilename);
262 procmime_mimeinfo_free_all(sub_info);
263 return tnef_broken_mimeinfo(_("Failed to parse VCard data."));
268 static gboolean tnef_parse (MimeParser *parser, MimeInfo *mimeinfo)
271 MimeInfo *sub_info = NULL;
272 variableLength *tmp_var;
274 int parse_result = 0;
275 gboolean cal_done = FALSE;
277 if (!procmime_decode_content(mimeinfo)) {
278 debug_print("error decoding\n");
281 debug_print("Tnef parser parsing part (%d).\n", mimeinfo->length);
282 if (mimeinfo->content == MIMECONTENT_FILE)
283 debug_print("content: %s\n", mimeinfo->data.filename);
285 debug_print("contents in memory (len %zd)\n",
286 strlen(mimeinfo->data.mem));
288 tnef = g_new0(TNEFStruct, 1);
289 TNEFInitialize(tnef);
291 tnef->Debug = debug_get_mode();
293 if (mimeinfo->content == MIMECONTENT_MEM)
294 parse_result = TNEFParseMemory(mimeinfo->data.mem, mimeinfo->length, tnef);
296 parse_result = TNEFParseFile(mimeinfo->data.filename, tnef);
298 mimeinfo->type = MIMETYPE_MULTIPART;
299 mimeinfo->subtype = g_strdup("mixed");
300 g_hash_table_insert(mimeinfo->typeparameters,
301 g_strdup("description"),
302 g_strdup("Parsed from MS-TNEF"));
304 if (parse_result != 0) {
305 g_warning("Failed to parse TNEF data.");
311 if (tnef->messageClass[0] != '\0') {
312 if (strcmp(tnef->messageClass, "IPM.Contact") == 0)
313 sub_info = tnef_parse_vcard(tnef);
314 else if (strcmp(tnef->messageClass, "IPM.Task") == 0)
315 sub_info = tnef_parse_vtask(tnef);
316 else if (strcmp(tnef->messageClass, "IPM.Appointment") == 0) {
317 sub_info = tnef_parse_vcal(tnef);
323 g_node_append(mimeinfo->node, sub_info->node);
326 if (tnef->MapiProperties.count > 0) {
327 tmp_var = MAPIFindProperty (&(tnef->MapiProperties), PROP_TAG(PT_BINARY,PR_RTF_COMPRESSED));
328 if (tmp_var != MAPI_UNDEFINED) {
329 sub_info = tnef_parse_rtf(tnef, tmp_var);
334 g_node_append(mimeinfo->node, sub_info->node);
337 tmp_var = MAPIFindUserProp(&(tnef->MapiProperties), PROP_TAG(PT_STRING8,0x24));
338 if (tmp_var != MAPI_UNDEFINED) {
339 if (!cal_done && strcmp(tmp_var->data, "IPM.Appointment") == 0) {
340 sub_info = tnef_parse_vcal(tnef);
345 g_node_append(mimeinfo->node, sub_info->node);
348 att = tnef->starting_attach.next;
350 gchar *filename = NULL;
351 gboolean is_object = TRUE;
354 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(30,0x3707));
355 if (tmp_var == MAPI_UNDEFINED)
356 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(30,0x3001));
357 if (tmp_var == MAPI_UNDEFINED)
358 tmp_var = &(att->Title);
361 filename = g_strdup(tmp_var->data);
363 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(PT_OBJECT, PR_ATTACH_DATA_OBJ));
364 if (tmp_var == MAPI_UNDEFINED)
365 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(PT_BINARY, PR_ATTACH_DATA_OBJ));
366 if (tmp_var == MAPI_UNDEFINED) {
367 tmp_var = &(att->FileData);
371 sub_info = tnef_dump_file(filename,
372 tmp_var->data + (is_object ? 16:0),
373 tmp_var->size - (is_object ? 16:0));
376 g_node_append(mimeinfo->node, sub_info->node);
378 memcpy(&signature, tmp_var->data+(is_object ? 16:0), sizeof(DWORD));
380 if (TNEFCheckForSignature(signature) == 0) {
381 debug_print("that's TNEF stuff, process it\n");
382 tnef_parse(parser, sub_info);
396 gint plugin_init(gchar **error)
398 if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
399 VERSION_NUMERIC, _("TNEF Parser"), error))
402 tnef_parser = g_new0(MimeParser, 1);
403 tnef_parser->type = MIMETYPE_APPLICATION;
404 tnef_parser->sub_type = "ms-tnef";
405 tnef_parser->parse = tnef_parse;
407 procmime_mimeparser_register(tnef_parser);
412 gboolean plugin_done(void)
414 procmime_mimeparser_unregister(tnef_parser);
421 const gchar *plugin_name(void)
423 return _("TNEF Parser");
426 const gchar *plugin_desc(void)
428 return _("This Claws Mail plugin allows you to read application/ms-tnef attachments.\n\n"
429 "The plugin uses the Ytnef library, which is copyright 2002-2007 by "
430 "Randall Hand <yerase@yerot.com>");
433 const gchar *plugin_type(void)
438 const gchar *plugin_licence(void)
443 const gchar *plugin_version(void)
448 struct PluginFeature *plugin_provides(void)
450 static struct PluginFeature features[] =
451 { {PLUGIN_MIMEPARSER, "application/ms-tnef"},
452 {PLUGIN_NOTHING, NULL}};