Tizen 2.0 Release
[platform/core/messaging/email-service.git] / email-core / email-core-imap-idle.c
1 /*
2 *  email-service
3 *
4 * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
5 *
6 * Contact: Kyuho Jo <kyuho.jo@samsung.com>, Sunghyun Kwon <sh0701.kwon@samsung.com>
7
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 */
21
22 /**
23  *
24  * This file contains functionality related to IMAP IDLE.
25  * to interact with email-service.
26  * @file                em_core-imap-idle.c
27  * @author      
28  * @version     0.1
29  * @brief               This file contains functionality to provide IMAP IDLE support in email-service. 
30  */
31
32 #include <email-internal-types.h>
33
34 #ifdef __FEATURE_IMAP_IDLE__
35 #include <glib.h>
36 #include <openssl/ssl.h>
37 #include "c-client.h"
38 #include "lnx_inc.h"
39 #include "email-core-imap-idle.h"
40 #include "email-debug-log.h"
41 #include "email-storage.h" 
42 #include "email-network.h"
43 #include "email-core-utils.h"
44 #include "email-core-mailbox.h"
45 #include "email-core-event.h"
46 #include "email-core-account.h"
47
48
49 /*Definitions copied temporarily from ssl_unix.c */
50 #define SSLBUFLEN 8192
51
52 typedef struct ssl_stream {
53   TCPSTREAM *tcpstream; /* TCP stream */
54   SSL_CTX *context;     /* SSL context */
55   SSL *con;             /* SSL connection */
56   int ictr;             /* input counter */
57   char *iptr;           /* input pointer */
58   char ibuf[SSLBUFLEN]; /* input buffer */
59 } SSLSTREAM;
60 /*Definitions copied temporarily from ssl_unix.c - end*/
61
62 thread_t imap_idle_thread;
63 int g_imap_idle_thread_alive = 0;
64
65 void* emcore_imap_idle_run(void* thread_user_data);
66 static int emcore_imap_idle_parse_response_stream(email_mailbox_t *mailbox, int *err_code);
67 static int emcore_imap_idle_connect_and_idle_on_mailbox(email_mailbox_t *mailbox, int *err_code);
68
69 int emcore_create_imap_idle_thread(int account_id, int *err_code)
70 {
71         EM_DEBUG_FUNC_BEGIN("account_id [%d], err_code [%p]", account_id, err_code);
72         int ret = false;
73         int err = EMAIL_ERROR_NONE;
74         int thread_error;
75
76         g_imap_idle_thread_alive = 1;
77         THREAD_CREATE(imap_idle_thread, emcore_imap_idle_run, NULL, thread_error);
78
79         if (thread_error != 0) {
80                 EM_DEBUG_EXCEPTION("cannot make IMAP IDLE thread...");
81                 err = EMAIL_ERROR_UNKNOWN;
82                 g_imap_idle_thread_alive = 0;
83                 goto FINISH_OFF;
84         }
85
86         ret = true;
87
88 FINISH_OFF:
89         if (err_code != NULL)
90                 *err_code = err;
91
92         EM_DEBUG_FUNC_END("ret [%d]", ret);
93         return ret;
94 }
95
96 /*
97 Need to identify various scenarios where thread needs to be killed
98 1. After the timer set to 10 min expires.
99 2. When No SIM, thread is created and emnetwork_check_network_status() fails.
100 */
101 int emcore_kill_imap_idle_thread(int *err_code)
102 {
103         EM_DEBUG_FUNC_BEGIN("err_code [%p]", err_code);
104         int ret = true;
105         int err = EMAIL_ERROR_NONE;
106
107         EM_DEBUG_LOG("killing thread");
108
109         /* kill the thread */
110         EM_DEBUG_LOG("Before g_thread_exit");
111         g_imap_idle_thread_alive = 0;
112         EM_DEBUG_LOG("After g_thread_exit");
113
114         EM_DEBUG_LOG("Making imap idle NULL");
115         imap_idle_thread = 0;
116         EM_DEBUG_LOG("killed IMAP IDLE");
117
118         if (err_code)
119                 *err_code = err;
120
121         EM_DEBUG_FUNC_END("ret [%d]", ret);
122         return ret;
123 }
124
125 int emcore_imap_idle_loop_start(email_mailbox_t *mailbox_list,  int num, int *err_code)
126 {
127         EM_DEBUG_FUNC_BEGIN("mailbox_list[%p], num[%d]", mailbox_list, num);
128         fd_set readfds;
129         int maxfd = 0;
130         int err = EMAIL_ERROR_NONE;
131         int counter = 0;
132         int select_result = 0;
133         int ret = false;
134         email_mailbox_t *temp = NULL;
135         struct timeval timeout;
136
137         EM_DEBUG_EXCEPTION(">>>>>>> emcore_imap_idle_loop_start start ");
138         if (!mailbox_list || !num) {
139                 EM_DEBUG_EXCEPTION("EMAIL_ERROR_INVALID_PARAM");
140                 err = EMAIL_ERROR_INVALID_PARAM;
141                 goto FINISH_OFF;
142         }
143
144         /* set timeout value to 10min */
145         timeout.tv_sec = 10 * 60;
146         timeout.tv_usec = 0;
147
148         /* IMAP IDLE LOOP */
149         while (1){
150                 EM_DEBUG_EXCEPTION(">>>>>>>>>>>IDLING>>>>>>>>>>>>");
151                 FD_ZERO(&readfds);
152                 temp = mailbox_list;
153                 for (counter = 0; counter < num; counter++) {
154                         FD_SET(temp->hold_connection, &readfds);
155                         if (temp->hold_connection > maxfd)
156                                 maxfd = temp->hold_connection; 
157                         temp = temp->next;
158                 }
159                 maxfd++;
160                 temp = mailbox_list;
161                 
162                 select_result = select(maxfd, &readfds, NULL, NULL, &timeout);
163
164                 if (select_result > 0) /* Data coming on some socket */ {
165                         EM_DEBUG_EXCEPTION(">>>> Data Coming from Socket ");
166                         for (counter = 0; counter < num; counter++) {
167                                 if (temp && FD_ISSET(temp->hold_connection, &readfds)) {
168                                         if (!emcore_imap_idle_parse_response_stream(temp, &err)) {
169                                                 EM_DEBUG_EXCEPTION(">>>> emcore_imap_idle_loop_start 6 ");
170                                                 emcore_close_mailbox(temp->account_id, temp->mail_stream);
171                                                 EM_DEBUG_EXCEPTION(">>>> emcore_imap_idle_loop_start 7 ");
172                                                 goto FINISH_OFF;
173                                         }
174                                         break; /* break for now - check if it is possible to get data on two sockets - shasikala.p */
175                                 }
176                                 temp = temp->next;
177                         }
178                 }
179
180                 else if (select_result == 0) /* Timeout occurred */ {
181                         EM_DEBUG_EXCEPTION(">>>> emcore_imap_idle_loop_start 8 ");
182                         IMAPLOCAL *imap_local = NULL;
183                         char cmd[128], tag[32];
184                         char *p = NULL;
185                         /* send DONE Command */
186                         /* free all data here */
187                         for (counter = 0; counter < num; counter++) {
188                                 EM_DEBUG_LOG(">>>> emcore_imap_idle_loop_start 9 ");
189                                 if (temp && temp->mail_stream) {
190                                         imap_local = ((MAILSTREAM *)temp->mail_stream)->local;
191
192                                         sprintf(tag, "%08lx", 0xffffffff & (((MAILSTREAM *)temp->mail_stream)->gensym++));
193                                         sprintf(cmd, "%s DONE\015\012", tag);
194
195                                         if (!imap_local->netstream || !net_sout(imap_local->netstream, cmd, (int)EM_SAFE_STRLEN(cmd))) {
196                                                 EM_DEBUG_EXCEPTION("network error - failed to DONE on Mailbox - %s ", temp->name);
197                                         }
198                                         else {
199                                                 while (imap_local->netstream) {
200                                                 p = net_getline(imap_local->netstream);
201                                                 EM_DEBUG_EXCEPTION("p =[%s]", p);
202                                                         emcore_close_mailbox(temp->account_id, temp->mail_stream);
203                                                         break;
204                                             }
205                                         }
206                                 }
207                                 temp = temp->next;
208                         }
209                         
210                         
211                         /* kill idle thread */
212                         emcore_kill_imap_idle_thread(&err);
213                         break;
214                 }
215
216                 else {
217                         /*
218                         might happen that a socket descriptor passed to select got closed
219                         check which got closed and make hold_connection 0
220                         */
221                         EM_DEBUG_EXCEPTION(">>>>>> socket descriptor error :  No Data ");
222                         break;
223                 }
224
225                 select_result = 0;
226         }
227
228         ret = true;
229
230         EM_DEBUG_LOG(">>>> emcore_imap_idle_loop_start 17  ");
231
232 FINISH_OFF: 
233
234         if (err_code)
235                 *err_code = err;
236
237         EM_DEBUG_FUNC_END("ret [%d]", ret);
238         return ret;
239 }
240
241 void* emcore_imap_idle_run(void* thread_user_data)
242 {
243         EM_DEBUG_FUNC_BEGIN("thread_user_data [%p]", thread_user_data);
244         char              *mailbox_list[3];
245         int                mailbox_num = 0;
246         int                err = EMAIL_ERROR_NONE;
247         int                flag = true;
248         int                num = 0;
249         int                counter = 0;
250         int                accountID = (int)thread_user_data;
251         email_mailbox_t     *mail_box_list = NULL;
252         email_mailbox_t     *curr_mailbox = NULL;
253         email_mailbox_t     *prev_mailbox = NULL;
254         emstorage_mailbox_tbl_t *local_mailbox = NULL;
255         email_session_t     *session = NULL;
256
257         if ( !emnetwork_check_network_status(&err)) {
258                 EM_DEBUG_EXCEPTION("emnetwork_check_network_status failed [%d]", err);
259                 goto FINISH_OFF;
260         }
261         /* Connect to DB */
262         if (!emstorage_open(&err)) {
263                 EM_DEBUG_EXCEPTION("emstorage_open falied [%d]", err);
264                 goto FINISH_OFF;
265         }
266
267         if (!emcore_get_empty_session(&session))
268                 EM_DEBUG_EXCEPTION("emcore_get_empty_session failed...");
269
270         /* get the list of mailbox name on which IDLE notifications are required. */
271         /* currently all INBOXES of all accounts need to be notified */
272         /* Dependent on GetMyIdentities for account names */
273
274         /* For testing - mailbox_num and mailbox_list are hardcoded here */
275         mailbox_num     = 1;
276         mailbox_list[0] = strdup("INBOX");
277
278         /* make a list of mailboxes IDLING */
279         for (counter = 0; counter < mailbox_num; counter++){
280                 EM_DEBUG_EXCEPTION(">>>> emcore_imap_idle_run 4 ");
281                 if (!emstorage_get_mailbox_by_name(accountID, 0,  mailbox_list[counter], &local_mailbox, true, &err)) {
282                         EM_DEBUG_EXCEPTION("emstorage_get_mailbox_by_name failed [%d]", err);
283                 }
284                 else {
285                         curr_mailbox             = em_malloc(sizeof(email_mailbox_t));
286                         curr_mailbox->account_id = local_mailbox->account_id;
287                         curr_mailbox->mailbox_name       = EM_SAFE_STRDUP(local_mailbox->mailbox_name);
288                         curr_mailbox->local      = local_mailbox->local_yn;
289                         if (!emcore_imap_idle_connect_and_idle_on_mailbox(curr_mailbox, &err)) {
290                                 EM_DEBUG_EXCEPTION("emcore_imap_idle_connect_and_idle_on_mailbox failed [%d]", err);
291                                 emcore_free_mailbox(curr_mailbox);
292                                 EM_SAFE_FREE(curr_mailbox);
293                         }
294                         else {
295                                 if (flag) {
296                                         mail_box_list = curr_mailbox;
297                                         prev_mailbox = curr_mailbox;
298                                         flag = false;
299                                         num++;
300                                 }
301                                 else {
302                                         prev_mailbox->next = curr_mailbox;
303                                         prev_mailbox = curr_mailbox;
304                                         num++;
305                                 }
306                         }
307                 }
308                 if (local_mailbox != NULL)
309                         emstorage_free_mailbox(&local_mailbox, 1, NULL);
310         }
311
312         emcore_clear_session(session);
313         emcore_imap_idle_loop_start(mail_box_list, num, NULL);
314
315 FINISH_OFF:
316
317         if (!emstorage_close(&err)) {
318                 EM_DEBUG_EXCEPTION("emstorage_close falied [%d]", err);
319         }
320
321         EM_DEBUG_FUNC_END();
322         return NULL;
323 }
324
325 int emcore_imap_idle_insert_sync_event(email_mailbox_t *mailbox, int *err_code)
326 {
327         EM_DEBUG_FUNC_BEGIN();
328         
329         int ret = false;
330         int err = EMAIL_ERROR_NONE;
331         int handle;
332         
333         if (!mailbox || mailbox->account_id <= 0) {
334                 EM_DEBUG_EXCEPTION("EMAIL_ERROR_INVALID_PARAM");
335                 err = EMAIL_ERROR_INVALID_PARAM;
336                 goto FINISH_OFF;
337         }
338         email_event_t event_data = { 0 };
339
340         event_data.type               = EMAIL_EVENT_SYNC_HEADER;
341         event_data.event_param_data_1 = mailbox ? EM_SAFE_STRDUP(mailbox->mailbox_name)  :  NULL;
342         event_data.event_param_data_3 = NULL;
343         event_data.account_id         = mailbox->account_id;
344                         
345         if (!emcore_insert_event(&event_data, &handle, &err)) {
346                 EM_DEBUG_EXCEPTION("emcore_insert_event failed [%d]", err);
347                 goto FINISH_OFF;
348         }
349
350         ret = true;
351
352 FINISH_OFF:
353
354         if (err_code)
355                 *err_code = err;
356         EM_DEBUG_FUNC_END("ret [%d]", ret);
357         return ret;
358 }
359
360 /* connects to given mailbox. send idle and returns socket id */
361 static int emcore_imap_idle_connect_and_idle_on_mailbox(email_mailbox_t *mailbox, int *err_code)
362 {
363         EM_DEBUG_FUNC_BEGIN("mailbox [%p], err_code [%p]", mailbox, err_code);
364         void          *mail_stream = NULL;
365         char           cmd[128] = { 0, };
366         char           tag[32] = { 0, };
367         char          *p = NULL;
368         int            socket_id = 0;
369         int            ret = 0;
370         int            err = EMAIL_ERROR_NONE;
371         email_account_t *ref_account = NULL;
372         IMAPLOCAL     *imap_local = NULL;
373         NETSTREAM     *net_stream = NULL;
374         TCPSTREAM     *tcp_stream = NULL;
375
376         /* NULL param check */
377         if (!mailbox) {
378                 EM_DEBUG_EXCEPTION("EMAIL_ERROR_INVALID_PARAM");
379                 err = EMAIL_ERROR_INVALID_PARAM;
380                 goto FINISH_OFF;
381         }
382
383         ref_account = emcore_get_account_reference(mailbox->account_id);
384         if (!ref_account) {
385                 EM_DEBUG_EXCEPTION("emcore_get_account_reference failed. account_id[%d]", mailbox->account_id);
386                 err = EMAIL_ERROR_INVALID_ACCOUNT;
387                 goto FINISH_OFF;
388         }
389
390         /* Open connection to mailbox */
391         if (!emcore_connect_to_remote_mailbox(mailbox->account_id, mailbox->mailbox_name, (void **)&mail_stream, &err) || !mail_stream) {
392                 EM_DEBUG_EXCEPTION("emcore_connect_to_remote_mailbox failed [%d]", err);
393                 goto FINISH_OFF;
394         }
395
396         imap_local = ((MAILSTREAM *)mail_stream)->local;
397         net_stream = imap_local->netstream;
398         
399         /* check if ssl option is enabled on this account - shasikala.p */
400
401         if (ref_account->incoming_server_secure_connection){
402                 SSLSTREAM *ssl_stream = net_stream->stream;
403                 tcp_stream = ssl_stream->tcpstream;
404         }
405         else
406                 tcp_stream = net_stream->stream;
407
408         /* Get Socket ID */
409         socket_id = ((TCPSTREAM *)tcp_stream)->tcpsi;
410
411         sprintf(tag, "%08lx", 0xffffffff & (((MAILSTREAM *)mail_stream)->gensym++));
412         sprintf(cmd, "%s IDLE\015\012", tag);
413
414         /* Send IDLE command */
415         if (!imap_local->netstream || !net_sout(imap_local->netstream, cmd, (int)EM_SAFE_STRLEN(cmd))) {
416                 EM_DEBUG_EXCEPTION("network error - failed to IDLE on Mailbox - %s ", mailbox->mailbox_name);
417                 err = EMAIL_ERROR_IMAP4_IDLE_FAILURE;
418                 goto FINISH_OFF;
419         }
420
421         /* Get the response for IDLE command */
422         while (imap_local->netstream){
423                 p = net_getline(imap_local->netstream);
424                 EM_DEBUG_LOG("p =[%s]", p);
425                 if (!strncmp(p, "+", 1)) {
426                         ret = 1;
427                         break;
428                 }
429                 else if (!strncmp(p, "*", 1)) {
430                         EM_SAFE_FREE(p);
431                         continue;
432                 }
433                 else {
434                         ret = 0;
435                         break;
436                 }
437     }
438         EM_SAFE_FREE(p);
439
440 FINISH_OFF: 
441         
442         if (ret) {
443                 /* IMAP IDLE - SUCCESS */
444                 mailbox->mail_stream     = mail_stream;
445                 mailbox->hold_connection = socket_id; /* holds connection continuously on the given socket_id */
446         }
447         else if (mail_stream)
448                 emcore_close_mailbox(mailbox->account_id, mail_stream);
449
450         if (ref_account) {
451                 emcore_free_account(ref_account);
452                 EM_SAFE_FREE(ref_account);
453         }
454
455         if (err_code)
456                 *err_code = err;
457
458         EM_DEBUG_FUNC_END("ret [%d]", ret);
459         return ret;
460 }
461
462 static int emcore_imap_idle_parse_response_stream(email_mailbox_t *mailbox, int *err_code)
463 {
464         EM_DEBUG_FUNC_BEGIN("mailbox [%p], err_code [%p]", mailbox, err_code);
465         int err = EMAIL_ERROR_NONE;
466         char *p = NULL;
467         int ret = false;
468         IMAPLOCAL *imap_local = NULL;
469         
470         if (!mailbox || !mailbox->mail_stream) {
471                 EM_DEBUG_EXCEPTION("EMAIL_ERROR_INVALID_PARAM");
472                 err = EMAIL_ERROR_INVALID_PARAM;
473                 goto FINISH_OFF;
474         }
475
476         imap_local = ((MAILSTREAM *)mailbox->mail_stream)->local;
477
478         if (!imap_local){
479                 EM_DEBUG_EXCEPTION("imap_local is NULL");
480                 err = EMAIL_ERROR_INVALID_PARAM;
481                 goto FINISH_OFF;
482         }
483
484         while (imap_local->netstream){
485                 p = net_getline(imap_local->netstream);
486                 if (p && !strncmp(p, "*", 1)) {
487                         EM_DEBUG_LOG("p is [%s]", p);
488                         if (p+1 && p+2 && p+3 && p+4 && !strncmp(p+2, "BYE", 3)) {
489                                 EM_DEBUG_LOG("BYE connection from server");
490                                 EM_SAFE_FREE(p);
491                                 goto FINISH_OFF;
492                         }
493                         else  { 
494                                 if (!emcore_imap_idle_insert_sync_event(mailbox, &err))
495                                         EM_DEBUG_EXCEPTION("Syncing mailbox %s failed with err_code [%d]", mailbox->mailbox_name, err);
496                                 EM_SAFE_FREE(p);        
497                                 break;
498                         }
499                 }
500                 else if (p && (!strncmp(p, "+", 1))) {
501                         /* Bad response from server */
502                         EM_DEBUG_LOG("p is [%s]", p);
503                         EM_SAFE_FREE(p);
504                         break;
505                 }
506                 else {
507                         EM_DEBUG_LOG("In else part");
508                         break;
509                 }
510         }
511
512         ret = true;
513
514 FINISH_OFF:
515
516         if (err_code)
517                 *err_code = err;
518
519         EM_DEBUG_FUNC_END("ret [%d]", ret);
520         return ret;
521 }
522 #endif /*  __FEATURE_IMAP_IDLE__ */