Fixes NB#131351, crash when exiting modest if there are new connection attempts
[modest] / src / modest-tny-local-folders-account.c
1 /* Copyright (c) 2007, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30
31 #include <string.h>
32 #include <stdio.h>
33 #include <config.h>
34 #include <glib/gi18n.h>
35 #include <tny-error.h>
36 #include <modest-tny-local-folders-account.h>
37 #include <modest-tny-outbox-account.h>
38 #include <modest-tny-folder.h>
39 #include <tny-camel-folder.h>
40 #include <tny-merge-folder.h>
41 #include <tny-simple-list.h>
42 #include <tny-gtk-lockable.h>
43
44 G_DEFINE_TYPE (ModestTnyLocalFoldersAccount, 
45         modest_tny_local_folders_account, 
46         TNY_TYPE_CAMEL_STORE_ACCOUNT);
47
48 #define TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE(o) \
49   (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_TNY_LOCAL_FOLDERS_ACCOUNT, ModestTnyLocalFoldersAccountPrivate))
50
51 typedef struct _ModestTnyLocalFoldersAccountPrivate ModestTnyLocalFoldersAccountPrivate;
52
53 struct _ModestTnyLocalFoldersAccountPrivate
54 {
55         TnyMergeFolder *outbox_folder;
56 };
57
58 static void         get_folders    (TnyFolderStore *self, 
59                                     TnyList *list, 
60                                     TnyFolderStoreQuery *query,
61                                     gboolean refresh, 
62                                     GError **err);
63
64 static TnyFolder*   create_folder  (TnyFolderStore *self, 
65                                     const gchar *name, 
66                                     GError **err);
67
68 enum {
69         OUTBOX_DELETED_SIGNAL,
70         LAST_SIGNAL
71 };
72
73 static guint signals[LAST_SIGNAL] = {0};
74
75 static void
76 modest_tny_local_folders_account_finalize (GObject *object)
77 {
78         ModestTnyLocalFoldersAccountPrivate *priv;
79
80         priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (object);
81         if (priv->outbox_folder) {
82                 g_object_unref (priv->outbox_folder);
83                 priv->outbox_folder = NULL;
84         }
85         G_OBJECT_CLASS (modest_tny_local_folders_account_parent_class)->finalize (object);
86 }
87
88 static void
89 modest_tny_local_folders_account_class_init (ModestTnyLocalFoldersAccountClass *klass)
90 {
91         GObjectClass *object_class = G_OBJECT_CLASS (klass);
92
93         g_type_class_add_private (klass, sizeof (ModestTnyLocalFoldersAccountPrivate));
94
95         object_class->finalize = modest_tny_local_folders_account_finalize;
96
97         /* Signals */
98
99         /* Note that this signal is removed before unsetting my own
100            reference to outbox, this means that by the time of this
101            call, modest_tny_local_folders_account_get_merged_outbox is
102            still valid. The reason is that the listeners of the signal
103            might want to do something with the outbox instance */
104         signals[OUTBOX_DELETED_SIGNAL] = g_signal_new
105         ("outbox-deleted", MODEST_TYPE_TNY_LOCAL_FOLDERS_ACCOUNT,
106         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET
107         (ModestTnyLocalFoldersAccountClass, outbox_deleted), NULL,
108         NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
109
110         /* Override virtual functions from the parent class: */
111         TNY_CAMEL_STORE_ACCOUNT_CLASS(klass)->get_folders = get_folders;
112         TNY_CAMEL_STORE_ACCOUNT_CLASS(klass)->create_folder = create_folder;
113 }
114
115 static void
116 modest_tny_local_folders_account_init (ModestTnyLocalFoldersAccount *self)
117 {
118         /* Do nothing */
119 }
120
121 ModestTnyLocalFoldersAccount*
122 modest_tny_local_folders_account_new (void)
123 {
124   return g_object_new (MODEST_TYPE_TNY_LOCAL_FOLDERS_ACCOUNT, NULL);
125 }
126
127 /**********************************************************/
128 /*          TnyCamelStoreAccount functions redefinitions  */
129 /**********************************************************/
130 static gboolean 
131 modest_tny_local_folders_account_query_passes (TnyFolderStoreQuery *query, TnyFolder *folder)
132 {
133         gboolean retval = FALSE;
134
135         if (query && (tny_list_get_length (tny_folder_store_query_get_items (query)) > 0)) {
136                 TnyList *items = tny_folder_store_query_get_items (query);
137                 TnyIterator *iterator;
138                 iterator = tny_list_create_iterator (items);
139
140                 while (!tny_iterator_is_done (iterator))
141                 {
142                         TnyFolderStoreQueryItem *item = (TnyFolderStoreQueryItem*) tny_iterator_get_current (iterator);
143                         if (item) {
144                                 TnyFolderStoreQueryOption options = tny_folder_store_query_item_get_options (item);
145                                 const regex_t *regex = tny_folder_store_query_item_get_regex (item);
146
147                                 if ((options & TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED) &&
148                                    tny_folder_is_subscribed (folder))
149                                         retval = TRUE;
150
151                                 if ((options & TNY_FOLDER_STORE_QUERY_OPTION_UNSUBSCRIBED) &&
152                                     !(tny_folder_is_subscribed (folder)))
153                                         retval = TRUE;
154
155                                 if (regex && options & TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME)
156                                    if (regexec (regex, tny_folder_get_name (folder), 0, NULL, 0) == 0)
157                                         retval = TRUE;
158
159                                 if (regex && options & TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_ID)
160                                   if (regexec (regex, tny_folder_get_id (folder), 0, NULL, 0) == 0)
161                                         retval = TRUE;
162
163                                 g_object_unref (G_OBJECT (item));
164                         }
165
166                         tny_iterator_next (iterator);
167                 }
168                  
169                 g_object_unref (G_OBJECT (iterator));
170                 g_object_unref (G_OBJECT (items));
171         } else
172                 retval = TRUE;
173
174         return retval;
175 }
176
177 static void
178 get_folders (TnyFolderStore *self, 
179              TnyList *list, 
180              TnyFolderStoreQuery *query, 
181              gboolean refresh, 
182              GError **err)
183 {
184         TnyCamelStoreAccountClass *parent_class;
185         ModestTnyLocalFoldersAccountPrivate *priv;
186
187         /* Call the base class implementation: */
188         parent_class = g_type_class_peek_parent (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT_GET_CLASS (self));
189         parent_class->get_folders (self, list, query, refresh, err);
190         
191         /* Add our extra folder only if it passes the query */
192         priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (self);
193                 
194         if (priv->outbox_folder && 
195             modest_tny_local_folders_account_query_passes (query, TNY_FOLDER (priv->outbox_folder)))
196                 tny_list_prepend (list, G_OBJECT (priv->outbox_folder));
197 }
198
199 static TnyFolder*
200 create_folder (TnyFolderStore *self, 
201                const gchar *name, 
202                GError **err)
203 {
204         TnyCamelStoreAccountClass *parent_class;
205
206         parent_class = g_type_class_peek_parent (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT_GET_CLASS (self));
207
208         /* If the folder name is been used by our extra folders */
209         if (modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (self), name)) {
210                 g_set_error (err, TNY_ERROR_DOMAIN, 
211                              TNY_SERVICE_ERROR_FOLDER_CREATE,
212                              "Folder name already in use");
213                 return NULL;
214         }
215
216         /* Call the base class implementation: */
217         return parent_class->create_folder (self, name, err);
218 }
219
220 /*****************************/
221 /*      Public methods       */ 
222 /*****************************/
223 gboolean
224 modest_tny_local_folders_account_folder_name_in_use (ModestTnyLocalFoldersAccount *self,
225                                                      const gchar *name)
226 {
227         ModestTnyLocalFoldersAccountPrivate *priv;
228         gchar *down_name;
229         const gchar *type_name;
230         gboolean retval;
231         
232         /* Check that we're not trying to create/rename any folder
233            with the same name that our OUTBOX, DRAFT, SENT */
234         priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (self);
235         down_name = g_utf8_strdown (name, strlen (name));
236
237         type_name = modest_local_folder_info_get_type_name (TNY_FOLDER_TYPE_OUTBOX);
238         if (!strcmp (type_name, down_name)) {
239                 retval = TRUE;
240         } else {
241                 type_name = modest_local_folder_info_get_type_name (TNY_FOLDER_TYPE_DRAFTS);
242                 if (!strcmp (type_name, down_name)) {
243                         retval = TRUE;
244                 } else {
245                         type_name = modest_local_folder_info_get_type_name (TNY_FOLDER_TYPE_SENT);
246                         if (!strcmp (type_name, down_name)) {
247                                 retval = TRUE;
248                         } else {
249                                 retval = FALSE;
250                         }
251                 }
252         }
253         g_free (down_name);
254
255         return retval;
256 }
257
258 void
259 modest_tny_local_folders_account_add_folder_to_outbox (ModestTnyLocalFoldersAccount *self, 
260                                                        TnyFolder *per_account_outbox)
261 {
262         ModestTnyLocalFoldersAccountPrivate *priv;
263
264         g_return_if_fail (MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (self));
265         g_return_if_fail (TNY_IS_FOLDER (per_account_outbox));
266
267         priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (self);
268
269         /* Create on-demand */
270         if (!priv->outbox_folder) {
271                 priv->outbox_folder = (TnyMergeFolder *) 
272                         tny_merge_folder_new_with_ui_locker (_("mcen_me_folder_outbox"), 
273                                                              tny_gtk_lockable_new ());
274                 /* Set type to outbox */
275                 tny_merge_folder_set_folder_type (priv->outbox_folder, 
276                                                   TNY_FOLDER_TYPE_OUTBOX);
277         }
278
279         /* Add outbox to the global OUTBOX folder */
280         tny_merge_folder_add_folder (priv->outbox_folder, per_account_outbox);
281 }
282
283 void 
284 modest_tny_local_folders_account_remove_folder_from_outbox (ModestTnyLocalFoldersAccount *self, 
285                                                             TnyFolder *per_account_outbox)
286 {
287         ModestTnyLocalFoldersAccountPrivate *priv;
288         TnyList *merged_folders = NULL;
289
290         g_return_if_fail (MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (self));
291         g_return_if_fail (TNY_IS_FOLDER (per_account_outbox));
292
293         priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (self);
294
295         /* Remove outbox from the global OUTBOX folder */
296         tny_merge_folder_remove_folder (priv->outbox_folder, per_account_outbox);
297
298         /* If there is no folder in the outbox the delete it */
299         merged_folders = tny_simple_list_new ();
300         tny_merge_folder_get_folders (priv->outbox_folder, merged_folders);
301         if (tny_list_get_length (merged_folders) == 0) {
302                 /* Emit signal */
303                 g_signal_emit ((GObject *)self, signals[OUTBOX_DELETED_SIGNAL], 0);
304
305                 /* Unref my own reference */
306                 g_object_unref (priv->outbox_folder);
307                 priv->outbox_folder = NULL;
308         }
309         g_object_unref (merged_folders);
310 }
311
312 TnyFolder *
313 modest_tny_local_folders_account_get_merged_outbox (ModestTnyLocalFoldersAccount *self)
314 {
315         ModestTnyLocalFoldersAccountPrivate *priv;
316         g_return_val_if_fail (MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (self), NULL);
317
318         priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (self);
319         if (priv->outbox_folder)
320                 return g_object_ref (priv->outbox_folder);
321         else
322                 return NULL;
323 }