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);
318 if (temp && 3 < strlen(temp))
321 for (i=0;i<sizeof(__mcc_cc_list)/sizeof(ctsvc_mcc_cc_map);i++) {
322 if (__mcc_cc_list[i].mcc == mcc) {
323 cc = __mcc_cc_list[i].cc;
331 static void __ctsvc_network_cc_changed(TapiHandle *handle, const char *noti_id, void *data, void *user_data)
333 ctsvc_get_network_cc(true);
336 void* ctsvc_init_tapi_handle_for_cc()
339 return handle_for_cc;
341 handle_for_cc = tel_init(NULL);
343 int ret = tel_register_noti_event(handle_for_cc,
344 TAPI_PROP_NETWORK_PLMN, __ctsvc_network_cc_changed, NULL);
345 WARN_IF(ret != TAPI_API_SUCCESS, "tel_register_noti_event Fail(%d)", ret);
348 CTS_ERR("tel_init fail");
350 return handle_for_cc;
353 void ctsvc_deinit_tapi_handle_for_cc()
356 int ret = tel_deregister_noti_event(handle_for_cc, TAPI_PROP_NETWORK_PLMN);
357 WARN_IF(ret != TAPI_API_SUCCESS, "tel_register_noti_event Fail(%d)", ret);
358 tel_deinit(handle_for_cc);
360 handle_for_cc = NULL;
363 static inline int __ctsvc_phone_number_has_country_code(const char *src, int len)
370 switch (src[ret++]-'0') {
375 if (len <= ret) return 0;
376 switch (src[ret++]-'0') {
391 CTS_ERR("The parameter(src:%s) has invalid character set", src);
395 if (len <= ret) return 0;
396 switch (src[ret++]-'0') {
411 CTS_ERR("The parameter(src:%s) has invalid character set", src);
415 if (len <= ret) return 0;
416 switch (src[ret++]-'0') {
431 CTS_ERR("The parameter(src:%s) has invalid character set", src);
435 if (len <= ret) return 0;
436 switch (src[ret++]-'0') {
451 CTS_ERR("The parameter(src:%s) has invalid character set", src);
455 if (len <= ret) return 0;
456 switch (src[ret++]-'0') {
471 CTS_ERR("The parameter(src:%s) has invalid character set", src);
475 if (len <= ret) return 0;
476 switch (src[ret++]-'0') {
491 CTS_ERR("The parameter(src:%s) has invalid character set", src);
495 if (len <= ret) return 0;
496 switch (src[ret++]-'0') {
511 CTS_ERR("The parameter(src:%s) has invalid character set", src);
516 CTS_ERR("The parameter(src:%s) has invalid character set", src);
524 * Number Matching Rule
525 * refer to the http://www.itu.int/dms_pub/itu-t/opb/sp/T-SP-E.164C-2011-PDF-E.pdf
528 CTSVC_PLUS_ONLY, /* + */
529 CTSVC_PLUS_IP_ONLY, /* +IP (International prefix) */
530 CTSVC_PLUS_CC_ONLY, /* +CC (Country code) */
531 CTSVC_PLUS_IP_CC, /* +IP CC */
532 CTSVC_IP_ONLY, /* IP */
533 CTSVC_CC_ONLY, /* CC */
534 CTSVC_IP_CC, /* IP CC */
538 static int __ctsvc_number_has_ip_and_cc(const char*number, int len, int *index)
540 bool have_cc = false;
541 bool have_plus = false;
542 int ret = CTSVC_NONE;
551 switch(number[start_index]) {
555 if (len <= start_index) {
556 *index = start_index;
557 return CTSVC_PLUS_ONLY; /* '+' */
563 * 0 (Turks and Caicos Islands, Samoa)
564 * 00, 011, 0011, 010, 000
565 * 001/007 (Cambodia), 001/008 (Indonesia, Singapore)
566 * 001/002 (Korea), 002(Taiwan)
567 * 810 (Belarus, Kazakhstan, Russian, Tajikistan, Turkmenistan)
568 * 009/007/005(Colombia), 009(Nigeria)
570 * 00/012/013/014 (Israel)
572 switch(number[start_index]) {
576 if (len <= start_index) {
577 *index = start_index;
578 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+0' */
581 switch(number[start_index]) {
582 case '0': /* '+00' */
585 if (len <= start_index) {
586 *index = start_index;
587 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+00' */
590 switch(number[start_index]) {
591 case '0': /* '+000' */
592 case '2': /* '+002' */
593 case '5': /* '+005' */
594 case '7': /* '+007' */
595 case '8': /* '+008' */
596 case '9': /* '+009' */
599 if (len <= start_index) {
600 *index = start_index;
601 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+00Y' */
604 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
606 *index = start_index;
607 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+00Y CC' */
610 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index-1], len-start_index+1);
612 *index = start_index-1;
613 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+00 CC' */
616 *index = start_index;
617 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+00Y XXX' */
618 case '1': /* '+001' */
620 if (len <= start_index) {
621 *index = start_index;
622 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+001' */
625 if (number[start_index] == '1') {
627 if (len <= start_index) {
628 *index = start_index;
629 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+0011' */
632 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
634 *index = start_index;
635 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+0011 CC' */
640 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
641 *index = start_index;
643 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+001 CC' */
645 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+001 XXX' */
646 default: /* '+00 3', '+00 4', '+00 6' */
647 *index = start_index;
648 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
650 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+00 CC' */
652 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+00 XXX' */
653 } /* end of fourth switch */
656 case '1': /* '+01' */
659 if (len <= start_index) {
660 *index = start_index-1; /* '+0 1' */
661 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_NONE);
664 switch(number[start_index]) {
665 case '0': /* '+010' */
666 case '1': /* '+011' */
667 case '2': /* '+012' */
668 case '3': /* '+013' */
669 case '4': /* '+014' */
672 if (len <= start_index) {
673 *index = start_index;
674 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+01Y' */
677 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
678 *index = start_index;
680 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+01Y CC' */
682 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+01Y XXX' */
686 *index = start_index-1; /* '+0 1' */
687 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_NONE);
691 default: /* '+0 CC' */
693 have_cc = __ctsvc_phone_number_has_country_code(&number[start_index], len-start_index);
694 *index = start_index;
696 return (have_plus?CTSVC_PLUS_IP_CC:CTSVC_IP_CC); /* '+0 CC' */
698 return (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY); /* '+0 XXX' */
701 } /* end of third switch */
703 break; /* end of '+0' */
706 if (start_index+2 <= len && STRING_EQUAL == strncmp(&number[start_index], "19", 2)) { /* '+119' */
707 match_len = start_index + 2;
708 ret = (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY);
711 match_len = start_index-1;
712 ret = (have_plus?CTSVC_PLUS_ONLY:CTSVC_NONE); /* '+ CC' */
717 if (start_index+2 <= len && STRING_EQUAL == strncmp(&number[start_index], "10", 2)) { /* '+810' */
718 match_len = start_index + 2;
719 ret = (have_plus?CTSVC_PLUS_IP_ONLY:CTSVC_IP_ONLY);
722 match_len = start_index-1;
723 ret = (have_plus?CTSVC_PLUS_ONLY:CTSVC_NONE); /* '+ CC' */
727 match_len = start_index;
728 ret = (have_plus?CTSVC_PLUS_ONLY:CTSVC_NONE); /* '+ CC' */
730 } /* end of second switch */
732 break; /* '+' default */
733 } /* end of first switch */
737 if (match_len < len) {
738 have_cc = __ctsvc_phone_number_has_country_code(&number[match_len], len-match_len);
742 return CTSVC_CC_ONLY;
743 case CTSVC_PLUS_ONLY:
744 return CTSVC_PLUS_CC_ONLY;
745 case CTSVC_PLUS_IP_ONLY:
746 return CTSVC_PLUS_IP_CC;
755 int ctsvc_normalize_number(const char *src, char *dest, int dest_size, bool replace_alphabet)
760 char *normalized_out = NULL;
761 char temp[dest_size];
765 CTS_ERR("The parameter(src) is NULL");
775 if (src[s_pos] == '+')
776 temp[d_pos++] = src[s_pos++];
777 while (src[s_pos] != 0) {
778 if (src[s_pos] == '+' || src[s_pos] == ';')
780 temp[d_pos++] = src[s_pos++];
784 ret = phone_number_get_normalized_number(temp, &normalized_out);
785 if (PHONE_NUMBER_ERROR_NONE == ret) {
786 d_pos = strlen(normalized_out);
787 memcpy(dest, normalized_out, d_pos+1);
788 free(normalized_out);
791 memcpy(dest, temp, d_pos+1);
800 * vaild character : digit, +, *, #,, ;, alphabet(depends on replace_alphabet parameter)
801 * Remove invalid string from number
803 int ctsvc_clean_number(const char *src, char *dest, int dest_size, bool replace_alphabet)
808 char temp[dest_size];
811 CTS_ERR("The parameter(src) is NULL");
817 while (src[s_pos] != 0) {
819 if (dest_size-2 < pos) break;
821 char_len = ctsvc_check_utf8(src[s_pos]);
827 /* fullwidth -> halfwidth */
828 if (src[s_pos] == 0xef) {
829 if (src[s_pos+1] == 0xbc) {
830 if (0x90 <= src[s_pos+2] && src[s_pos+2] <= 0x99) /* ef bc 90 : '0' ~ ef bc 99 : '9' */
831 temp[pos++] = src[s_pos+2] - 0x60;
832 else if (0xa1 <= src[s_pos+2] && src[s_pos+2] <= 0xba) /* ef bc a1 : 'A' ~ ef bc ba : 'Z' */
833 temp[pos++] = src[s_pos+2] - 0x60;
834 else if (0x8b == src[s_pos+2]) /* ef bc 8b : '+' */
836 else if (0x8a == src[s_pos+2]) /* ef bc 8a : '*' */
838 else if (0x83 == src[s_pos+2]) /* ef bc 83 : '#' */
840 else if (0x8c == src[s_pos+2]) /* ef bc 8c : ',' */
842 else if (0x9b == src[s_pos+2]) /* ef bc 9b : ';' */
845 else if (src[s_pos+1] == 0xbd
846 && (0x81 <= src[s_pos+2] && src[s_pos+2] <= 0x9a)) /* ef bd 81 : 'a' ~ ef bd 9a : 'z' */
847 temp[pos++] = src[s_pos+2] - 0x40;
854 else if (char_len == 1) {
855 if (0x41 <= src[s_pos] && src[s_pos] <= 0x5a) /* 'A' ~ 'Z' */
856 temp[pos++] = src[s_pos];
857 else if (0x61 <= src[s_pos] && src[s_pos] <= 0x7a) /* 'a' ~ 'z' */
858 temp[pos++] = src[s_pos] - 0x20;
860 temp[pos++] = src[s_pos];
868 while (temp[pos] != 0) {
869 if ('0' <= temp[pos] && temp[pos] <= '9')
870 dest[d_pos++] = temp[pos];
871 else if (temp[pos] == '+' || temp[pos] == '#'
872 || temp[pos] == '*' || temp[pos] == ';' || temp[pos] == ',')
873 dest[d_pos++] = temp[pos];
881 static int __ctsvc_minmatch_number(const char *src, char *dest, int dest_size, int min_match)
886 const char *temp_number;
887 const char *cc = ctsvc_get_network_cc(false);
890 len = __ctsvc_phone_number_has_country_code(&src[1], strlen(src)-1);
891 temp_number = src + len +1;
893 else if ('0' == src[0])
895 else if (cc && cc[0] == '7' && src[0] == '8')
900 len = strlen(temp_number);
903 while (0 <= (len-d_pos-1) && temp_number[len-d_pos-1]
904 && d_pos < min_match) {
905 if (dest_size-d_pos == 0) {
906 CTS_ERR("Destination string buffer is not enough(%s)", src);
907 return CONTACTS_ERROR_INTERNAL;
910 dest[d_pos] = temp_number[len-d_pos-1];
916 for (i=0; i<len/2;i++) {
919 dest[i] = dest[len-i-1];
924 memcpy(dest, src, strlen(src));
925 dest[strlen(src)] = 0;
928 return CONTACTS_ERROR_NONE;
931 int ctsvc_get_minmatch_number(const char *src, char *dest, int dest_size, int min_match)
935 RETV_IF(NULL == src, CONTACTS_ERROR_INVALID_PARAMETER);
936 RETV_IF(NULL == dest, CONTACTS_ERROR_INVALID_PARAMETER);
938 ret = __ctsvc_minmatch_number(src, dest, dest_size, min_match);
939 if (ret != CONTACTS_ERROR_NONE) {
940 CTS_ERR("__ctsvc_minmatch_number() Fail(%d)", ret);
944 return CONTACTS_ERROR_NONE;
947 static bool __ctsvc_is_phonenumber_halfwidth(const char* keyword)
950 int len = strlen(keyword);
952 /* TODO: we should add predicate including '+' */
953 /* TODO: finally, we try to check the number with regular expression. */
954 for (i=0; i<len; i++) {
955 if ((keyword[i] < '0' || '9' < keyword[i]) && keyword[i] != '+') {
956 CTS_ERR("keyword[%d]: %c is not number)", i, keyword[i]);
963 #define UTF8_FULLWIDTH_LENGTH 3
964 static bool __ctsvc_is_phonenumber_fullwidth(const char* keyword)
969 if (keyword == NULL || *keyword == '\0')
972 str_len = strlen(keyword);
973 for (i=0;i<str_len;i += char_len) {
974 char_len = ctsvc_check_utf8(keyword[i]);
975 if (char_len != UTF8_FULLWIDTH_LENGTH || str_len-i < UTF8_FULLWIDTH_LENGTH)
978 if (keyword[i] == 0xef) {
979 if (keyword[i+1] == 0xbc) {
980 if (0x90 <= keyword[i+2] && keyword[i+2] <= 0x99) /* ef bc 90 : '0' ~ ef bc 99 : '9' */
982 else if (0x8b == keyword[i+2]) /* ef bc 8b : '+' */
996 bool ctsvc_is_phonenumber(const char* src)
998 return (__ctsvc_is_phonenumber_halfwidth(src) || __ctsvc_is_phonenumber_fullwidth(src));
1001 /* numbers are cleaned number or normalized number */
1002 static bool __ctsvc_number_compare(const char *number1, const char *number2)
1007 int minmatch_len = ctsvc_get_phonenumber_min_match_digit();
1008 const char *cc = ctsvc_get_network_cc(false);
1010 if (NULL == number1 || NULL == number2 || '\0' == *number1 || '\0' == *number2)
1013 len1 = strlen(number1);
1014 len2 = strlen(number2);
1016 /* compare number in reverse order */
1017 for (matched = 0; 0 < len1 && 0 < len2;) {
1018 if (number1[len1-1] != number2[len2-1])
1026 if (len1 == 0 && len2 == 0)
1029 /* one is substring of the other string */
1030 if (minmatch_len <= matched&& (len1 == 0 || len2 == 0))
1033 /* one is +IPCC or +CC, the other is start wth NTP (National trunk prefix) */
1034 if (minmatch_len <= matched) {
1040 * International Prefix (IP) is related to current location where to call
1041 * Country Code (CC) is related to the SIM card who to call
1042 * If you try to call in United State using Korea SIM card,
1043 * the number will be 011 82 X XXXXXXXX.
1044 * So, when comparing number, just check IP validation and CC and natinal number matching.
1047 int n1 = __ctsvc_number_has_ip_and_cc(number1, len1, &index1);
1048 int n2 = __ctsvc_number_has_ip_and_cc(number2, len2, &index2);
1051 * + (IP) CC XXXXXXXX, 0XXXXXXXX
1052 * + (810) 7 XXX XXX XX XX, 8XXX XXX XX XX (Russian)
1054 if ((CTSVC_PLUS_IP_CC == n1 || CTSVC_PLUS_CC_ONLY == n1 ||
1055 CTSVC_IP_CC == n1 || CTSVC_CC_ONLY == n1)
1056 && (number2[0] == '0' || (cc && cc[0] == '7' && number2[0] == '8')))
1058 else if ((CTSVC_PLUS_IP_CC == n2 || CTSVC_PLUS_CC_ONLY == n2 ||
1059 CTSVC_IP_CC == n2 || CTSVC_CC_ONLY == n2)
1060 && (number1[0] == '0' || (cc && cc[0] == '7' && number1[0] == '8')))
1063 * + IP CC XXXXXXXX, + CC XXXXXXXX (ex. +001 82 11 1234 5678, +82 10 1234 5678)
1065 else if ((CTSVC_PLUS_IP_CC == n1 || CTSVC_IP_CC == n1)
1066 && (CTSVC_PLUS_CC_ONLY == n2 || CTSVC_CC_ONLY == n2)) {
1067 int p = (CTSVC_PLUS_CC_ONLY == n2)?1:0;
1068 cc_index = __ctsvc_phone_number_has_country_code(&number2[p], len2-p);
1069 if (0 < cc_index && STRING_EQUAL == strncmp(&number1[index1], &number2[p], cc_index))
1072 else if ((CTSVC_PLUS_IP_CC == n2 || CTSVC_IP_CC == n2)
1073 && (CTSVC_PLUS_CC_ONLY == n1 || CTSVC_CC_ONLY == n1)) {
1074 int p = (CTSVC_PLUS_CC_ONLY == n1)?1:0;
1075 cc_index = __ctsvc_phone_number_has_country_code(&number1[p], len1-p);
1076 if (0 < cc_index && STRING_EQUAL == strncmp(&number2[index2], &number1[p], cc_index))
1080 * + CC XXXXXXXX, + IP CC XXXXXXXX (ex. +001 82 10 1234 5678, +82 10 1234 5678)
1082 else if ((CTSVC_PLUS_IP_ONLY == n1 || CTSVC_IP_ONLY == n1)
1083 && CTSVC_PLUS_ONLY == n2) {
1086 else if ((CTSVC_PLUS_IP_ONLY == n2 || CTSVC_IP_ONLY == n2)
1087 && CTSVC_PLUS_ONLY == n1) {
1095 /* When querying _NUMBER_COMPARE_, this function will be called. */
1096 void ctsvc_db_phone_number_equal_callback(sqlite3_context * context,
1097 int argc, sqlite3_value ** argv)
1099 #ifdef _CONTACTS_IPC_SERVER
1104 sqlite3_result_int(context, 0);
1105 CTS_ERR("argc invalid");
1109 number1 = (char*)sqlite3_value_text(argv[0]);
1110 number2 = (char*)sqlite3_value_text(argv[1]);
1112 sqlite3_result_int(context, __ctsvc_number_compare(number1, number2));