ca8ee04321744e1b978d2e114201e5b237baf57c
[platform/core/telephony/tel-plugin-imc.git] / src / imc_phonebook.c
1 /*
2  * tel-plugin-imc
3  *
4  * Copyright (c) 2013 Samsung Electronics Co. Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include <glib.h>
24
25 #include <tcore.h>
26 #include <server.h>
27 #include <plugin.h>
28 #include <core_object.h>
29 #include <hal.h>
30 #include <queue.h>
31 #include <storage.h>
32 #include <at.h>
33
34 #include <co_phonebook.h>
35 #include <co_sim.h>
36
37 #include "imc_phonebook.h"
38 #include "imc_common.h"
39
40 typedef struct {
41         GSList *used_index_fdn;
42         gboolean used_index_fdn_valid;
43
44         GSList *used_index_adn;
45         gboolean used_index_adn_valid;
46
47         GSList *used_index_sdn;
48         gboolean used_index_sdn_valid;
49
50         GSList *used_index_usim;
51         gboolean used_index_usim_valid;
52 } PrivateInfo;
53
54 static TelPbResult
55 __imc_phonebook_convert_cme_error_tel_phonebook_result(const TcoreAtResponse *at_resp)
56 {
57         TelPbResult result = TEL_PB_RESULT_FAILURE;
58         GSList *tokens = NULL;
59         const gchar *line;
60
61         dbg("Entry");
62
63         if (!at_resp || !at_resp->lines) {
64                 err("Invalid response data");
65                 return result;
66         }
67
68         line = (const gchar *)at_resp->lines->data;
69         tokens = tcore_at_tok_new(line);
70         if (g_slist_length(tokens) > 0) {
71                 gchar *resp_str;
72                 gint cme_err;
73
74                 resp_str = g_slist_nth_data(tokens, 0);
75                 if (!resp_str) {
76                         err("Invalid CME Error data");
77                         tcore_at_tok_free(tokens);
78                         return result;
79                 }
80                 cme_err = atoi(resp_str);
81                 dbg("CME Error: [%d]", cme_err);
82
83                 switch (cme_err) {
84                 case 3:
85                         result = TEL_PB_RESULT_OPERATION_NOT_PERMITTED;
86                 break;
87
88                 case 4:
89                         result = TEL_PB_RESULT_OPERATION_NOT_SUPPORTED;
90                 break;
91
92                 case 17:
93                         result = TEL_PB_RESULT_PIN2_REQUIRED;
94                 break;
95
96                 case 18:
97                         result = TEL_PB_RESULT_PUK2_REQUIRED;
98                 break;
99
100                 case 20:
101                         result = TEL_PB_RESULT_MEMORY_FAILURE;
102                 break;
103
104                 case 21:
105                         result = TEL_PB_RESULT_INVALID_INDEX;
106                 break;
107
108                 case 50:
109                         result =  TEL_PB_RESULT_INVALID_PARAMETER;
110                 break;
111
112                 case 100:
113                         result =  TEL_PB_RESULT_UNKNOWN_FAILURE;
114                 break;
115
116                 default:
117                         result = TEL_PB_RESULT_FAILURE;
118                 }
119         }
120         tcore_at_tok_free(tokens);
121
122         return result;
123 }
124
125 static gboolean __imc_phonebook_get_sim_type(CoreObject *co_pb,
126                 TelSimCardType *sim_type)
127 {
128         TcorePlugin *plugin;
129         CoreObject *co_sim;
130         tcore_check_return_value_assert(co_pb != NULL, FALSE);
131         tcore_check_return_value_assert(sim_type != NULL, FALSE);
132
133         plugin = tcore_object_ref_plugin(co_pb);
134         co_sim = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_SIM);
135         return tcore_sim_get_type(co_sim, sim_type);
136 }
137
138 static gboolean __imc_phonebook_get_pb_type_str(TelPbType pb_type,
139                 gchar **req_type_str)
140 {
141         tcore_check_return_value_assert(req_type_str != NULL, FALSE);
142
143         switch (pb_type) {
144         case TEL_PB_FDN:
145                 *req_type_str = g_strdup("FD");
146                 break;
147         case TEL_PB_ADN:
148         case TEL_PB_USIM:
149                 *req_type_str = g_strdup("SM");
150                 break;
151         case TEL_PB_SDN:
152                 *req_type_str = g_strdup("SN");
153                 break;
154         }
155
156         return TRUE;
157 }
158
159 static gboolean __imc_phonebook_check_and_select_type(CoreObject *co,
160                 TelPbType req_type, gchar **set_pb_cmd)
161 {
162         TelPbList *support_list;
163         TelPbType current_type;
164
165         /* Check whether pb_type is supported or not */
166         tcore_phonebook_get_support_list(co, &support_list);
167         if ((req_type == TEL_PB_FDN && support_list->fdn == FALSE)
168                         || (req_type == TEL_PB_ADN && support_list->adn == FALSE)
169                         || (req_type == TEL_PB_SDN && support_list->sdn == FALSE)
170                         || (req_type == TEL_PB_USIM && support_list->usim == FALSE)) {
171                 err("Not supported pb_type");
172                 g_free(support_list);
173                 return FALSE;
174         }
175         g_free(support_list);
176
177         /* Check Current type & Request type */
178         tcore_phonebook_get_selected_type(co, &current_type);
179         if (current_type != req_type) {
180                 gchar *req_type_str = NULL;
181                 __imc_phonebook_get_pb_type_str(req_type, &req_type_str);
182                 dbg("Add AT-Command to change [%s] Type", req_type_str);
183                 /* Select Phonebook type */
184                 *set_pb_cmd = g_strdup_printf("AT+CPBS=\"%s\";", req_type_str);
185         } else {
186                 *set_pb_cmd = g_strdup_printf("AT");
187         }
188
189         return TRUE;
190 }
191
192 static gboolean __imc_phonebook_get_index_list_by_type(CoreObject *co,
193                 TelPbType pb_type, GSList **list)
194 {
195         PrivateInfo *private_info = tcore_object_ref_user_data(co);
196         tcore_check_return_value_assert(private_info != NULL, FALSE);
197
198         switch (pb_type) {
199         case TEL_PB_FDN:
200                 if (private_info->used_index_fdn_valid != TRUE)
201                         return FALSE;
202                 *list = private_info->used_index_fdn;
203                 break;
204         case TEL_PB_ADN:
205                 if (private_info->used_index_adn_valid != TRUE)
206                         return FALSE;
207                 *list = private_info->used_index_adn;
208                 break;
209         case TEL_PB_SDN:
210                 if (private_info->used_index_sdn_valid != TRUE)
211                         return FALSE;
212                 *list = private_info->used_index_sdn;
213                 break;
214         case TEL_PB_USIM:
215                 if (private_info->used_index_usim_valid != TRUE)
216                         return FALSE;
217                 *list = private_info->used_index_usim;
218                 break;
219         }
220
221         return TRUE;
222 }
223
224 static void __imc_phonebook_check_used_index(CoreObject *co,
225                 TelPbType pb_type, guint req_index, guint *used_index)
226 {
227         GSList *list = NULL;
228
229         /* Get used_index list by req_type */
230         if (__imc_phonebook_get_index_list_by_type(co, pb_type, &list) != TRUE) {
231                 err("used_index list is NOT valid");
232                 *used_index = req_index;
233                 return;
234         }
235
236         /* Use first used_index in case req_index is not used */
237         *used_index = (guint)g_slist_nth_data(list, 0);
238         while (list) {
239                 if ((guint)list->data == req_index) {
240                         /* req_index is equal to one of used_index */
241                         *used_index = req_index;
242                         return;
243                 }
244                 list = g_slist_next(list);
245         }
246 }
247
248 static gint __imc_phonebook_compare_index(gconstpointer a, gconstpointer b)
249 {
250         guint index1 = (guint)a;
251         guint index2 = (guint)b;
252
253         return index1 - index2;
254 }
255
256 static void on_response_imc_phonebook_get_used_index(TcorePending *p,
257                 guint data_len, const void *data, void *user_data)
258 {
259         const TcoreAtResponse *at_resp = data;
260         ImcRespCbData *resp_cb_data = user_data;
261         CoreObject *co = tcore_pending_ref_core_object(p);
262         tcore_check_return_assert(at_resp != NULL);
263         tcore_check_return_assert(resp_cb_data != NULL);
264
265         dbg("Entry");
266
267         if (at_resp->success != TRUE) {
268                 err("Response NOK");
269                 return;
270         }
271
272         dbg("Response OK");
273
274         if (at_resp->lines == NULL) {
275                 err("at_resp->lines is NULL");
276         } else {
277                 GSList *lines = at_resp->lines;
278                 TelPbType *req_type;
279                 GSList **list = NULL;
280                 PrivateInfo *private_info = tcore_object_ref_user_data(co);
281                 tcore_check_return_assert(private_info != NULL);
282
283                 /* Select used_index_list by req_type */
284                 req_type = (TelPbType *)IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
285                 switch (*req_type) {
286                 case TEL_PB_FDN:
287                         list = &private_info->used_index_fdn;
288                         private_info->used_index_fdn_valid = TRUE;
289                         break;
290                 case TEL_PB_ADN:
291                         list = &private_info->used_index_adn;
292                         private_info->used_index_adn_valid = TRUE;
293                         break;
294                 case TEL_PB_SDN:
295                         list = &private_info->used_index_sdn;
296                         private_info->used_index_sdn_valid = TRUE;
297                         break;
298                 case TEL_PB_USIM:
299                         list = &private_info->used_index_usim;
300                         private_info->used_index_usim_valid = TRUE;
301                         break;
302                 }
303
304                 while (lines) {
305                         const gchar *line = lines->data;
306                         GSList *tokens = NULL;
307                         gchar *temp;
308
309                         dbg("Line: [%s]", line);
310
311                         tokens = tcore_at_tok_new(line);
312                         if (tokens == NULL) {
313                                 err("tokens is NULL");
314                                 return;
315                         }
316
317                         /* Get only used_index */
318                         temp = g_slist_nth_data(tokens, 0);
319                         if (temp) {
320                                 /* Insert used_index in PrivateInfo sorted in ascending */
321                                 *list = g_slist_insert_sorted(*list, (gpointer)atoi(temp),
322                                         __imc_phonebook_compare_index);
323                         }
324                         tcore_at_tok_free(tokens);
325
326                         /* Get next lines */
327                         lines = g_slist_next(lines);
328                 }
329                 dbg("pb_type: [%d], used_index Length: [%d]",
330                         *req_type, g_slist_length(*list));
331         }
332 }
333
334 static void __imc_phonebook_get_used_index(CoreObject *co, TelPbType pb_type, guint max_index)
335 {
336         gchar *at_cmd;
337         ImcRespCbData *resp_cb_data;
338         TelReturn ret = TEL_RETURN_FAILURE;
339
340         dbg("Entry");
341
342         /* AT-Command */
343         at_cmd = g_strdup_printf("AT+CPBR=1,%d", max_index);
344
345         /* Response callback data */
346         resp_cb_data = imc_create_resp_cb_data(NULL, NULL,
347                 (void *)&pb_type, sizeof(TelPbType));
348
349         /* Send Request to Modem */
350         ret = tcore_at_prepare_and_send_request(co,
351                 at_cmd, "+CPBR",
352                 TCORE_AT_COMMAND_TYPE_MULTILINE,
353                 NULL,
354                 on_response_imc_phonebook_get_used_index, resp_cb_data,
355                 on_send_imc_request, NULL);
356         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Get Used Index");
357
358         /* Free resources */
359         g_free(at_cmd);
360 }
361
362 static void on_response_imc_phonebook_get_support_list(TcorePending *p,
363                 guint data_len, const void *data, void *user_data)
364 {
365         const TcoreAtResponse *at_resp = data;
366         CoreObject *co = tcore_pending_ref_core_object(p);
367         TelPbInitInfo init_info = {0, };
368         tcore_check_return_assert(at_resp != NULL);
369
370         dbg("Entry");
371
372         if (at_resp->success != TRUE) {
373                 err("Response NOK");
374                 return;
375         }
376
377         dbg("Response OK");
378
379         if (at_resp->lines == NULL) {
380                 err("at_resp->lines is NULL");
381                 return;
382         } else {
383                 const gchar *line = (const gchar *)at_resp->lines->data;
384                 GSList *tokens = NULL;
385                 gchar *pb_type_list;
386                 gchar *pb_type;
387
388                 dbg("Line: [%s]", line);
389
390                 tokens = tcore_at_tok_new(line);
391                 if (tokens == NULL) {
392                         err("tokens is NULL");
393                         return;
394                 }
395
396                 pb_type_list = g_slist_nth_data(tokens, 0);
397                 pb_type = strtok(pb_type_list, "(,)");
398                 while (pb_type) {
399                         pb_type = tcore_at_tok_extract(pb_type);
400                         if (g_strcmp0(pb_type, "FD") == 0) {
401                                 init_info.pb_list.fdn = TRUE;
402                         } else if (g_strcmp0(pb_type, "SN") == 0) {
403                                 init_info.pb_list.sdn = TRUE;
404                         } else if (g_strcmp0(pb_type, "SM") == 0) {
405                                 TelSimCardType sim_type;
406                                 __imc_phonebook_get_sim_type(co, &sim_type);
407                                 if (sim_type == TEL_SIM_CARD_TYPE_USIM)
408                                         init_info.pb_list.usim = TRUE;
409                                 else
410                                         init_info.pb_list.adn = TRUE;
411                         }
412                         g_free(pb_type);
413                         /* Get Next pb_type */
414                         pb_type = strtok(NULL, "(,)");
415                 }
416                 tcore_at_tok_free(tokens);
417         }
418
419         dbg("FDN: [%s], ADN: [%s], SDN: [%s], USIM: [%s]",
420                 init_info.pb_list.fdn ? "TRUE" : "FALSE",
421                 init_info.pb_list.adn ? "TRUE" : "FALSE",
422                 init_info.pb_list.sdn ? "TRUE" : "FALSE",
423                 init_info.pb_list.usim ? "TRUE" : "FALSE");
424
425         init_info.init_status = TRUE;
426         tcore_phonebook_set_support_list(co, &init_info.pb_list);
427         tcore_phonebook_set_status(co, init_info.init_status);
428
429         /* Send Notification */
430         tcore_object_send_notification(co,
431                 TCORE_NOTIFICATION_PHONEBOOK_STATUS,
432                 sizeof(TelPbInitInfo), &init_info);
433 }
434
435 /*
436  * Operation - get_support_list
437  *
438  * Request -
439  * AT-Command: AT+CPBS=?
440  *
441  * Response -
442  * Success: (Single line)
443  *      (list of supported <storage>s)
444  *      OK
445  * Failure:
446  *      +CME ERROR: <error>
447  */
448 static void __imc_phonebook_get_support_list(CoreObject *co)
449 {
450         TelReturn ret;
451
452         dbg("Entry");
453
454         /* Send Request to Modem */
455         ret = tcore_at_prepare_and_send_request(co,
456                 "AT+CPBS=?", "+CPBS",
457                 TCORE_AT_COMMAND_TYPE_SINGLELINE,
458                 NULL,
459                 on_response_imc_phonebook_get_support_list, NULL,
460                 on_send_imc_request, NULL);
461         IMC_CHECK_REQUEST_RET(ret, NULL, "Get Support List");
462 }
463
464 static gboolean on_notification_imc_phonebook_status(CoreObject *co,
465                 const void *event_info, void *user_data)
466 {
467         dbg("Phonebook Init Completed");
468
469         /* Get Supported list */
470         __imc_phonebook_get_support_list(co);
471
472         return TRUE;
473 }
474
475 static void on_response_imc_phonebook_get_info(TcorePending *p,
476                 guint data_len, const void *data, void *user_data)
477 {
478         const TcoreAtResponse *at_resp = data;
479         ImcRespCbData *resp_cb_data = user_data;
480         CoreObject *co = tcore_pending_ref_core_object(p);
481         TelPbResult result = TEL_PB_RESULT_FAILURE;
482         TelPbInfo pb_info = {0, };
483         tcore_check_return_assert(at_resp != NULL);
484         tcore_check_return_assert(resp_cb_data != NULL);
485
486         dbg("Entry");
487
488         if (at_resp->success != TRUE) {
489                 err("Response NOK");
490                 result = __imc_phonebook_convert_cme_error_tel_phonebook_result(at_resp);
491                 goto out;
492         }
493
494         dbg("Response OK");
495
496         if (at_resp->lines == NULL) {
497                 err("at_resp->lines is NULL");
498         } else {
499                 GSList *lines = at_resp->lines;
500                 const gchar *line;
501                 GSList *tokens = NULL;
502                 gchar *temp;
503                 gint used = 0, total = 0;
504                 gint nlen = 0, tlen = 0;
505                 TelPbType *req_type;
506                 PrivateInfo *private_info;
507
508                 /* +CPBS: <storage>[,<used>][,total] */
509                 line = g_slist_nth_data(lines, 0);
510                 dbg("First Line: [%s]", line);
511                 tokens = tcore_at_tok_new(line);
512                 if (tokens == NULL) {
513                         err("tokens is NULL");
514                         goto out;
515                 }
516
517                 /* Get used_count */
518                 temp = g_slist_nth_data(tokens, 1);
519                 if (temp)
520                         used = atoi(temp);
521                 /* Get total_count */
522                 temp = g_slist_nth_data(tokens, 2);
523                 if (temp)
524                         total = atoi(temp);
525
526                 tcore_at_tok_free(tokens);
527
528                 /* +CPBF: [<nlength>],[<tlength>],[<glength>],[<slength>],[<elength>] */
529                 line = g_slist_nth_data(lines, 1);
530                 dbg("Second Line: [%s]", line);
531                 tokens = tcore_at_tok_new(line);
532                 if (tokens == NULL) {
533                         err("tokens is NULL");
534                         goto out;
535                 }
536
537                 /* Get number Length */
538                 temp = g_slist_nth_data(tokens, 0);
539                 if (temp)
540                         nlen = atoi(temp);
541                 /* Get text Length */
542                 temp = g_slist_nth_data(tokens, 1);
543                 if (temp)
544                         tlen = atoi(temp);
545
546                 /* Set Response Data */
547                 req_type = (TelPbType *)IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
548                 pb_info.pb_type = *req_type;
549                 if (*req_type == TEL_PB_USIM) {
550                         pb_info.info_u.usim.max_count = total;
551                         pb_info.info_u.usim.used_count = used;
552                         pb_info.info_u.usim.max_num_len = nlen;
553                         pb_info.info_u.usim.max_text_len = tlen;
554                         /* Get group name Length */
555                         temp = g_slist_nth_data(tokens, 2);
556                         if (temp)
557                                 pb_info.info_u.usim.max_gas_len = atoi(temp);
558                         /* Get second name Length */
559                         temp = g_slist_nth_data(tokens, 3);
560                         if (temp)
561                                 pb_info.info_u.usim.max_sne_len = atoi(temp);
562                         /* Get email Length */
563                         temp = g_slist_nth_data(tokens, 4);
564                         if (temp)
565                                 pb_info.info_u.usim.max_email_len = atoi(temp);
566                 } else {
567                         pb_info.info_u.sim.max_count = total;
568                         pb_info.info_u.sim.used_count = used;
569                         pb_info.info_u.sim.max_num_len = nlen;
570                         pb_info.info_u.sim.max_text_len = tlen;
571                 }
572
573                 /* Set Request type in PrivateObject */
574                 tcore_phonebook_set_selected_type(co, *req_type);
575                 result = TEL_PB_RESULT_SUCCESS;
576                 tcore_at_tok_free(tokens);
577
578                 /* If don't have valid used_index, get used_index by req_type */
579                 private_info = tcore_object_ref_user_data(co);
580                 if ((*req_type == TEL_PB_FDN && private_info->used_index_fdn_valid == FALSE)
581                                 || (*req_type == TEL_PB_ADN && private_info->used_index_adn_valid == FALSE)
582                                 || (*req_type == TEL_PB_SDN && private_info->used_index_sdn_valid == FALSE)
583                                 || (*req_type == TEL_PB_USIM && private_info->used_index_usim_valid == FALSE))
584                         __imc_phonebook_get_used_index(co, *req_type, total);
585         }
586
587 out:
588         /* Invoke callback */
589         if (resp_cb_data->cb)
590                 resp_cb_data->cb(co, (gint)result, &pb_info, resp_cb_data->cb_data);
591
592         /* Free callback data */
593         imc_destroy_resp_cb_data(resp_cb_data);
594 }
595
596 static void on_response_imc_phonebook_read_record(TcorePending *p,
597                 guint data_len, const void *data, void *user_data)
598 {
599         const TcoreAtResponse *at_resp = data;
600         ImcRespCbData *resp_cb_data = user_data;
601         CoreObject *co = tcore_pending_ref_core_object(p);
602         TelPbResult result = TEL_PB_RESULT_FAILURE;
603         GSList *tokens = NULL;
604         gchar *index = NULL, *number = NULL, *name = NULL;
605         TelPbReadRecord read_record = {0, };
606         tcore_check_return_assert(at_resp != NULL);
607         tcore_check_return_assert(resp_cb_data != NULL);
608
609         dbg("Entry");
610
611         if (at_resp->success != TRUE) {
612                 err("Response NOK");
613                 result = __imc_phonebook_convert_cme_error_tel_phonebook_result(at_resp);
614                 goto out;
615         }
616
617         dbg("Response OK");
618
619         if (at_resp->lines == NULL) {
620                 err("at_resp->lines is NULL");
621         } else {
622                 const gchar *line = (const gchar *)at_resp->lines->data;
623                 TelPbType *req_type;
624                 GSList *list = NULL;
625
626                 dbg("Line: [%s]", line);
627
628                 tokens = tcore_at_tok_new(line);
629                 if (tokens == NULL) {
630                         err("tokens is NULL");
631                         goto out;
632                 }
633
634                 /* Get index */
635                 index = g_slist_nth_data(tokens, 0);
636                 if (index == NULL) {
637                         err("No index");
638                         goto out;
639                 }
640
641                 /* Get number */
642                 number = g_slist_nth_data(tokens, 1);
643                 if (number) {
644                         number = tcore_at_tok_extract(number);
645                 } else {
646                         err("No number");
647                         goto out;
648                 }
649
650                 /* Get name */
651                 name = g_slist_nth_data(tokens, 3);
652                 if (name) {
653                         name = tcore_at_tok_extract(name);
654                 } else {
655                         err("No name");
656                         goto out;
657                 }
658
659                 /* Set Request type in PrivateObject */
660                 req_type = (TelPbType *)IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
661                 tcore_phonebook_set_selected_type(co, *req_type);
662
663                 /* Set Response Data */
664                 read_record.index = atoi(index);
665                 read_record.pb_type = *req_type;
666
667                 /* Get used_index list by req_type */
668                 if (__imc_phonebook_get_index_list_by_type(co, *req_type, &list) == TRUE) {
669                         while (list) {
670                                 if ((guint)list->data == read_record.index) {
671                                         if ((list = g_slist_next(list)) != NULL) {
672                                                 /* If exist, set next_index */
673                                                 read_record.next_index = (guint)list->data;
674                                                 dbg("next_index is [%u]", read_record.next_index);
675                                         } else {
676                                                 /* read_record.index is the end of used_index */
677                                                 read_record.next_index = -1;
678                                                 dbg("End of used_index");
679                                         }
680                                         break;
681                                 }
682                                 list = g_slist_next(list);
683                         }
684                 } else {
685                         /* No PrivateInfo */
686                         read_record.next_index = 0;
687                 }
688
689                 if (*req_type == TEL_PB_USIM) {
690                         gchar *hidden, *group, *anr, *sne, *email;
691
692                         /* Get Name and Number */
693                         g_strlcpy(read_record.rec_u.usim.name, name, TEL_PB_TEXT_MAX_LEN + 1);
694                         g_strlcpy(read_record.rec_u.usim.number, number, TEL_PB_NUMBER_MAX_LEN + 1);
695
696                         /* Get Hidden */
697                         hidden = g_slist_nth_data(tokens, 4);
698                         if (hidden) {
699                                 read_record.rec_u.usim.hidden = atoi(hidden);
700                         }
701
702                         /* Get Group name */
703                         group = g_slist_nth_data(tokens, 5);
704                         if (group) {
705                                 group = tcore_at_tok_extract(group);
706                                 g_strlcpy(read_record.rec_u.usim.grp_name, group, TEL_PB_TEXT_MAX_LEN + 1);
707                                 g_free(group);
708                         }
709
710                         /* Get ANR */
711                         anr = g_slist_nth_data(tokens, 6);
712                         if (anr) {
713                                 anr = tcore_at_tok_extract(anr);
714                                 if (strlen(anr)) {
715                                         g_strlcpy(read_record.rec_u.usim.anr[0].number,
716                                                 anr, TEL_PB_NUMBER_MAX_LEN + 1);
717                                         read_record.rec_u.usim.anr_count = 1;
718                                 }
719                                 g_free(anr);
720                         }
721
722                         /* Get SNE */
723                         sne = g_slist_nth_data(tokens, 8);
724                         if (sne) {
725                                 sne = tcore_at_tok_extract(sne);
726                                 g_strlcpy(read_record.rec_u.usim.sne, sne, TEL_PB_TEXT_MAX_LEN + 1);
727                                 g_free(sne);
728                         }
729
730                         /* Get email */
731                         email = g_slist_nth_data(tokens, 9);
732                         if (email) {
733                                 email = tcore_at_tok_extract(email);
734                                 if (strlen(email)) {
735                                         g_strlcpy(read_record.rec_u.usim.email[0], email, TEL_PB_TEXT_MAX_LEN + 1);
736                                         read_record.rec_u.usim.email_count = 1;
737                                 }
738                                 g_free(email);
739                         }
740                 }
741                 else {
742                         /* Get Name and Number */
743                         g_strlcpy(read_record.rec_u.sim.name, name, TEL_PB_TEXT_MAX_LEN + 1);
744                         g_strlcpy(read_record.rec_u.sim.number, number, TEL_PB_NUMBER_MAX_LEN + 1);
745                 }
746
747                 result = TEL_PB_RESULT_SUCCESS;
748         }
749
750 out:
751         /* Invoke callback */
752         if (resp_cb_data->cb)
753                 resp_cb_data->cb(co, (gint)result, &read_record, resp_cb_data->cb_data);
754
755         /* Free callback data */
756         imc_destroy_resp_cb_data(resp_cb_data);
757
758         /* Free resources */
759         tcore_at_tok_free(tokens);
760         g_free(number);
761         g_free(name);
762 }
763
764 static void on_response_imc_phonebook_update_record(TcorePending *p,
765                 guint data_len, const void *data, void *user_data)
766 {
767         const TcoreAtResponse *at_resp = data;
768         ImcRespCbData *resp_cb_data = user_data;
769         CoreObject *co = tcore_pending_ref_core_object(p);
770         TelPbUpdateRecord *req_data;
771         TelPbResult result = TEL_PB_RESULT_FAILURE;
772         GSList *list = NULL;
773         tcore_check_return_assert(at_resp != NULL);
774         tcore_check_return_assert(resp_cb_data != NULL);
775
776         dbg("Entry");
777
778         if (at_resp->success != TRUE) {
779                 err("Response NOK");
780                 result = __imc_phonebook_convert_cme_error_tel_phonebook_result(at_resp);
781                 goto out;
782         }
783
784         dbg("Response OK");
785         result = TEL_PB_RESULT_SUCCESS;
786
787         /* Set Request type in PrivateObject */
788         req_data = (TelPbUpdateRecord *)IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
789         tcore_phonebook_set_selected_type(co, req_data->pb_type);
790
791         /* Get used_index list by req_type */
792         if (__imc_phonebook_get_index_list_by_type(co,
793                         req_data->pb_type, &list) != TRUE) {
794                 err("used_index list is NOT valid");
795         } else {
796                 list = g_slist_insert_sorted(list, (gpointer)req_data->index,
797                         __imc_phonebook_compare_index);
798                 dbg("list: [0x%x]", list);
799         }
800
801 out:
802         /* Invoke callback */
803         if (resp_cb_data->cb)
804                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
805
806         /* Free callback data */
807         imc_destroy_resp_cb_data(resp_cb_data);
808 }
809
810 static void on_response_imc_phonebook_delete_record(TcorePending *p,
811                 guint data_len, const void *data, void *user_data)
812 {
813         const TcoreAtResponse *at_resp = data;
814         ImcRespCbData *resp_cb_data = user_data;
815         CoreObject *co = tcore_pending_ref_core_object(p);
816         TelPbRecordInfo *req_data;
817         GSList *list = NULL;
818         TelPbResult result = TEL_PB_RESULT_FAILURE;
819         tcore_check_return_assert(at_resp != NULL);
820         tcore_check_return_assert(resp_cb_data != NULL);
821
822         dbg("Entry");
823
824         if (at_resp->success != TRUE) {
825                 err("Response NOK");
826                 result = __imc_phonebook_convert_cme_error_tel_phonebook_result(at_resp);
827                 goto out;
828         }
829
830         dbg("Response OK");
831         result = TEL_PB_RESULT_SUCCESS;
832
833         /* Set Request type in PrivateObject */
834         req_data = (TelPbRecordInfo *)IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
835         tcore_phonebook_set_selected_type(co, req_data->pb_type);
836
837         /* Get used_index list by req_type */
838         if (__imc_phonebook_get_index_list_by_type(co,
839                         req_data->pb_type, &list) != TRUE) {
840                 err("used_index list is NOT valid");
841         } else {
842                 list = g_slist_remove(list, (gconstpointer)req_data->index);
843                 dbg("Remove index: [%u], list: [0x%x]", req_data->index, list);
844         }
845
846 out:
847         /* Invoke callback */
848         if (resp_cb_data->cb)
849                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
850
851         /* Free callback data */
852         imc_destroy_resp_cb_data(resp_cb_data);
853 }
854
855 /*
856  * Operation - get_info
857  *
858  * Request -
859  * AT-Command: AT+CPBS?;+CPBF=?
860  *
861  * Response -
862  * Success: (Multi line)
863  *      +CPBS: <storage>[,<used>][,total]
864  *      +CPBF: [<nlength>],[<tlength>],[<glength>],[<slength>],[<elength>]
865  *      OK
866  * where,
867  * <nlength> Maximum length of field <number>
868  * <tlength> Maximum length of field <text>
869  * <glength> Maximum length of field <group>
870  * <slength> Maximum length of field <secondtext>
871  * <elength> Maximum length of field <email>
872  *
873  * Failure:
874  *      +CME ERROR: <error>
875  */
876 static TelReturn imc_phonebook_get_info(CoreObject *co,
877                 const TelPbType pb_type,
878                 TcoreObjectResponseCallback cb, void *cb_data)
879 {
880         gchar *at_cmd;
881         gchar *set_pb_cmd;
882         ImcRespCbData *resp_cb_data;
883         TelReturn ret = TEL_RETURN_FAILURE;
884
885         dbg("Entry");
886
887         /* Check whether pb_type is supported or not, and Select pb_type */
888         if (__imc_phonebook_check_and_select_type(co, pb_type, &set_pb_cmd) != TRUE) {
889                 return ret;
890         }
891
892         /* AT-Command */
893         at_cmd = g_strdup_printf("%s+CPBS?;+CPBF=?", set_pb_cmd);
894
895         /* Response callback data */
896         resp_cb_data = imc_create_resp_cb_data(cb, cb_data,
897                 (void *)&pb_type, sizeof(TelPbType));
898
899         /* Send Request to Modem */
900         ret = tcore_at_prepare_and_send_request(co,
901                 at_cmd, "+CPB",
902                 TCORE_AT_COMMAND_TYPE_MULTILINE,
903                 NULL,
904                 on_response_imc_phonebook_get_info, resp_cb_data,
905                 on_send_imc_request, NULL);
906         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Get Info");
907
908         /* Free resources */
909         g_free(at_cmd);
910         g_free(set_pb_cmd);
911
912         return ret;
913 }
914
915 /*
916  * Operation - read_record
917  *
918  * Request -
919  * AT-Command: AT+CPBR=<index>
920  * where,
921  * <index>
922  * 1    Integer type values in range of location numbers of phonebook memory
923  * ...
924  *
925  * Response -
926  * Success: (Single line);
927  *      +CPBR: <index>,<number>,<type>,<text>[,<hidden>][,<group>]
928  *      [,<adnumber>][,<adtype>][,<secondtext>][,<email>]]
929  *      OK
930  * where,
931  * <number> String type phone number of format <type>
932  * <type> Type of address octet in integer format
933  * <text> String type field of maximum length <tlength>
934  * <hidden> Indicates if the entry is hidden or not â€“ only available,
935  *              if a UICC with an active USIM application is present
936  * 0    Phonebook entry not hidden
937  * 1    Phonebook entry hidden
938  * <group> String type field of maximum length <glength>
939  * <adnumber> String type phone number of format <adtype>
940  * <adtype> Type of address octet in integer format
941  * <secondtext> String type field of maximum length <slength>
942  * <email> String type field of maximum length <elength>
943  *
944  * Failure:
945  *      +CME ERROR: <error>
946  */
947 static TelReturn imc_phonebook_read_record(CoreObject *co,
948                 const TelPbRecordInfo *record,
949                 TcoreObjectResponseCallback cb, void *cb_data)
950 {
951         gchar *at_cmd;
952         gchar *set_pb_cmd;
953         ImcRespCbData *resp_cb_data;
954         guint used_index = 0;
955         TelReturn ret = TEL_RETURN_FAILURE;
956
957         dbg("Entry");
958
959         /* Check whether pb_type is supported or not, and Select pb_type */
960         if (__imc_phonebook_check_and_select_type(co, record->pb_type, &set_pb_cmd) != TRUE) {
961                 return ret;
962         }
963
964         /* Check whether index is used or not */
965         __imc_phonebook_check_used_index(co, record->pb_type, record->index, &used_index);
966
967         /* AT-Command */
968         at_cmd = g_strdup_printf("%s+CPBR=%u", set_pb_cmd, used_index);
969
970         /* Response callback data */
971         resp_cb_data = imc_create_resp_cb_data(cb, cb_data,
972                 (void *)&(record->pb_type), sizeof(TelPbType));
973
974         /* Send Request to Modem */
975         ret = tcore_at_prepare_and_send_request(co,
976                 at_cmd, "+CPBR",
977                 TCORE_AT_COMMAND_TYPE_SINGLELINE,
978                 NULL,
979                 on_response_imc_phonebook_read_record, resp_cb_data,
980                 on_send_imc_request, NULL);
981         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Read Record");
982
983         /* Free resources */
984         g_free(at_cmd);
985         g_free(set_pb_cmd);
986
987         return ret;
988 }
989
990 /*
991  * Operation - update_record
992  *
993  * Request -
994  * AT-Command: AT+CPBW=[<index>][,<number>[,<type>[,<text>[,<group>[,<adnumber>
995  *      [,<adtype>[,<secondtext>[,<email>[,<hidden>]]]]]]]]]
996  * where,
997  * ... same read_record Operation
998  *
999  * Response -
1000  * Success: (No Result)
1001  *      OK
1002  * Failure:
1003  *      +CME ERROR: <error>
1004  */
1005 static TelReturn imc_phonebook_update_record(CoreObject *co,
1006                 const TelPbUpdateRecord *req_data,
1007                 TcoreObjectResponseCallback cb, void *cb_data)
1008 {
1009         gchar *at_cmd;
1010         gchar *set_pb_cmd;
1011         ImcRespCbData *resp_cb_data;
1012         TelReturn ret = TEL_RETURN_FAILURE;
1013
1014         dbg("Entry");
1015
1016         /* Check whether pb_type is supported or not, and Select pb_type */
1017         if (__imc_phonebook_check_and_select_type(co, req_data->pb_type, &set_pb_cmd) != TRUE) {
1018                 return ret;
1019         }
1020
1021         /* Set AT-Command according pb_type */
1022         if (req_data->pb_type == TEL_PB_USIM) {
1023                 at_cmd = g_strdup_printf("%s+CPBW=%u,\"%s\",,\"%s\",\"%s\",\"%s\",,\"%s\",\"%s\",%d",
1024                         set_pb_cmd, req_data->index,
1025                         req_data->rec_u.usim.number, req_data->rec_u.usim.name,
1026                         req_data->rec_u.usim.grp_name, req_data->rec_u.usim.anr[0].number,
1027                         req_data->rec_u.usim.sne, req_data->rec_u.usim.email[0],
1028                         req_data->rec_u.usim.hidden);
1029         } else {
1030                 at_cmd = g_strdup_printf("%s+CPBW=%u,\"%s\",,\"%s\"",
1031                         set_pb_cmd, req_data->index,
1032                         req_data->rec_u.sim.number,
1033                         req_data->rec_u.sim.name);
1034         }
1035
1036         /* Response callback data */
1037         resp_cb_data = imc_create_resp_cb_data(cb, cb_data,
1038                 (void *)req_data, sizeof(TelPbUpdateRecord));
1039
1040         /* Send Request to Modem */
1041         ret = tcore_at_prepare_and_send_request(co,
1042                 at_cmd, NULL,
1043                 TCORE_AT_COMMAND_TYPE_NO_RESULT,
1044                 NULL,
1045                 on_response_imc_phonebook_update_record, resp_cb_data,
1046                 on_send_imc_request, NULL);
1047         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Update Record");
1048
1049         /* Free resources */
1050         g_free(at_cmd);
1051         g_free(set_pb_cmd);
1052
1053         return ret;
1054 }
1055
1056 /*
1057  * Operation - delete_record
1058  *
1059  * Request -
1060  * AT-Command: AT+CPBW=<index>
1061  * where,
1062  * <index>
1063  * 1    Integer type values in range of location numbers of phonebook memory
1064  * ...
1065  *
1066  * Response -
1067  * Success: (No Result)
1068  *      OK
1069  * Failure:
1070  *      +CME ERROR: <error>
1071  */
1072 static TelReturn imc_phonebook_delete_record(CoreObject *co,
1073                 const TelPbRecordInfo *record,
1074                 TcoreObjectResponseCallback cb, void *cb_data)
1075 {
1076         gchar *at_cmd;
1077         gchar *set_pb_cmd;
1078         ImcRespCbData *resp_cb_data;
1079         TelReturn ret = TEL_RETURN_FAILURE;
1080
1081         dbg("Entry");
1082
1083         /* Check whether pb_type is supported or not, and Select pb_type */
1084         if (__imc_phonebook_check_and_select_type(co, record->pb_type, &set_pb_cmd) != TRUE) {
1085                 return ret;
1086         }
1087
1088         /* AT-Command */
1089         at_cmd = g_strdup_printf("%s+CPBW=%u", set_pb_cmd, record->index);
1090
1091         /* Response callback data */
1092         resp_cb_data = imc_create_resp_cb_data(cb, cb_data,
1093                 (void *)record, sizeof(TelPbRecordInfo));
1094
1095         /* Send Request to Modem */
1096         ret = tcore_at_prepare_and_send_request(co,
1097                 at_cmd, NULL,
1098                 TCORE_AT_COMMAND_TYPE_NO_RESULT,
1099                 NULL,
1100                 on_response_imc_phonebook_delete_record, resp_cb_data,
1101                 on_send_imc_request, NULL);
1102         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Delete Record");
1103
1104         /* Free resources */
1105         g_free(at_cmd);
1106         g_free(set_pb_cmd);
1107
1108         return ret;
1109 }
1110
1111 /* Phonebook Operations */
1112 static TcorePbOps imc_phonebook_ops = {
1113         .get_info = imc_phonebook_get_info,
1114         .read_record = imc_phonebook_read_record,
1115         .update_record = imc_phonebook_update_record,
1116         .delete_record = imc_phonebook_delete_record,
1117 };
1118
1119 gboolean imc_phonebook_init(TcorePlugin *p, CoreObject *co)
1120 {
1121         PrivateInfo *private_info;
1122
1123         dbg("Entry");
1124
1125         /* Set PrivateInfo */
1126         private_info = tcore_malloc0(sizeof(PrivateInfo));
1127         tcore_object_link_user_data(co, private_info);
1128
1129         /* Set operations */
1130         tcore_phonebook_set_ops(co, &imc_phonebook_ops);
1131
1132         /* Add Callbacks */
1133         tcore_object_add_callback(co, "+PBREADY", on_notification_imc_phonebook_status, NULL);
1134
1135         dbg("Exit");
1136         return TRUE;
1137 }
1138
1139 void imc_phonebook_exit(TcorePlugin *p, CoreObject *co)
1140 {
1141         PrivateInfo *private_info;
1142
1143         private_info = tcore_object_ref_user_data(co);
1144         tcore_check_return_assert(private_info != NULL);
1145
1146         /* Free PrivateInfo */
1147         g_slist_free_full(private_info->used_index_fdn, g_free);
1148         g_slist_free_full(private_info->used_index_adn, g_free);
1149         g_slist_free_full(private_info->used_index_sdn, g_free);
1150         g_slist_free_full(private_info->used_index_usim, g_free);
1151         tcore_free(private_info);
1152
1153         dbg("Exit");
1154 }