• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright 2006, The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 // Old implementation for phone_number_compare(), which has used in cupcake, but once replaced with
19 // the new, more strict version, and reverted again.
20 
21 #include <string.h>
22 
23 namespace android {
24 
25 static int MIN_MATCH = 7;
26 
27 /** True if c is ISO-LATIN characters 0-9 */
isISODigit(char c)28 static bool isISODigit (char c)
29 {
30     return c >= '0' && c <= '9';
31 }
32 
33 /** True if c is ISO-LATIN characters 0-9, *, # , +  */
isNonSeparator(char c)34 static bool isNonSeparator(char c)
35 {
36     return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
37 }
38 
39 /**
40  * Phone numbers are stored in "lookup" form in the database
41  * as reversed strings to allow for caller ID lookup
42  *
43  * This method takes a phone number and makes a valid SQL "LIKE"
44  * string that will match the lookup form
45  *
46  */
47 /** all of a up to len must be an international prefix or
48  *  separators/non-dialing digits
49  */
matchIntlPrefix(const char * a,int len)50 static bool matchIntlPrefix(const char* a, int len)
51 {
52     /* '([^0-9*#+]\+[^0-9*#+] | [^0-9*#+]0(0|11)[^0-9*#+] )$' */
53     /*        0    1                     2 3 45               */
54 
55     int state = 0;
56     for (int i = 0 ; i < len ; i++) {
57         char c = a[i];
58 
59         switch (state) {
60             case 0:
61                 if      (c == '+') state = 1;
62                 else if (c == '0') state = 2;
63                 else if (isNonSeparator(c)) return false;
64             break;
65 
66             case 2:
67                 if      (c == '0') state = 3;
68                 else if (c == '1') state = 4;
69                 else if (isNonSeparator(c)) return false;
70             break;
71 
72             case 4:
73                 if      (c == '1') state = 5;
74                 else if (isNonSeparator(c)) return false;
75             break;
76 
77             default:
78                 if (isNonSeparator(c)) return false;
79             break;
80 
81         }
82     }
83 
84     return state == 1 || state == 3 || state == 5;
85 }
86 
87 /** all of 'a' up to len must match non-US trunk prefix ('0') */
matchTrunkPrefix(const char * a,int len)88 static bool matchTrunkPrefix(const char* a, int len)
89 {
90     bool found;
91 
92     found = false;
93 
94     for (int i = 0 ; i < len ; i++) {
95         char c = a[i];
96 
97         if (c == '0' && !found) {
98             found = true;
99         } else if (isNonSeparator(c)) {
100             return false;
101         }
102     }
103 
104     return found;
105 }
106 
107 /** all of 'a' up to len must be a (+|00|011)country code)
108  *  We're fast and loose with the country code. Any \d{1,3} matches */
matchIntlPrefixAndCC(const char * a,int len)109 static bool matchIntlPrefixAndCC(const char* a, int len)
110 {
111     /*  [^0-9*#+]*(\+|0(0|11)\d\d?\d? [^0-9*#+] $ */
112     /*      0       1 2 3 45  6 7  8              */
113 
114     int state = 0;
115     for (int i = 0 ; i < len ; i++ ) {
116         char c = a[i];
117 
118         switch (state) {
119             case 0:
120                 if      (c == '+') state = 1;
121                 else if (c == '0') state = 2;
122                 else if (isNonSeparator(c)) return false;
123             break;
124 
125             case 2:
126                 if      (c == '0') state = 3;
127                 else if (c == '1') state = 4;
128                 else if (isNonSeparator(c)) return false;
129             break;
130 
131             case 4:
132                 if      (c == '1') state = 5;
133                 else if (isNonSeparator(c)) return false;
134             break;
135 
136             case 1:
137             case 3:
138             case 5:
139                 if      (isISODigit(c)) state = 6;
140                 else if (isNonSeparator(c)) return false;
141             break;
142 
143             case 6:
144             case 7:
145                 if      (isISODigit(c)) state++;
146                 else if (isNonSeparator(c)) return false;
147             break;
148 
149             default:
150                 if (isNonSeparator(c)) return false;
151         }
152     }
153 
154     return state == 6 || state == 7 || state == 8;
155 }
156 
157 /** or -1 if both are negative */
minPositive(int a,int b)158 static int minPositive(int a, int b)
159 {
160     if (a >= 0 && b >= 0) {
161         return (a < b) ? a : b;
162     } else if (a >= 0) { /* && b < 0 */
163         return a;
164     } else if (b >= 0) { /* && a < 0 */
165         return b;
166     } else { /* a < 0 && b < 0 */
167         return -1;
168     }
169 }
170 
171 /**
172  * Return the offset into a of the first appearance of b, or -1 if there
173  * is no such character in a.
174  */
indexOf(const char * a,char b)175 static int indexOf(const char *a, char b) {
176     const char *ix = strchr(a, b);
177 
178     if (ix == NULL)
179         return -1;
180     else
181         return ix - a;
182 }
183 
184 /**
185  * Compare phone numbers a and b, return true if they're identical
186  * enough for caller ID purposes.
187  *
188  * - Compares from right to left
189  * - requires MIN_MATCH (7) characters to match
190  * - handles common trunk prefixes and international prefixes
191  *   (basically, everything except the Russian trunk prefix)
192  *
193  * Tolerates nulls
194  */
phone_number_compare_loose(const char * a,const char * b)195 bool phone_number_compare_loose(const char* a, const char* b)
196 {
197     int ia, ib;
198     int matched;
199     int numSeparatorCharsInA = 0;
200     int numSeparatorCharsInB = 0;
201 
202     if (a == NULL || b == NULL) {
203         return false;
204     }
205 
206     ia = strlen(a);
207     ib = strlen(b);
208     if (ia == 0 || ib == 0) {
209         return false;
210     }
211 
212     // Compare from right to left
213     ia--;
214     ib--;
215 
216     matched = 0;
217 
218     while (ia >= 0 && ib >=0) {
219         char ca, cb;
220         bool skipCmp = false;
221 
222         ca = a[ia];
223 
224         if (!isNonSeparator(ca)) {
225             ia--;
226             skipCmp = true;
227             numSeparatorCharsInA++;
228         }
229 
230         cb = b[ib];
231 
232         if (!isNonSeparator(cb)) {
233             ib--;
234             skipCmp = true;
235             numSeparatorCharsInB++;
236         }
237 
238         if (!skipCmp) {
239             if (cb != ca) {
240                 break;
241             }
242             ia--; ib--; matched++;
243         }
244     }
245 
246     if (matched < MIN_MATCH) {
247         const int effectiveALen = strlen(a) - numSeparatorCharsInA;
248         const int effectiveBLen = strlen(b) - numSeparatorCharsInB;
249 
250         // if the number of dialable chars in a and b match, but the matched chars < MIN_MATCH,
251         // treat them as equal (i.e. 404-04 and 40404)
252         if (effectiveALen == effectiveBLen && effectiveALen == matched) {
253             return true;
254         }
255 
256         return false;
257     }
258 
259     // At least one string has matched completely;
260     if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
261         return true;
262     }
263 
264     /*
265      * Now, what remains must be one of the following for a
266      * match:
267      *
268      *  - a '+' on one and a '00' or a '011' on the other
269      *  - a '0' on one and a (+,00)<country code> on the other
270      *     (for this, a '0' and a '00' prefix would have succeeded above)
271      */
272 
273     if (matchIntlPrefix(a, ia + 1) && matchIntlPrefix(b, ib +1)) {
274         return true;
275     }
276 
277     if (matchTrunkPrefix(a, ia + 1) && matchIntlPrefixAndCC(b, ib +1)) {
278         return true;
279     }
280 
281     if (matchTrunkPrefix(b, ib + 1) && matchIntlPrefixAndCC(a, ia +1)) {
282         return true;
283     }
284 
285     /*
286      * Last resort: if the number of unmatched characters on both sides is less than or equal
287      * to the length of the longest country code and only one number starts with a + accept
288      * the match. This is because some countries like France and Russia have an extra prefix
289      * digit that is used when dialing locally in country that does not show up when you dial
290      * the number using the country code. In France this prefix digit is used to determine
291      * which land line carrier to route the call over.
292      */
293     bool aPlusFirst = (*a == '+');
294     bool bPlusFirst = (*b == '+');
295     if (ia < 4 && ib < 4 && (aPlusFirst || bPlusFirst) && !(aPlusFirst && bPlusFirst)) {
296         return true;
297     }
298 
299     return false;
300 }
301 
302 }  // namespace android
303