1284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy/*
2284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy * Copyright (C) 2011 The Android Open Source Project
3284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy *
4284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy * Licensed under the Apache License, Version 2.0 (the "License");
5284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy * you may not use this file except in compliance with the License.
6284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy * You may obtain a copy of the License at
7284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy *
8284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy *      http://www.apache.org/licenses/LICENSE-2.0
9284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy *
10284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy * Unless required by applicable law or agreed to in writing, software
11284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy * distributed under the License is distributed on an "AS IS" BASIS,
12284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy * See the License for the specific language governing permissions and
14284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy * limitations under the License.
15284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy */
16284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy
17284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedypackage com.android.email.mail.store.imap;
18284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy
19284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedyimport com.android.emailcommon.Logging;
20284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy
21284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedyimport android.util.Log;
22284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy
23284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedyimport java.util.ArrayList;
24284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy
25284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy/**
26284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy * Utility methods for use with IMAP.
27284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy */
28284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedypublic class ImapUtility {
29284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy    /**
30284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * Apply quoting rules per IMAP RFC,
31284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * quoted          = DQUOTE *QUOTED-CHAR DQUOTE
32284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * QUOTED-CHAR     = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
33284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * quoted-specials = DQUOTE / "\"
34284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     *
35284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * This is used primarily for IMAP login, but might be useful elsewhere.
36284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     *
37284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * NOTE:  Not very efficient - you may wish to preflight this, or perhaps it should check
38284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * for trouble chars before calling the replace functions.
39284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     *
40284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * @param s The string to be quoted.
41284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * @return A copy of the string, having undergone quoting as described above
42284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     */
43284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy    public static String imapQuoted(String s) {
44284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy
45284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        // First, quote any backslashes by replacing \ with \\
46284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        // regex Pattern:  \\    (Java string const = \\\\)
47284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        // Substitute:     \\\\  (Java string const = \\\\\\\\)
48284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        String result = s.replaceAll("\\\\", "\\\\\\\\");
49284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy
50284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        // Then, quote any double-quotes by replacing " with \"
51284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        // regex Pattern:  "    (Java string const = \")
52284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        // Substitute:     \\"  (Java string const = \\\\\")
53284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        result = result.replaceAll("\"", "\\\\\"");
54284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy
55284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        // return string with quotes around it
56284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        return "\"" + result + "\"";
57284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy    }
58284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy
59284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy    /**
60284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * Gets all of the values in a sequence set per RFC 3501. Any ranges are expanded into a
61284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * list of individual numbers. If the set is invalid, an empty array is returned.
62284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * <pre>
63284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * sequence-number = nz-number / "*"
64284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * sequence-range  = sequence-number ":" sequence-number
65284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * sequence-set    = (sequence-number / sequence-range) *("," sequence-set)
66284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * </pre>
67284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     */
68284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy    public static String[] getImapSequenceValues(String set) {
69284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        ArrayList<String> list = new ArrayList<String>();
70284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        if (set != null) {
71284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy            String[] setItems = set.split(",");
72284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy            for (String item : setItems) {
73284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                if (item.indexOf(':') == -1) {
74284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    // simple item
75284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    try {
76284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                        Integer.parseInt(item); // Don't need the value; just ensure it's valid
77284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                        list.add(item);
78284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    } catch (NumberFormatException e) {
79284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                        Log.d(Logging.LOG_TAG, "Invalid UID value", e);
80284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    }
81284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                } else {
82284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    // range
83284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    for (String rangeItem : getImapRangeValues(item)) {
84284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                        list.add(rangeItem);
85284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    }
86284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                }
87284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy            }
88284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        }
89284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        String[] stringList = new String[list.size()];
90284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        return list.toArray(stringList);
91284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy    }
92284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy
93284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy    /**
94284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * Expand the given number range into a list of individual numbers. If the range is not valid,
95284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * an empty array is returned.
96284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * <pre>
97284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * sequence-number = nz-number / "*"
98284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * sequence-range  = sequence-number ":" sequence-number
99284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * sequence-set    = (sequence-number / sequence-range) *("," sequence-set)
100284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     * </pre>
101284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy     */
102284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy    public static String[] getImapRangeValues(String range) {
103284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        ArrayList<String> list = new ArrayList<String>();
104284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        try {
105284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy            if (range != null) {
106284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                int colonPos = range.indexOf(':');
107284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                if (colonPos > 0) {
108284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    int first  = Integer.parseInt(range.substring(0, colonPos));
109284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    int second = Integer.parseInt(range.substring(colonPos + 1));
110284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    if (first < second) {
111284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                        for (int i = first; i <= second; i++) {
112284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                            list.add(Integer.toString(i));
113284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                        }
114284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    } else {
115284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                        for (int i = first; i >= second; i--) {
116284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                            list.add(Integer.toString(i));
117284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                        }
118284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                    }
119284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy                }
120284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy            }
121284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        } catch (NumberFormatException e) {
122284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy            Log.d(Logging.LOG_TAG, "Invalid range value", e);
123284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        }
124284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        String[] stringList = new String[list.size()];
125284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy        return list.toArray(stringList);
126284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy    }
127284d8d7db5743d24b9aa246afe3d24139bdb0813Todd Kennedy}
128