From ec14e81935047bd2b2182595b76650b5f2fef893 Mon Sep 17 00:00:00 2001 From: hiro Date: Mon, 12 Jul 2010 09:33:27 +0000 Subject: implemented concatenation of partial messages (RFC 2046). git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@2608 ee746299-78ed-0310-b773-934348b2243d --- libsylph/procmime.c | 43 ++++++++++++++++ libsylph/procmime.h | 6 ++- libsylph/procmsg.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++- libsylph/procmsg.h | 5 +- libsylph/utils.c | 99 +++++++++++++++++++++++++++-------- libsylph/utils.h | 7 +++ 6 files changed, 281 insertions(+), 24 deletions(-) (limited to 'libsylph') diff --git a/libsylph/procmime.c b/libsylph/procmime.c index f4f55ede..2789563f 100644 --- a/libsylph/procmime.c +++ b/libsylph/procmime.c @@ -736,6 +736,49 @@ void procmime_scan_content_type_str(const gchar *content_type, procmime_mime_params_free(mparams); } +void procmime_scan_content_type_partial(const gchar *content_type, + gint *total, gchar **part_id, + gint *number) +{ + MimeParams *mparams; + GSList *cur; + gchar *id_str = NULL; + gint t = 0, n = 0; + + *total = 0; + *part_id = NULL; + *number = 0; + + mparams = procmime_parse_mime_parameter(content_type); + + if (!mparams->hvalue || + g_ascii_strcasecmp(mparams->hvalue, "message/partial") != 0) { + procmime_mime_params_free(mparams); + return; + } + + for (cur = mparams->plist; cur != NULL; cur = cur->next) { + MimeParam *param = (MimeParam *)cur->data; + if (!g_ascii_strcasecmp(param->name, "total")) { + t = atoi(param->value); + } else if (!id_str && !g_ascii_strcasecmp(param->name, "id")) { + id_str = g_strdup(param->value); + } else if (!g_ascii_strcasecmp(param->name, "number")) { + n = atoi(param->value); + } + } + + procmime_mime_params_free(mparams); + + if (t > 0 && n > 0 && t >= n && id_str) { + *total = t; + *part_id = id_str; + *number = n; + } else { + g_free(id_str); + } +} + void procmime_scan_content_disposition(MimeInfo *mimeinfo, const gchar *content_disposition) { diff --git a/libsylph/procmime.h b/libsylph/procmime.h index 8d5b1855..4970f7c7 100644 --- a/libsylph/procmime.h +++ b/libsylph/procmime.h @@ -1,6 +1,6 @@ /* * LibSylph -- E-Mail client library - * Copyright (C) 1999-2007 Hiroyuki Yamamoto + * Copyright (C) 1999-2010 Hiroyuki Yamamoto * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -156,6 +156,10 @@ void procmime_scan_content_type_str (const gchar *content_type, gchar **charset, gchar **name, gchar **boundary); +void procmime_scan_content_type_partial (const gchar *content_type, + gint *total, + gchar **part_id, + gint *number); void procmime_scan_content_disposition (MimeInfo *mimeinfo, const gchar *content_disposition); MimeInfo *procmime_scan_mime_header (FILE *fp); diff --git a/libsylph/procmsg.c b/libsylph/procmsg.c index 2f6ebaad..6c384aa6 100644 --- a/libsylph/procmsg.c +++ b/libsylph/procmsg.c @@ -1,6 +1,6 @@ /* * LibSylph -- E-Mail client library - * Copyright (C) 1999-2009 Hiroyuki Yamamoto + * Copyright (C) 1999-2010 Hiroyuki Yamamoto * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1724,6 +1724,149 @@ void procmsg_print_message_part(MsgInfo *msginfo, MimeInfo *partinfo, g_free(prtmp); } +/** + * procmsg_concat_partial_messages: + * @mlist: list of MsgInfo* including message/partial messages. + * @file: output file name of concatenated message. + * + * Concatenate @mlist which consists of message/partial messages and + * output to @file. If @mlist has different partial id, the first one + * is used. + * + * Return value: 0 on success, or -1 if failed. + **/ +gint procmsg_concat_partial_messages(GSList *mlist, const gchar *file) +{ + static HeaderEntry hentry[] = {{"Content-Type:", NULL, FALSE}, + {NULL, NULL, FALSE}}; + FILE *fp; + gchar buf[BUFFSIZE]; + FILE *tmp_fp; + gchar *part_id = NULL; + gint total = 0; + MsgInfo *msg_array[100] = {NULL}; + MsgInfo *msginfo; + MimeInfo *mimeinfo; + GSList *cur; + gint i; + + g_return_val_if_fail(mlist != NULL, -1); + g_return_val_if_fail(file != NULL, -1); + + debug_print("procmsg_concat_partial_messages\n"); + + for (cur = mlist; cur != NULL; cur = cur->next) { + gint n = 0; + gint t = 0; + gchar *cur_id = NULL; + + msginfo = (MsgInfo *)cur->data; + + fp = procmsg_open_message_decrypted(msginfo, &mimeinfo); + if (!fp) + continue; + if (!mimeinfo->content_type || + g_ascii_strcasecmp(mimeinfo->content_type, "message/partial") != 0) + goto skip; + + rewind(fp); + if (procheader_get_one_field(buf, sizeof(buf), fp, hentry) == -1) + goto skip; + + procmime_scan_content_type_partial(buf + strlen(hentry[0].name), &t, &cur_id, &n); + if (n == 0 || t > 100 || n > t) { + debug_print("skip\n"); + g_free(cur_id); + goto skip; + } + + debug_print("partial: %d/%d id=%s\n", n, t, cur_id); + if (!part_id) + part_id = g_strdup(cur_id); + if (total == 0) + total = t; + + if (total != t || strcmp(part_id, cur_id) != 0) { + debug_print("skip\n"); + g_free(cur_id); + goto skip; + } + + msg_array[n - 1] = msginfo; + + g_free(cur_id); +skip: + fclose(fp); + procmime_mimeinfo_free_all(mimeinfo); + } + + if (!part_id) { + debug_print("piece not found\n"); + return -1; + } + + g_print("part_id = %s , total = %d\n", part_id, total); + g_free(part_id); + + /* check if all pieces exist */ + for (i = 0; i < total; i++) { + if (msg_array[i] == NULL) { + debug_print("message part %d not exist\n", i + 1); + return -1; + } + } + + /* concatenate parts */ + if ((tmp_fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return -1; + } + + for (i = 0; i < total; i++) { + msginfo = msg_array[i]; + off_t out_size; + gint empty_line_size = 0; + + fp = procmsg_open_message_decrypted(msginfo, &mimeinfo); + if (!fp) { + g_warning("cannot open message part %d\n", i + 1); + fclose(tmp_fp); + g_unlink(file); + return -1; + } + + out_size = get_left_file_size(fp); + if (out_size < 0) { + g_warning("cannot tell left file size of part %d\n", i + 1); + fclose(tmp_fp); + g_unlink(file); + return -1; + } + empty_line_size = get_last_empty_line_size(fp, out_size); + if (empty_line_size < 0) { + g_warning("cannot get last empty line size of part %d\n", i + 1); + fclose(tmp_fp); + g_unlink(file); + return -1; + } + + if (append_file_part(fp, ftell(fp), out_size - empty_line_size, + tmp_fp) < 0) { + g_warning("write failed\n"); + fclose(tmp_fp); + g_unlink(file); + return -1; + } + + fclose(fp); + procmime_mimeinfo_free_all(mimeinfo); + } + + fclose(tmp_fp); + + return 0; +} + static gboolean procmsg_get_flags(FolderItem *item, gint num, MsgPermFlags *flags) { diff --git a/libsylph/procmsg.h b/libsylph/procmsg.h index 32ec3dff..894e09f6 100644 --- a/libsylph/procmsg.h +++ b/libsylph/procmsg.h @@ -1,6 +1,6 @@ /* * LibSylph -- E-Mail client library - * Copyright (C) 1999-2009 Hiroyuki Yamamoto + * Copyright (C) 1999-2010 Hiroyuki Yamamoto * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -325,6 +325,9 @@ void procmsg_print_message_part (MsgInfo *msginfo, const gchar *cmdline, gboolean all_headers); +gint procmsg_concat_partial_messages (GSList *mlist, + const gchar *file); + MsgInfo *procmsg_get_msginfo (FolderItem *item, gint num); diff --git a/libsylph/utils.c b/libsylph/utils.c index 145eccc6..23bb3b62 100644 --- a/libsylph/utils.c +++ b/libsylph/utils.c @@ -2422,6 +2422,49 @@ off_t get_left_file_size(FILE *fp) return size; } +gint get_last_empty_line_size(FILE *fp, off_t size) +{ + glong pos; + gint lsize = 0; + gchar buf[4]; + size_t nread; + + if (size < 4) + return -1; + + if ((pos = ftell(fp)) < 0) { + perror("ftell"); + return -1; + } + if (fseek(fp, size - 4, SEEK_CUR) < 0) { + perror("fseek"); + return -1; + } + + /* read last 4 bytes */ + nread = fread(buf, sizeof(buf), 1, fp); + if (nread != 1) { + perror("fread"); + return -1; + } + g_print("last 4 bytes: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); + if (buf[3] == '\n') { + if (buf[2] == '\n') + lsize = 1; + else if (buf[2] == '\r') { + if (buf[1] == '\n') + lsize = 2; + } + } + + if (fseek(fp, pos, SEEK_SET) < 0) { + perror("fseek"); + return -1; + } + + return lsize; +} + gboolean file_exist(const gchar *file, gboolean allow_fifo) { if (file == NULL) @@ -2989,29 +3032,20 @@ gint move_file(const gchar *src, const gchar *dest, gboolean overwrite) return 0; } -gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest) +gint append_file_part(FILE *fp, off_t offset, size_t length, FILE *dest_fp) { - FILE *dest_fp; gint n_read; gint bytes_left, to_read; gchar buf[BUFSIZ]; - gboolean err = FALSE; + + g_return_val_if_fail(fp != NULL, -1); + g_return_val_if_fail(dest_fp != NULL, -1); if (fseek(fp, offset, SEEK_SET) < 0) { perror("fseek"); return -1; } - if ((dest_fp = g_fopen(dest, "wb")) == NULL) { - FILE_OP_ERROR(dest, "fopen"); - return -1; - } - - if (change_file_mode_rw(dest_fp, dest) < 0) { - FILE_OP_ERROR(dest, "chmod"); - g_warning("can't change file mode\n"); - } - bytes_left = length; to_read = MIN(bytes_left, sizeof(buf)); @@ -3019,9 +3053,7 @@ gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest) if (n_read < to_read && ferror(fp)) break; if (fwrite(buf, n_read, 1, dest_fp) < 1) { - g_warning(_("writing to %s failed.\n"), dest); - fclose(dest_fp); - g_unlink(dest); + g_warning("append_file_part: writing to file failed.\n"); return -1; } bytes_left -= n_read; @@ -3032,14 +3064,39 @@ gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest) if (ferror(fp)) { perror("fread"); - err = TRUE; + return -1; } - if (fclose(dest_fp) == EOF) { - FILE_OP_ERROR(dest, "fclose"); - err = TRUE; + if (fflush(dest_fp) == EOF) { + FILE_OP_ERROR("append_file_part", "fflush"); + return -1; } - if (err) { + return 0; +} + +gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest) +{ + FILE *dest_fp; + + if ((dest_fp = g_fopen(dest, "wb")) == NULL) { + FILE_OP_ERROR(dest, "fopen"); + return -1; + } + + if (change_file_mode_rw(dest_fp, dest) < 0) { + FILE_OP_ERROR(dest, "chmod"); + g_warning("can't change file mode\n"); + } + + if (append_file_part(fp, offset, length, dest_fp) < 0) { + g_warning("writing to %s failed.\n", dest); + fclose(dest_fp); + g_unlink(dest); + return -1; + } + + if (fclose(dest_fp) == EOF) { + FILE_OP_ERROR(dest, "fclose"); g_unlink(dest); return -1; } diff --git a/libsylph/utils.h b/libsylph/utils.h index 93d9533f..200f03b3 100644 --- a/libsylph/utils.h +++ b/libsylph/utils.h @@ -416,6 +416,9 @@ off_t get_file_size (const gchar *file); off_t get_file_size_as_crlf (const gchar *file); off_t get_left_file_size (FILE *fp); +gint get_last_empty_line_size (FILE *fp, + off_t size); + gboolean file_exist (const gchar *file, gboolean allow_fifo); gboolean is_dir_exist (const gchar *dir); @@ -447,6 +450,10 @@ gint copy_dir (const gchar *src, gint move_file (const gchar *src, const gchar *dest, gboolean overwrite); +gint append_file_part (FILE *fp, + off_t offset, + size_t length, + FILE *dest_fp); gint copy_file_part (FILE *fp, off_t offset, size_t length, -- cgit v1.2.3