1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/*
2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project
3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License");
5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License.
6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at
7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *      http://www.apache.org/licenses/LICENSE-2.0
9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software
11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS,
12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and
14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License.
15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.sms;
18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context;
20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.Cursor;
21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.Uri;
22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.provider.BaseColumns;
23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils;
24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.util.Patterns;
25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.SqliteWrapper;
27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil;
28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.HashSet;
30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Set;
31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.regex.Matcher;
32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.regex.Pattern;
33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/**
35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Utility functions for the Messaging Service
36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class MmsSmsUtils {
38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private MmsSmsUtils() {
39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Forbidden being instantiated.
40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // An alias (or commonly called "nickname") is:
43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // Nickname must begin with a letter.
44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // Only letters a-z, numbers 0-9, or . are allowed in Nickname field.
45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean isAlias(final String string, final int subId) {
46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (!MmsConfig.get(subId).isAliasEnabled()) {
47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int len = string == null ? 0 : string.length();
51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (len < MmsConfig.get(subId).getAliasMinChars() ||
53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                len > MmsConfig.get(subId).getAliasMaxChars()) {
54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (!Character.isLetter(string.charAt(0))) {    // Nickname begins with a letter
58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        for (int i = 1; i < len; i++) {
61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final char c = string.charAt(i);
62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (!(Character.isLetterOrDigit(c) || c == '.')) {
63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return false;
64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return true;
68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * mailbox         =       name-addr
72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * name-addr       =       [display-name] angle-addr
73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * angle-addr      =       [CFWS] "<" addr-spec ">" [CFWS]
74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final Pattern NAME_ADDR_EMAIL_PATTERN =
76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*");
77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static String extractAddrSpec(final String address) {
79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Matcher match = NAME_ADDR_EMAIL_PATTERN.matcher(address);
80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (match.matches()) {
82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return match.group(2);
83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return address;
85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Returns true if the address is an email address
89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param address the input address to be tested
91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return true if address is an email address
92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean isEmailAddress(final String address) {
94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (TextUtils.isEmpty(address)) {
95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String s = extractAddrSpec(address);
99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Matcher match = Patterns.EMAIL_ADDRESS.matcher(s);
100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return match.matches();
101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Returns true if the number is a Phone number
105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param number the input number to be tested
107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return true if number is a Phone number
108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean isPhoneNumber(final String number) {
110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (TextUtils.isEmpty(number)) {
111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Matcher match = Patterns.PHONE.matcher(number);
115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return match.matches();
116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Check if MMS is required when sending to email address
120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param destinationHasEmailAddress destination includes an email address
122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return true if MMS is required.
123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean getRequireMmsForEmailAddress(final boolean destinationHasEmailAddress,
125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int subId) {
126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (!TextUtils.isEmpty(MmsConfig.get(subId).getEmailGateway())) {
127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } else {
129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return destinationHasEmailAddress;
130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Helper functions for the "threads" table used by MMS and SMS.
135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final class Threads implements android.provider.Telephony.ThreadsColumns {
137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private static final String[] ID_PROJECTION = { BaseColumns._ID };
138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private static final Uri THREAD_ID_CONTENT_URI = Uri.parse(
139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                "content://mms-sms/threadID");
140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public static final Uri CONTENT_URI = Uri.withAppendedPath(
141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                android.provider.Telephony.MmsSms.CONTENT_URI, "conversations");
142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // No one should construct an instance of this class.
144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private Threads() {
145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * This is a single-recipient version of
149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * getOrCreateThreadId.  It's convenient for use with SMS
150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * messages.
151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public static long getOrCreateThreadId(final Context context, final String recipient) {
153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Set<String> recipients = new HashSet<String>();
154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            recipients.add(recipient);
156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return getOrCreateThreadId(context, recipients);
157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * Given the recipients list and subject of an unsaved message,
161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * return its thread ID.  If the message starts a new thread,
162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * allocate a new thread ID.  Otherwise, use the appropriate
163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * existing thread ID.
164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         *
165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * Find the thread ID of the same set of recipients (in
166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * any order, without any additions). If one
167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * is found, return it.  Otherwise, return a unique thread ID.
168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public static long getOrCreateThreadId(
170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final Context context, final Set<String> recipients) {
171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon();
172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            for (String recipient : recipients) {
174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (isEmailAddress(recipient)) {
175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    recipient = extractAddrSpec(recipient);
176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                uriBuilder.appendQueryParameter("recipient", recipient);
179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Uri uri = uriBuilder.build();
182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            //if (DEBUG) Rlog.v(TAG, "getOrCreateThreadId uri: " + uri);
183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    uri, ID_PROJECTION, null, null, null);
186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (cursor != null) {
187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                try {
188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    if (cursor.moveToFirst()) {
189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        return cursor.getLong(0);
190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    } else {
191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        LogUtil.e(LogUtil.BUGLE_DATAMODEL_TAG,
192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                                "getOrCreateThreadId returned no rows!");
193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                } finally {
195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    cursor.close();
196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(LogUtil.BUGLE_DATAMODEL_TAG, "getOrCreateThreadId failed with "
200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    + LogUtil.sanitizePII(recipients.toString()));
201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            throw new IllegalArgumentException("Unable to find or allocate a thread ID.");
202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd}
205