4 * Copyright (c) 2010 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include <unicode/ustring.h>
23 #include <TapiUtility.h>
24 #include <ITapiNetwork.h>
26 #include <phone_number.h>
29 #include "ctsvc_internal.h"
30 #include "ctsvc_server_setting.h"
31 #include "ctsvc_normalize.h"
32 #include "ctsvc_localize_utils.h"
33 #include "ctsvc_number_utils.h"
40 const static ctsvc_mcc_cc_map __mcc_cc_list[] = {
276 static char *cc = NULL;
277 static TapiHandle *handle_for_cc = NULL;
279 char* ctsvc_get_network_cc(bool reload)
286 TapiHandle *handle = NULL;
287 static bool cc_loaded = false;
289 if (cc_loaded && false == reload)
294 handle = (TapiHandle *)ctsvc_init_tapi_handle_for_cc();
295 RETVM_IF(NULL == handle, NULL, "tel_init() Fail");
297 ret = tel_get_property_int(handle, TAPI_PROP_NETWORK_SERVICE_TYPE, &state);
298 if (ret != TAPI_API_SUCCESS) {
299 CTS_ERR("tel_get_property_int Fail(%d)", ret);
304 if (state == TAPI_NETWORK_SERVICE_TYPE_UNKNOWN
305 || state == TAPI_NETWORK_SERVICE_TYPE_NO_SERVICE
306 || state == TAPI_NETWORK_SERVICE_TYPE_EMERGENCY
307 || state == TAPI_NETWORK_SERVICE_TYPE_SEARCH) {
308 CTS_INFO("network service is not working : state(%d)", state);
312 ret = tel_get_property_string(handle, TAPI_PROP_NETWORK_PLMN, &temp);
313 if (ret != TAPI_API_SUCCESS) {
314 CTS_ERR("tel_get_property_string Fail(%d)", ret);
323 for (i=0;i<sizeof(__mcc_cc_list)/sizeof(ctsvc_mcc_cc_map);i++) {
324 if (__mcc_cc_list[i].mcc == mcc) {
325 cc = __mcc_cc_list[i].cc;
333 static void __ctsvc_network_cc_changed(TapiHandle *handle, const char *noti_id, void *data, void *user_data)
335 ctsvc_get_network_cc(true);
338 void* ctsvc_init_tapi_handle_for_cc()
341 return handle_for_cc;
343 handle_for_cc = tel_init(NULL);
345 int ret = tel_register_noti_event(handle_for_cc,
346 TAPI_PROP_NETWORK_PLMN, __ctsvc_network_cc_changed, NULL);
347 WARN_IF(ret != TAPI_API_SUCCESS, "tel_register_noti_event Fail(%d)", ret);
350 CTS_ERR("tel_init fail");
352 return handle_for_cc;
355 void ctsvc_deinit_tapi_handle_for_cc()
358 int ret = tel_deregister_noti_event(handle_for_cc, TAPI_PROP_NETWORK_PLMN);
359 WARN_IF(ret != TAPI_API_SUCCESS, "tel_register_noti_event Fail(%d)", ret);
360 tel_deinit(handle_for_cc);
362 handle_for_cc = NULL;
365 static inline int __ctsvc_phone_number_has_country_code(const char *src, int len)
372 switch (src[ret++]-'0') {
377 if (len <= ret) return 0;
378 switch (src[ret++]-'0') {
393 CTS_ERR("The parameter(src:%s) has invalid character set", src);
397 if (len <= ret) return 0;
398 switch (src[ret++]-'0') {
413 CTS_ERR("The parameter(src:%s) has invalid character set", src);
417 if (len <= ret) return 0;
418 switch (src[ret++]-'0') {
433 CTS_ERR("The parameter(src:%s) has invalid character set", src);
437 if (len <= ret) return 0;
438 switch (src[ret++]-'0') {
453 CTS_ERR("The parameter(src:%s) has invalid character set", src);
457 if (len <= ret) return 0;
458 switch (src[ret++]-'0') {
473 CTS_ERR("The parameter(src:%s) has invalid character set", src);
477 if (len <= ret) return 0;
478 switch (src[ret++]-'0') {
493 CTS_ERR("The parameter(src:%s) has invalid character set", src);
497 if (len <= ret) return 0;
498 switch (src[ret++]-'0') {
513 CTS_ERR("The parameter(src:%s) has invalid character set", src);
518 CTS_ERR("The parameter(src:%s) has invalid character set", src);
526 * Number Matching Rule
527 * refer to the http://www.itu.int/dms_pub/itu-t/opb/sp/T-SP-E.164C-2011-PDF-E.pdf
530 CTSVC_PLUS_ONLY, /* + */
531 CTSVC_PLUS_IP_ONLY, /* +IP (International prefix) */
532 CTSVC_PLUS_CC_ONLY, /* +CC (Country code) */
533 CTSVC_PLUS_IP_CC, /* +IP CC */
534 CTSVC_IP_ONLY, /* IP */
535 CTSVC_CC_ONLY, /* CC */
536 CTSVC_IP_CC, /* IP CC */
540 static int __ctsvc_number_has_ip_and_cc(const char*number, int len, int *index)
542 bool have_cc = false;
543 bool have_plus = false;
544 int ret = CTSVC_NONE;
553 switch(number[start_index]) {
557 if (len <= start_index) {
558 *index = start_index;
559 return CTSVC_PLUS_ONLY; /* '+' */
565 * 0 (Turks and Caicos Islands, Samoa)
566 * 00, 011, 0011, 010, 000
567 * 001/007 (Cambodia), 001/008 (Indonesia, Singapore)
568 * 001/002 (Korea), 002(Taiwan)
569 * 810 (Belarus, Kazakhstan, Russian, Tajikistan, Turkmenistan)
570 * 009/007/005(Colombia), 009(Nigeria)
572 * 00/012/013/014 (Israel)
574 switch(number[start_index]) {
578 if (len <= start_index) {
579 *index = start_index;
580 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+0' */
583 switch(number[start_index]) {
584 case '0': /* '+00' */
587 if (len <= start_index) {
588 *index = start_index;
589 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+00' */
592 switch(number[start_index]) {
593 case '0': /* '+000' */
594 case '2': /* '+002' */
595 case '5': /* '+005' */
596 case '7': /* '+007' */
597 case '8': /* '+008' */
598 case '9': /* '+009' */
601 if (len <= start_index) {
602 *index = start_index;
603 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+00Y' */
606 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
608 *index = start_index;
609 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+00Y CC' */
612 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index-1], len-start_index+1);
614 *index = start_index-1;
615 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+00 CC' */
618 *index = start_index;
619 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+00Y XXX' */
620 case '1': /* '+001' */
622 if (len <= start_index) {
623 *index = start_index;
624 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+001' */
627 if (number[start_index] == '1') {
629 if (len <= start_index) {
630 *index = start_index;
631 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+0011' */
634 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
636 *index = start_index;
637 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+0011 CC' */
642 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
643 *index = start_index;
645 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+001 CC' */
647 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+001 XXX' */
648 default: /* '+00 3', '+00 4', '+00 6' */
649 *index = start_index;
650 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
652 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+00 CC' */
654 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+00 XXX' */
655 } /* end of fourth switch */
658 case '1': /* '+01' */
661 if (len <= start_index) {
662 *index = start_index-1; /* '+0 1' */
663 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_NONE);
666 switch(number[start_index]) {
667 case '0': /* '+010' */
668 case '1': /* '+011' */
669 case '2': /* '+012' */
670 case '3': /* '+013' */
671 case '4': /* '+014' */
674 if (len <= start_index) {
675 *index = start_index;
676 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+01Y' */
679 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
680 *index = start_index;
682 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+01Y CC' */
684 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+01Y XXX' */
688 *index = start_index-1; /* '+0 1' */
689 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_NONE);
693 default: /* '+0 CC' */
695 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
696 *index = start_index;
698 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+0 CC' */
700 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+0 XXX' */
703 } /* end of third switch */
705 break; /* end of '+0' */
708 if (start_index+2 <= len && STRING_EQUAL == strncmp(&number[start_index], "19", 2)) { /* '+119' */
709 match_len = start_index + 2;
710 ret = (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY);
713 match_len = start_index-1;
714 ret = (have_plus?CTSVC_PLUS_ONLY:CTSVC_NONE); /* '+ CC' */
719 if (start_index+2 <= len && STRING_EQUAL == strncmp(&number[start_index], "10", 2)) { /* '+810' */
720 match_len = start_index + 2;
721 ret = (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY);
724 match_len = start_index-1;
725 ret = (have_plus?CTSVC_PLUS_ONLY:CTSVC_NONE); /* '+ CC' */
729 match_len = start_index;
730 ret = (have_plus?CTSVC_PLUS_ONLY:CTSVC_NONE); /* '+ CC' */
732 } /* end of second switch */
734 break; /* '+' default */
735 } /* end of first switch */
739 if (match_len < len) {
740 have_cc = __ctsvc_phone_number_has_country_code(&number[match_len], len-match_len);
744 return CTSVC_CC_ONLY;
745 case CTSVC_PLUS_ONLY:
746 return CTSVC_PLUS_CC_ONLY;
747 case CTSVC_PLUS_IP_ONLY:
748 return CTSVC_PLUS_IP_CC;
757 int ctsvc_normalize_number(const char *src, char *dest, int dest_size, bool replace_alphabet)
762 char *normalized_out = NULL;
763 char temp[dest_size];
767 CTS_ERR("The parameter(src) is NULL");
777 if (src[s_pos] == '+')
778 temp[d_pos++] = src[s_pos++];
779 while (src[s_pos] != 0) {
780 if (src[s_pos] == '+' || src[s_pos] == ';')
782 temp[d_pos++] = src[s_pos++];
786 ret = phone_number_get_normalized_number(temp, &normalized_out);
787 if (PHONE_NUMBER_ERROR_NONE == ret) {
788 d_pos = strlen(normalized_out);
789 memcpy(dest, normalized_out, d_pos+1);
790 free(normalized_out);
793 memcpy(dest, temp, d_pos+1);
802 * vaild character : digit, +, *, #,, ;, alphabet(depends on replace_alphabet parameter)
803 * Remove invalid string from number
805 int ctsvc_clean_number(const char *src, char *dest, int dest_size, bool replace_alphabet)
810 char temp[dest_size];
813 CTS_ERR("The parameter(src) is NULL");
819 while (src[s_pos] != 0) {
821 if (dest_size-2 < pos) break;
823 char_len = ctsvc_check_utf8(src[s_pos]);
829 /* fullwidth -> halfwidth */
830 if (src[s_pos] == 0xef) {
831 if (src[s_pos+1] == 0xbc) {
832 if (0x90 <= src[s_pos+2] && src[s_pos+2] <= 0x99) /* ef bc 90 : '0' ~ ef bc 99 : '9' */
833 temp[pos++] = src[s_pos+2] - 0x60;
834 else if (0xa1 <= src[s_pos+2] && src[s_pos+2] <= 0xba) /* ef bc a1 : 'A' ~ ef bc ba : 'Z' */
835 temp[pos++] = src[s_pos+2] - 0x60;
836 else if (0x8b == src[s_pos+2]) /* ef bc 8b : '+' */
838 else if (0x8a == src[s_pos+2]) /* ef bc 8a : '*' */
840 else if (0x83 == src[s_pos+2]) /* ef bc 83 : '#' */
842 else if (0x8c == src[s_pos+2]) /* ef bc 8c : ',' */
844 else if (0x9b == src[s_pos+2]) /* ef bc 9b : ';' */
847 else if (src[s_pos+1] == 0xbd
848 && (0x81 <= src[s_pos+2] && src[s_pos+2] <= 0x9a)) /* ef bd 81 : 'a' ~ ef bd 9a : 'z' */
849 temp[pos++] = src[s_pos+2] - 0x40;
856 else if (char_len == 1) {
857 if (0x41 <= src[s_pos] && src[s_pos] <= 0x5a) /* 'A' ~ 'Z' */
858 temp[pos++] = src[s_pos];
859 else if (0x61 <= src[s_pos] && src[s_pos] <= 0x7a) /* 'a' ~ 'z' */
860 temp[pos++] = src[s_pos] - 0x20;
862 temp[pos++] = src[s_pos];
870 while (temp[pos] != 0) {
871 if ('0' <= temp[pos] && temp[pos] <= '9')
872 dest[d_pos++] = temp[pos];
873 else if (temp[pos] == '+' || temp[pos] == '#'
874 || temp[pos] == '*' || temp[pos] == ';' || temp[pos] == ',')
875 dest[d_pos++] = temp[pos];
883 static int __ctsvc_minmatch_number(const char *src, char *dest, int dest_size, int min_match)
888 const char *temp_number;
889 const char *cc = ctsvc_get_network_cc(false);
892 len = __ctsvc_phone_number_has_country_code(&src[1], strlen(src)-1);
893 temp_number = src + len +1;
895 else if ('0' == src[0])
897 else if (cc && cc[0] == '7' && src[0] == '8')
902 len = strlen(temp_number);
905 while (0 <= (len-d_pos-1) && temp_number[len-d_pos-1]
906 && d_pos < min_match) {
907 if (dest_size-d_pos == 0) {
908 CTS_ERR("Destination string buffer is not enough(%s)", src);
909 return CONTACTS_ERROR_INTERNAL;
912 dest[d_pos] = temp_number[len-d_pos-1];
918 for (i=0; i<len/2;i++) {
921 dest[i] = dest[len-i-1];
926 memcpy(dest, src, strlen(src));
927 dest[strlen(src)] = 0;
930 return CONTACTS_ERROR_NONE;
933 int ctsvc_get_minmatch_number(const char *src, char *dest, int dest_size, int min_match)
937 RETV_IF(NULL == src, CONTACTS_ERROR_INVALID_PARAMETER);
938 RETV_IF(NULL == dest, CONTACTS_ERROR_INVALID_PARAMETER);
940 ret = __ctsvc_minmatch_number(src, dest, dest_size, min_match);
941 if (ret != CONTACTS_ERROR_NONE) {
942 CTS_ERR("__ctsvc_minmatch_number() Fail(%d)", ret);
946 return CONTACTS_ERROR_NONE;
949 static bool __ctsvc_is_phonenumber_halfwidth(const char* keyword)
952 int len = strlen(keyword);
954 /* TODO: we should add predicate including '+' */
955 /* TODO: finally, we try to check the number with regular expression. */
956 for (i=0; i<len; i++) {
957 if ((keyword[i] < '0' || '9' < keyword[i]) && keyword[i] != '+') {
958 CTS_ERR("keyword[%d]: %c is not number)", i, keyword[i]);
965 #define UTF8_FULLWIDTH_LENGTH 3
966 static bool __ctsvc_is_phonenumber_fullwidth(const char* keyword)
971 if (keyword == NULL || *keyword == '\0')
974 str_len = strlen(keyword);
975 for (i=0;i<str_len;i += char_len) {
976 char_len = ctsvc_check_utf8(keyword[i]);
977 if (char_len != UTF8_FULLWIDTH_LENGTH || str_len-i < UTF8_FULLWIDTH_LENGTH)
980 if (keyword[i] == 0xef) {
981 if (keyword[i+1] == 0xbc) {
982 if (0x90 <= keyword[i+2] && keyword[i+2] <= 0x99) /* ef bc 90 : '0' ~ ef bc 99 : '9' */
984 else if (0x8b == keyword[i+2]) /* ef bc 8b : '+' */
998 bool ctsvc_is_phonenumber(const char* src)
1000 return (__ctsvc_is_phonenumber_halfwidth(src) || __ctsvc_is_phonenumber_fullwidth(src));
1003 /* numbers are cleaned number or normalized number */
1004 static bool __ctsvc_number_compare(const char *number1, const char *number2)
1009 int minmatch_len = ctsvc_get_phonenumber_min_match_digit();
1010 const char *cc = ctsvc_get_network_cc(false);
1012 if (NULL == number1 || NULL == number2 || '\0' == *number1 || '\0' == *number2)
1015 len1 = strlen(number1);
1016 len2 = strlen(number2);
1018 /* compare number in reverse order */
1019 for (matched = 0; 0 < len1 && 0 < len2;) {
1020 if (number1[len1-1] != number2[len2-1])
1028 if (len1 == 0 && len2 == 0)
1031 /* one is substring of the other string */
1032 if (minmatch_len <= matched&& (len1 == 0 || len2 == 0))
1035 /* one is +IPCC or +CC, the other is start wth NTP (National trunk prefix) */
1036 if (minmatch_len <= matched) {
1042 * International Prefix (IP) is related to current location where to call
1043 * Country Code (CC) is related to the SIM card who to call
1044 * If you try to call in United State using Korea SIM card,
1045 * the number will be 011 82 X XXXXXXXX.
1046 * So, when comparing number, just check IP validation and CC and natinal number matching.
1049 int n1 = __ctsvc_number_has_ip_and_cc(number1, len1, &index1);
1050 int n2 = __ctsvc_number_has_ip_and_cc(number2, len2, &index2);
1053 * + (IP) CC XXXXXXXX, 0XXXXXXXX
1054 * + (810) 7 XXX XXX XX XX, 8XXX XXX XX XX (Russian)
1056 if ((CTSVC_PLUS_IP_CC == n1 || CTSVC_PLUS_CC_ONLY == n1 ||
1057 CTSVC_IP_CC == n1 || CTSVC_CC_ONLY == n1)
1058 && (number2[0] == '0' || (cc && cc[0] == '7' && number2[0] == '8')))
1060 else if ((CTSVC_PLUS_IP_CC == n2 || CTSVC_PLUS_CC_ONLY == n2 ||
1061 CTSVC_IP_CC == n2 || CTSVC_CC_ONLY == n2)
1062 && (number1[0] == '0' || (cc && cc[0] == '7' && number1[0] == '8')))
1065 * + IP CC XXXXXXXX, + CC XXXXXXXX (ex. +001 82 11 1234 5678, +82 10 1234 5678)
1067 else if ((CTSVC_PLUS_IP_CC == n1 || CTSVC_IP_CC == n1)
1068 && (CTSVC_PLUS_CC_ONLY == n2 || CTSVC_CC_ONLY == n2)) {
1069 int p = (CTSVC_PLUS_CC_ONLY == n2)?1:0;
1070 cc_index = __ctsvc_phone_number_has_country_code(&number2[p], len2-p);
1071 if (0 < cc_index && STRING_EQUAL == strncmp(&number1[index1], &number2[p], cc_index))
1074 else if ((CTSVC_PLUS_IP_CC == n2 || CTSVC_IP_CC == n2)
1075 && (CTSVC_PLUS_CC_ONLY == n1 || CTSVC_CC_ONLY == n1)) {
1076 int p = (CTSVC_PLUS_CC_ONLY == n1)?1:0;
1077 cc_index = __ctsvc_phone_number_has_country_code(&number1[p], len1-p);
1078 if (0 < cc_index && STRING_EQUAL == strncmp(&number2[index2], &number1[p], cc_index))
1082 * + CC XXXXXXXX, + IP CC XXXXXXXX (ex. +001 82 10 1234 5678, +82 10 1234 5678)
1084 else if ((CTSVC_PLUS_IP_ONLY == n1 || CTSVC_IP_ONLY == n1)
1085 && CTSVC_PLUS_ONLY == n2) {
1088 else if ((CTSVC_PLUS_IP_ONLY == n2 || CTSVC_IP_ONLY == n2)
1089 && CTSVC_PLUS_ONLY == n1) {
1097 /* When querying _NUMBER_COMPARE_, this function will be called. */
1098 void ctsvc_db_phone_number_equal_callback(sqlite3_context * context,
1099 int argc, sqlite3_value ** argv)
1101 #ifdef _CONTACTS_IPC_SERVER
1106 sqlite3_result_int(context, 0);
1107 CTS_ERR("argc invalid");
1111 number1 = (char*)sqlite3_value_text(argv[0]);
1112 number2 = (char*)sqlite3_value_text(argv[1]);
1114 sqlite3_result_int(context, __ctsvc_number_compare(number1, number2));