4 * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Kyuho Jo <kyuho.jo@samsung.com>, Sunghyun Kwon <sh0701.kwon@samsung.com>
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 * This file contains functionality related to IMAP IDLE.
25 * to interact with email-service.
26 * @file em_core-imap-idle.c
29 * @brief This file contains functionality to provide IMAP IDLE support in email-service.
32 #include <email-internal-types.h>
34 #ifdef __FEATURE_IMAP_IDLE__
36 #include <openssl/ssl.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"
49 /*Definitions copied temporarily from ssl_unix.c */
50 #define SSLBUFLEN 8192
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 */
60 /*Definitions copied temporarily from ssl_unix.c - end*/
62 thread_t imap_idle_thread;
63 int g_imap_idle_thread_alive = 0;
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);
69 int emcore_create_imap_idle_thread(int account_id, int *err_code)
71 EM_DEBUG_FUNC_BEGIN("account_id [%d], err_code [%p]", account_id, err_code);
73 int err = EMAIL_ERROR_NONE;
76 g_imap_idle_thread_alive = 1;
77 THREAD_CREATE(imap_idle_thread, emcore_imap_idle_run, NULL, thread_error);
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;
92 EM_DEBUG_FUNC_END("ret [%d]", ret);
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.
101 int emcore_kill_imap_idle_thread(int *err_code)
103 EM_DEBUG_FUNC_BEGIN("err_code [%p]", err_code);
105 int err = EMAIL_ERROR_NONE;
107 EM_DEBUG_LOG("killing thread");
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");
114 EM_DEBUG_LOG("Making imap idle NULL");
115 imap_idle_thread = 0;
116 EM_DEBUG_LOG("killed IMAP IDLE");
121 EM_DEBUG_FUNC_END("ret [%d]", ret);
125 int emcore_imap_idle_loop_start(email_mailbox_t *mailbox_list, int num, int *err_code)
127 EM_DEBUG_FUNC_BEGIN("mailbox_list[%p], num[%d]", mailbox_list, num);
130 int err = EMAIL_ERROR_NONE;
132 int select_result = 0;
134 email_mailbox_t *temp = NULL;
135 struct timeval timeout;
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;
144 /* set timeout value to 10min */
145 timeout.tv_sec = 10 * 60;
150 EM_DEBUG_EXCEPTION(">>>>>>>>>>>IDLING>>>>>>>>>>>>");
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;
162 select_result = select(maxfd, &readfds, NULL, NULL, &timeout);
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 ");
174 break; /* break for now - check if it is possible to get data on two sockets - shasikala.p */
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];
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;
192 sprintf(tag, "%08lx", 0xffffffff & (((MAILSTREAM *)temp->mail_stream)->gensym++));
193 sprintf(cmd, "%s DONE\015\012", tag);
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);
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);
211 /* kill idle thread */
212 emcore_kill_imap_idle_thread(&err);
218 might happen that a socket descriptor passed to select got closed
219 check which got closed and make hold_connection 0
221 EM_DEBUG_EXCEPTION(">>>>>> socket descriptor error : No Data ");
230 EM_DEBUG_LOG(">>>> emcore_imap_idle_loop_start 17 ");
237 EM_DEBUG_FUNC_END("ret [%d]", ret);
241 void* emcore_imap_idle_run(void* thread_user_data)
243 EM_DEBUG_FUNC_BEGIN("thread_user_data [%p]", thread_user_data);
244 char *mailbox_list[3];
246 int err = EMAIL_ERROR_NONE;
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;
257 if ( !emnetwork_check_network_status(&err)) {
258 EM_DEBUG_EXCEPTION("emnetwork_check_network_status failed [%d]", err);
262 if (!emstorage_open(&err)) {
263 EM_DEBUG_EXCEPTION("emstorage_open falied [%d]", err);
267 if (!emcore_get_empty_session(&session))
268 EM_DEBUG_EXCEPTION("emcore_get_empty_session failed...");
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 */
274 /* For testing - mailbox_num and mailbox_list are hardcoded here */
276 mailbox_list[0] = strdup("INBOX");
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);
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);
296 mail_box_list = curr_mailbox;
297 prev_mailbox = curr_mailbox;
302 prev_mailbox->next = curr_mailbox;
303 prev_mailbox = curr_mailbox;
308 if (local_mailbox != NULL)
309 emstorage_free_mailbox(&local_mailbox, 1, NULL);
312 emcore_clear_session(session);
313 emcore_imap_idle_loop_start(mail_box_list, num, NULL);
317 if (!emstorage_close(&err)) {
318 EM_DEBUG_EXCEPTION("emstorage_close falied [%d]", err);
325 int emcore_imap_idle_insert_sync_event(email_mailbox_t *mailbox, int *err_code)
327 EM_DEBUG_FUNC_BEGIN();
330 int err = EMAIL_ERROR_NONE;
333 if (!mailbox || mailbox->account_id <= 0) {
334 EM_DEBUG_EXCEPTION("EMAIL_ERROR_INVALID_PARAM");
335 err = EMAIL_ERROR_INVALID_PARAM;
338 email_event_t event_data = { 0 };
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;
345 if (!emcore_insert_event(&event_data, &handle, &err)) {
346 EM_DEBUG_EXCEPTION("emcore_insert_event failed [%d]", err);
356 EM_DEBUG_FUNC_END("ret [%d]", ret);
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)
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, };
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;
376 /* NULL param check */
378 EM_DEBUG_EXCEPTION("EMAIL_ERROR_INVALID_PARAM");
379 err = EMAIL_ERROR_INVALID_PARAM;
383 ref_account = emcore_get_account_reference(mailbox->account_id);
385 EM_DEBUG_EXCEPTION("emcore_get_account_reference failed. account_id[%d]", mailbox->account_id);
386 err = EMAIL_ERROR_INVALID_ACCOUNT;
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);
396 imap_local = ((MAILSTREAM *)mail_stream)->local;
397 net_stream = imap_local->netstream;
399 /* check if ssl option is enabled on this account - shasikala.p */
401 if (ref_account->incoming_server_secure_connection){
402 SSLSTREAM *ssl_stream = net_stream->stream;
403 tcp_stream = ssl_stream->tcpstream;
406 tcp_stream = net_stream->stream;
409 socket_id = ((TCPSTREAM *)tcp_stream)->tcpsi;
411 sprintf(tag, "%08lx", 0xffffffff & (((MAILSTREAM *)mail_stream)->gensym++));
412 sprintf(cmd, "%s IDLE\015\012", tag);
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;
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)) {
429 else if (!strncmp(p, "*", 1)) {
443 /* IMAP IDLE - SUCCESS */
444 mailbox->mail_stream = mail_stream;
445 mailbox->hold_connection = socket_id; /* holds connection continuously on the given socket_id */
447 else if (mail_stream)
448 emcore_close_mailbox(mailbox->account_id, mail_stream);
451 emcore_free_account(ref_account);
452 EM_SAFE_FREE(ref_account);
458 EM_DEBUG_FUNC_END("ret [%d]", ret);
462 static int emcore_imap_idle_parse_response_stream(email_mailbox_t *mailbox, int *err_code)
464 EM_DEBUG_FUNC_BEGIN("mailbox [%p], err_code [%p]", mailbox, err_code);
465 int err = EMAIL_ERROR_NONE;
468 IMAPLOCAL *imap_local = NULL;
470 if (!mailbox || !mailbox->mail_stream) {
471 EM_DEBUG_EXCEPTION("EMAIL_ERROR_INVALID_PARAM");
472 err = EMAIL_ERROR_INVALID_PARAM;
476 imap_local = ((MAILSTREAM *)mailbox->mail_stream)->local;
479 EM_DEBUG_EXCEPTION("imap_local is NULL");
480 err = EMAIL_ERROR_INVALID_PARAM;
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");
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);
500 else if (p && (!strncmp(p, "+", 1))) {
501 /* Bad response from server */
502 EM_DEBUG_LOG("p is [%s]", p);
507 EM_DEBUG_LOG("In else part");
519 EM_DEBUG_FUNC_END("ret [%d]", ret);
522 #endif /* __FEATURE_IMAP_IDLE__ */