1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
2 /*
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Michael Zucchi <notzed@ximian.com>
18  */
19 
20 #include "evolution-data-server-config.h"
21 
22 #include <dirent.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 
31 #include <glib/gi18n-lib.h>
32 
33 #include "camel-mh-folder.h"
34 #include "camel-mh-store.h"
35 #include "camel-mh-summary.h"
36 
37 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/
38 
G_DEFINE_TYPE(CamelMhFolder,camel_mh_folder,CAMEL_TYPE_LOCAL_FOLDER)39 G_DEFINE_TYPE (CamelMhFolder, camel_mh_folder, CAMEL_TYPE_LOCAL_FOLDER)
40 
41 static gchar *
42 mh_folder_get_filename (CamelFolder *folder,
43                         const gchar *uid,
44                         GError **error)
45 {
46 	CamelLocalFolder *lf = (CamelLocalFolder *) folder;
47 
48 	return g_strdup_printf ("%s/%s", lf->folder_path, uid);
49 }
50 
51 static gboolean
mh_folder_append_message_sync(CamelFolder * folder,CamelMimeMessage * message,CamelMessageInfo * info,gchar ** appended_uid,GCancellable * cancellable,GError ** error)52 mh_folder_append_message_sync (CamelFolder *folder,
53                                CamelMimeMessage *message,
54                                CamelMessageInfo *info,
55                                gchar **appended_uid,
56                                GCancellable *cancellable,
57                                GError **error)
58 {
59 	CamelLocalFolder *lf = (CamelLocalFolder *) folder;
60 	CamelStream *output_stream;
61 	CamelMessageInfo *mi;
62 	gchar *name;
63 	gboolean has_attachment;
64 
65 	/* FIXME: probably needs additional locking (although mh doesn't appear do do it) */
66 
67 	d (printf ("Appending message\n"));
68 
69 	camel_local_folder_lock_changes (lf);
70 
71 	/* If we can't lock, don't do anything */
72 	if (!lf || camel_local_folder_lock (lf, CAMEL_LOCK_WRITE, error) == -1) {
73 		camel_local_folder_unlock_changes (lf);
74 		return FALSE;
75 	}
76 
77 	/* add it to the summary/assign the uid, etc */
78 	mi = camel_local_summary_add ((CamelLocalSummary *) camel_folder_get_folder_summary (folder), message, info, lf->changes, error);
79 	camel_local_folder_unlock_changes (lf);
80 	if (mi == NULL)
81 		goto check_changed;
82 
83 	has_attachment = camel_mime_message_has_attachment (message);
84 	if (((camel_message_info_get_flags (mi) & CAMEL_MESSAGE_ATTACHMENTS) && !has_attachment) ||
85 	    ((camel_message_info_get_flags (mi) & CAMEL_MESSAGE_ATTACHMENTS) == 0 && has_attachment)) {
86 		camel_message_info_set_flags (mi, CAMEL_MESSAGE_ATTACHMENTS, has_attachment ? CAMEL_MESSAGE_ATTACHMENTS : 0);
87 	}
88 
89 	d (printf ("Appending message: uid is %s\n", camel_message_info_get_uid (mi)));
90 
91 	/* write it out, use the uid we got from the summary */
92 	name = g_strdup_printf ("%s/%s", lf->folder_path, camel_message_info_get_uid (mi));
93 	output_stream = camel_stream_fs_new_with_name (
94 		name, O_WRONLY | O_CREAT, 0600, error);
95 	if (output_stream == NULL)
96 		goto fail_write;
97 
98 	if (camel_data_wrapper_write_to_stream_sync (
99 		(CamelDataWrapper *) message, output_stream, cancellable, error) == -1
100 	    || camel_stream_close (output_stream, cancellable, error) == -1)
101 		goto fail_write;
102 
103 	/* close this? */
104 	g_object_unref (output_stream);
105 
106 	g_free (name);
107 
108 	if (appended_uid)
109 		*appended_uid = g_strdup(camel_message_info_get_uid(mi));
110 
111 	goto check_changed;
112 
113  fail_write:
114 
115 	/* remove the summary info so we are not out-of-sync with the mh folder */
116 	camel_folder_summary_remove (camel_folder_get_folder_summary (folder), mi);
117 
118 	g_prefix_error (
119 		error, _("Cannot append message to mh folder: %s: "), name);
120 
121 	if (output_stream) {
122 		g_object_unref (output_stream);
123 		unlink (name);
124 	}
125 
126 	g_free (name);
127 
128  check_changed:
129 	camel_local_folder_unlock (lf);
130 
131 	camel_local_folder_claim_changes (lf);
132 
133 	g_clear_object (&mi);
134 
135 	return TRUE;
136 }
137 
138 static CamelMimeMessage *
mh_folder_get_message_sync(CamelFolder * folder,const gchar * uid,GCancellable * cancellable,GError ** error)139 mh_folder_get_message_sync (CamelFolder *folder,
140                             const gchar *uid,
141                             GCancellable *cancellable,
142                             GError **error)
143 {
144 	CamelLocalFolder *lf = (CamelLocalFolder *) folder;
145 	CamelStream *message_stream = NULL;
146 	CamelMimeMessage *message = NULL;
147 	CamelMessageInfo *info;
148 	gchar *name = NULL;
149 
150 	d (printf ("getting message: %s\n", uid));
151 
152 	if (!lf || camel_local_folder_lock (lf, CAMEL_LOCK_WRITE, error) == -1)
153 		return NULL;
154 
155 	/* get the message summary info */
156 	if ((info = camel_folder_summary_get (camel_folder_get_folder_summary (folder), uid)) == NULL) {
157 		set_cannot_get_message_ex (
158 			error, CAMEL_FOLDER_ERROR_INVALID_UID,
159 			uid, lf->folder_path, _("No such message"));
160 		goto fail;
161 	}
162 
163 	/* we only need it to check the message exists */
164 	g_clear_object (&info);
165 
166 	name = g_strdup_printf ("%s/%s", lf->folder_path, uid);
167 	message_stream = camel_stream_fs_new_with_name (
168 		name, O_RDONLY, 0, error);
169 	if (message_stream == NULL) {
170 		g_prefix_error (
171 			error, _("Cannot get message %s from folder %s: "),
172 			name, lf->folder_path);
173 		goto fail;
174 	}
175 
176 	message = camel_mime_message_new ();
177 	if (!camel_data_wrapper_construct_from_stream_sync (
178 		(CamelDataWrapper *) message,
179 		message_stream, cancellable, error)) {
180 		g_prefix_error (
181 			error, _("Cannot get message %s from folder %s: "),
182 			name, lf->folder_path);
183 		g_object_unref (message);
184 		message = NULL;
185 
186 	}
187 	g_object_unref (message_stream);
188 
189  fail:
190 	g_free (name);
191 
192 	camel_local_folder_unlock (lf);
193 
194 	camel_local_folder_claim_changes (lf);
195 
196 	return message;
197 }
198 
199 static CamelLocalSummary *
mh_folder_create_summary(CamelLocalFolder * lf,const gchar * folder,CamelIndex * index)200 mh_folder_create_summary (CamelLocalFolder *lf,
201                           const gchar *folder,
202                           CamelIndex *index)
203 {
204 	return (CamelLocalSummary *) camel_mh_summary_new (
205 		CAMEL_FOLDER (lf), folder, index);
206 }
207 
208 static void
camel_mh_folder_class_init(CamelMhFolderClass * class)209 camel_mh_folder_class_init (CamelMhFolderClass *class)
210 {
211 	CamelFolderClass *folder_class;
212 	CamelLocalFolderClass *local_folder_class;
213 
214 	folder_class = CAMEL_FOLDER_CLASS (class);
215 	folder_class->get_filename = mh_folder_get_filename;
216 	folder_class->append_message_sync = mh_folder_append_message_sync;
217 	folder_class->get_message_sync = mh_folder_get_message_sync;
218 
219 	local_folder_class = CAMEL_LOCAL_FOLDER_CLASS (class);
220 	local_folder_class->create_summary = mh_folder_create_summary;
221 }
222 
223 static void
camel_mh_folder_init(CamelMhFolder * mh_folder)224 camel_mh_folder_init (CamelMhFolder *mh_folder)
225 {
226 }
227 
228 CamelFolder *
camel_mh_folder_new(CamelStore * parent_store,const gchar * full_name,guint32 flags,GCancellable * cancellable,GError ** error)229 camel_mh_folder_new (CamelStore *parent_store,
230                      const gchar *full_name,
231                      guint32 flags,
232                      GCancellable *cancellable,
233                      GError **error)
234 {
235 	CamelFolder *folder;
236 	gchar *basename;
237 
238 	d (printf ("Creating mh folder: %s\n", full_name));
239 
240 	basename = g_path_get_basename (full_name);
241 
242 	folder = g_object_new (
243 		CAMEL_TYPE_MH_FOLDER,
244 		"display-name", basename, "full-name", full_name,
245 		"parent-store", parent_store, NULL);
246 	folder = (CamelFolder *) camel_local_folder_construct (
247 		CAMEL_LOCAL_FOLDER (folder), flags, cancellable, error);
248 
249 	g_free (basename);
250 
251 	return folder;
252 }
253 
254