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
);
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");
111 gchar
*content_type
= NULL
;
113 g_hash_table_insert(sub_info
->typeparameters
,
114 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
);
125 g_free(content_type
);
128 if (claws_fwrite(data
, 1, size
, fp
) < size
) {
129 FILE_OP_ERROR(tmpfilename
, "claws_fwrite");
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."));
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."));
144 sub_info
->tmp
= TRUE
;
145 sub_info
->length
= statbuf
.st_size
;
146 sub_info
->encoding_type
= ENC_BINARY
;
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
);
158 gboolean result
= FALSE
;
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
);
176 if (g_stat(tmpfilename
, &statbuf
) < 0) {
179 sub_info
->tmp
= TRUE
;
180 sub_info
->length
= statbuf
.st_size
;
181 sub_info
->encoding_type
= ENC_BINARY
;
185 claws_unlink(tmpfilename
);
186 procmime_mimeinfo_free_all(&sub_info
);
187 return tnef_broken_mimeinfo(_("Failed to parse VCalendar data."));
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
);
198 gboolean result
= FALSE
;
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
);
216 if (g_stat(tmpfilename
, &statbuf
) < 0) {
219 sub_info
->tmp
= TRUE
;
220 sub_info
->length
= statbuf
.st_size
;
221 sub_info
->encoding_type
= ENC_BINARY
;
224 claws_unlink(tmpfilename
);
225 procmime_mimeinfo_free_all(&sub_info
);
226 return tnef_broken_mimeinfo(_("Failed to parse VTask data."));
231 MimeInfo
*tnef_parse_rtf(TNEFStruct
*tnef
, variableLength
*tmp_var
)
234 MimeInfo
*info
= NULL
;
235 buf
.data
= DecompressRTF(tmp_var
, &(buf
.size
));
237 info
= tnef_dump_file("message.rtf", buf
.data
, buf
.size
);
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
);
251 gboolean result
= FALSE
;
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
);
270 ret
= g_stat(tmpfilename
, &statbuf
);
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
;
287 static gboolean
tnef_parse (MimeParser
*parser
, MimeInfo
*mimeinfo
)
290 MimeInfo
*sub_info
= NULL
;
291 variableLength
*tmp_var
;
293 int parse_result
= 0;
294 gboolean cal_done
= FALSE
;
296 if (!procmime_decode_content(mimeinfo
)) {
297 debug_print("error decoding\n");
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
);
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
);
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");
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
);
342 g_node_append(mimeinfo
->node
, sub_info
->node
);
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
);
353 g_node_append(mimeinfo
->node
, sub_info
->node
);
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
);
364 g_node_append(mimeinfo
->node
, sub_info
->node
);
367 att
= tnef
->starting_attach
.next
;
369 gchar
*filename
= NULL
;
370 gboolean is_object
= TRUE
;
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
);
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
);
390 sub_info
= tnef_dump_file(filename
,
391 tmp_var
->data
+ (is_object
? 16:0),
392 tmp_var
->size
- (is_object
? 16:0));
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
);
415 gint
plugin_init(gchar
**error
)
417 if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
418 VERSION_NUMERIC
, _("TNEF Parser"), error
))
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
);
431 gboolean
plugin_done(void)
433 procmime_mimeparser_unregister(tnef_parser
);
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)
457 const gchar
*plugin_licence(void)
462 const gchar
*plugin_version(void)
467 struct PluginFeature
*plugin_provides(void)
469 static struct PluginFeature features
[] =
470 { {PLUGIN_MIMEPARSER
, "application/ms-tnef"},
471 {PLUGIN_NOTHING
, NULL
}};