Fix error creation and warning
[claws.git] / src / plugins / tnef_parse / tnef_parse.c
blob4f033b402bbf22b19104c687f0f6562be6b842ec
1 /*
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.
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #include "claws-features.h"
24 #endif
26 #include <unistd.h>
27 #include <stdio.h>
29 #include <glib.h>
30 #include <glib/gi18n.h>
31 #include <gtk/gtk.h>
33 #ifdef YTNEF_H_SUBDIR
34 #include <libytnef/tnef-types.h>
35 #include <libytnef/ytnef.h>
36 #include <libytnef/mapi.h>
37 #include <libytnef/mapidefs.h>
38 #else
39 #include <tnef-types.h>
40 #include <ytnef.h>
41 #include <mapi.h>
42 #include <mapidefs.h>
43 #endif
45 #include "common/claws.h"
46 #include "common/version.h"
47 #include "main.h"
48 #include "plugin.h"
49 #include "procmime.h"
50 #include "utils.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);
62 GStatBuf statbuf;
64 if (!fp) {
65 g_free(tmpfilename);
66 return NULL;
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");
74 fprintf(fp, _("\n"
75 "Claws Mail TNEF parser:\n\n"
76 "%s\n"), reason?reason:_("Unknown error"));
78 claws_fclose(fp);
79 if (g_stat(tmpfilename, &statbuf) < 0) {
80 claws_unlink(tmpfilename);
81 procmime_mimeinfo_free_all(&sub_info);
82 return NULL;
86 sub_info->tmp = TRUE;
87 sub_info->length = statbuf.st_size;
88 sub_info->encoding_type = ENC_BINARY;
90 return sub_info;
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);
99 GStatBuf statbuf;
100 if (!fp) {
101 g_free(tmpfilename);
102 return NULL;
104 sub_info = procmime_mimeinfo_new();
105 sub_info->content = MIMECONTENT_FILE;
106 sub_info->data.filename = tmpfilename;
107 sub_info->type = MIMETYPE_APPLICATION;
108 sub_info->subtype = g_strdup("octet-stream");
110 if (filename) {
111 gchar *content_type = NULL;
113 g_hash_table_insert(sub_info->typeparameters,
114 g_strdup("filename"),
115 g_strdup(filename));
117 content_type = procmime_get_mime_type(filename);
118 if (content_type && strchr(content_type, '/')) {
119 g_free(sub_info->subtype);
120 sub_info->subtype = g_strdup(strchr(content_type, '/')+1);
121 *(strchr(content_type, '/')) = '\0';
122 sub_info->type = procmime_get_media_type(content_type);
124 if (content_type)
125 g_free(content_type);
128 if (claws_fwrite(data, 1, size, fp) < size) {
129 FILE_OP_ERROR(tmpfilename, "claws_fwrite");
130 claws_fclose(fp);
131 if (claws_unlink(tmpfilename) < 0)
132 FILE_OP_ERROR(tmpfilename, "claws_unlink");
133 procmime_mimeinfo_free_all(&sub_info);
134 return tnef_broken_mimeinfo(_("Failed to write the part data."));
136 claws_fclose(fp);
138 if (g_stat(tmpfilename, &statbuf) < 0) {
139 if (claws_unlink(tmpfilename) < 0)
140 FILE_OP_ERROR(tmpfilename, "claws_unlink");
141 procmime_mimeinfo_free_all(&sub_info);
142 return tnef_broken_mimeinfo(_("Failed to write the part data."));
143 } else {
144 sub_info->tmp = TRUE;
145 sub_info->length = statbuf.st_size;
146 sub_info->encoding_type = ENC_BINARY;
149 return sub_info;
152 MimeInfo *tnef_parse_vcal(TNEFStruct *tnef)
154 MimeInfo *sub_info = NULL;
155 gchar *tmpfilename = NULL;
156 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
157 GStatBuf statbuf;
158 gboolean result = FALSE;
159 if (!fp) {
160 g_free(tmpfilename);
161 return NULL;
163 sub_info = procmime_mimeinfo_new();
164 sub_info->content = MIMECONTENT_FILE;
165 sub_info->data.filename = tmpfilename;
166 sub_info->type = MIMETYPE_TEXT;
167 sub_info->subtype = g_strdup("calendar");
168 g_hash_table_insert(sub_info->typeparameters,
169 g_strdup("filename"),
170 g_strdup("calendar.ics"));
172 result = SaveVCalendar(fp, tnef);
174 claws_fclose(fp);
176 if (g_stat(tmpfilename, &statbuf) < 0) {
177 result = FALSE;
178 } else {
179 sub_info->tmp = TRUE;
180 sub_info->length = statbuf.st_size;
181 sub_info->encoding_type = ENC_BINARY;
184 if (!result) {
185 claws_unlink(tmpfilename);
186 procmime_mimeinfo_free_all(&sub_info);
187 return tnef_broken_mimeinfo(_("Failed to parse VCalendar data."));
189 return sub_info;
192 MimeInfo *tnef_parse_vtask(TNEFStruct *tnef)
194 MimeInfo *sub_info = NULL;
195 gchar *tmpfilename = NULL;
196 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
197 GStatBuf statbuf;
198 gboolean result = FALSE;
199 if (!fp) {
200 g_free(tmpfilename);
201 return NULL;
203 sub_info = procmime_mimeinfo_new();
204 sub_info->content = MIMECONTENT_FILE;
205 sub_info->data.filename = tmpfilename;
206 sub_info->type = MIMETYPE_TEXT;
207 sub_info->subtype = g_strdup("calendar");
208 g_hash_table_insert(sub_info->typeparameters,
209 g_strdup("filename"),
210 g_strdup("task.ics"));
212 result = SaveVTask(fp, tnef);
214 claws_fclose(fp);
216 if (g_stat(tmpfilename, &statbuf) < 0) {
217 result = FALSE;
218 } else {
219 sub_info->tmp = TRUE;
220 sub_info->length = statbuf.st_size;
221 sub_info->encoding_type = ENC_BINARY;
223 if (!result) {
224 claws_unlink(tmpfilename);
225 procmime_mimeinfo_free_all(&sub_info);
226 return tnef_broken_mimeinfo(_("Failed to parse VTask data."));
228 return sub_info;
231 MimeInfo *tnef_parse_rtf(TNEFStruct *tnef, variableLength *tmp_var)
233 variableLength buf;
234 MimeInfo *info = NULL;
235 buf.data = DecompressRTF(tmp_var, &(buf.size));
236 if (buf.data) {
237 info = tnef_dump_file("message.rtf", buf.data, buf.size);
238 free(buf.data);
239 return info;
240 } else {
241 return NULL;
245 MimeInfo *tnef_parse_vcard(TNEFStruct *tnef)
247 MimeInfo *sub_info = NULL;
248 gchar *tmpfilename = NULL;
249 FILE *fp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
250 GStatBuf statbuf;
251 gboolean result = FALSE;
252 gint ret;
253 if (!fp) {
254 g_free(tmpfilename);
255 return NULL;
257 sub_info = procmime_mimeinfo_new();
258 sub_info->content = MIMECONTENT_FILE;
259 sub_info->data.filename = tmpfilename;
260 sub_info->type = MIMETYPE_TEXT;
261 sub_info->subtype = g_strdup("x-vcard");
262 g_hash_table_insert(sub_info->typeparameters,
263 g_strdup("filename"),
264 g_strdup("contact.vcf"));
266 result = SaveVCard(fp, tnef);
268 claws_fclose(fp);
270 ret = g_stat(tmpfilename, &statbuf);
271 if (ret == -1) {
272 debug_print("couldn't stat tmpfilename '%s'\n", tmpfilename);
275 if ((ret == -1) || !result) {
276 claws_unlink(tmpfilename);
277 procmime_mimeinfo_free_all(&sub_info);
278 return tnef_broken_mimeinfo(_("Failed to parse VCard data."));
281 sub_info->tmp = TRUE;
282 sub_info->length = statbuf.st_size;
283 sub_info->encoding_type = ENC_BINARY;
284 return sub_info;
287 static gboolean tnef_parse (MimeParser *parser, MimeInfo *mimeinfo)
289 TNEFStruct *tnef;
290 MimeInfo *sub_info = NULL;
291 variableLength *tmp_var;
292 Attachment *att;
293 int parse_result = 0;
294 gboolean cal_done = FALSE;
296 if (!procmime_decode_content(mimeinfo)) {
297 debug_print("error decoding\n");
298 return FALSE;
300 debug_print("Tnef parser parsing part (%d).\n", mimeinfo->length);
301 if (mimeinfo->content == MIMECONTENT_FILE)
302 debug_print("content: %s\n", mimeinfo->data.filename);
303 else
304 debug_print("contents in memory (len %"G_GSIZE_FORMAT")\n",
305 strlen(mimeinfo->data.mem));
307 tnef = g_new0(TNEFStruct, 1);
308 TNEFInitialize(tnef);
310 tnef->Debug = debug_get_mode();
312 if (mimeinfo->content == MIMECONTENT_MEM)
313 parse_result = TNEFParseMemory(mimeinfo->data.mem, mimeinfo->length, tnef);
314 else
315 parse_result = TNEFParseFile(mimeinfo->data.filename, tnef);
317 mimeinfo->type = MIMETYPE_MULTIPART;
318 mimeinfo->subtype = g_strdup("mixed");
319 g_hash_table_insert(mimeinfo->typeparameters,
320 g_strdup("description"),
321 g_strdup("Parsed from MS-TNEF"));
323 if (parse_result != 0) {
324 g_warning("failed to parse TNEF data");
325 TNEFFree(tnef);
326 return FALSE;
329 sub_info = NULL;
330 if (tnef->messageClass[0] != '\0') {
331 if (strcmp(tnef->messageClass, "IPM.Contact") == 0)
332 sub_info = tnef_parse_vcard(tnef);
333 else if (strcmp(tnef->messageClass, "IPM.Task") == 0)
334 sub_info = tnef_parse_vtask(tnef);
335 else if (strcmp(tnef->messageClass, "IPM.Appointment") == 0) {
336 sub_info = tnef_parse_vcal(tnef);
337 cal_done = TRUE;
341 if (sub_info)
342 g_node_append(mimeinfo->node, sub_info->node);
343 sub_info = NULL;
345 if (tnef->MapiProperties.count > 0) {
346 tmp_var = MAPIFindProperty (&(tnef->MapiProperties), PROP_TAG(PT_BINARY,PR_RTF_COMPRESSED));
347 if (tmp_var != MAPI_UNDEFINED) {
348 sub_info = tnef_parse_rtf(tnef, tmp_var);
352 if (sub_info)
353 g_node_append(mimeinfo->node, sub_info->node);
354 sub_info = NULL;
356 tmp_var = MAPIFindUserProp(&(tnef->MapiProperties), PROP_TAG(PT_STRING8,0x24));
357 if (tmp_var != MAPI_UNDEFINED) {
358 if (!cal_done && strcmp(tmp_var->data, "IPM.Appointment") == 0) {
359 sub_info = tnef_parse_vcal(tnef);
363 if (sub_info)
364 g_node_append(mimeinfo->node, sub_info->node);
365 sub_info = NULL;
367 att = tnef->starting_attach.next;
368 while (att) {
369 gchar *filename = NULL;
370 gboolean is_object = TRUE;
371 DWORD signature;
373 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(30,0x3707));
374 if (tmp_var == MAPI_UNDEFINED)
375 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(30,0x3001));
376 if (tmp_var == MAPI_UNDEFINED)
377 tmp_var = &(att->Title);
379 if (tmp_var->data)
380 filename = g_strdup(tmp_var->data);
382 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(PT_OBJECT, PR_ATTACH_DATA_OBJ));
383 if (tmp_var == MAPI_UNDEFINED)
384 tmp_var = MAPIFindProperty(&(att->MAPI), PROP_TAG(PT_BINARY, PR_ATTACH_DATA_OBJ));
385 if (tmp_var == MAPI_UNDEFINED) {
386 tmp_var = &(att->FileData);
387 is_object = FALSE;
390 sub_info = tnef_dump_file(filename,
391 tmp_var->data + (is_object ? 16:0),
392 tmp_var->size - (is_object ? 16:0));
394 if (sub_info)
395 g_node_append(mimeinfo->node, sub_info->node);
397 memcpy(&signature, tmp_var->data+(is_object ? 16:0), sizeof(DWORD));
399 if (TNEFCheckForSignature(signature) == 0) {
400 debug_print("that's TNEF stuff, process it\n");
401 tnef_parse(parser, sub_info);
404 sub_info = NULL;
406 att = att->next;
408 g_free(filename);
411 TNEFFree(tnef);
412 return TRUE;
415 gint plugin_init(gchar **error)
417 if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
418 VERSION_NUMERIC, _("TNEF Parser"), error))
419 return -1;
421 tnef_parser = g_new0(MimeParser, 1);
422 tnef_parser->type = MIMETYPE_APPLICATION;
423 tnef_parser->sub_type = "ms-tnef";
424 tnef_parser->parse = tnef_parse;
426 procmime_mimeparser_register(tnef_parser);
428 return 0;
431 gboolean plugin_done(void)
433 procmime_mimeparser_unregister(tnef_parser);
434 g_free(tnef_parser);
435 tnef_parser = NULL;
437 return TRUE;
440 const gchar *plugin_name(void)
442 return _("TNEF Parser");
445 const gchar *plugin_desc(void)
447 return _("This Claws Mail plugin allows you to read application/ms-tnef attachments.\n\n"
448 "The plugin uses the Ytnef library, which is copyright 2002-2007 by "
449 "Randall Hand <yerase@yerot.com>");
452 const gchar *plugin_type(void)
454 return "GTK3";
457 const gchar *plugin_licence(void)
459 return "GPL3+";
462 const gchar *plugin_version(void)
464 return VERSION;
467 struct PluginFeature *plugin_provides(void)
469 static struct PluginFeature features[] =
470 { {PLUGIN_MIMEPARSER, "application/ms-tnef"},
471 {PLUGIN_NOTHING, NULL}};
472 return features;