1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.voicemail.impl.mail.store.imap;
17 
18 import com.android.voicemail.impl.mail.utils.LogUtils;
19 import java.util.ArrayList;
20 
21 /** Utility methods for use with IMAP. */
22 public class ImapUtility {
23   public static final String TAG = "ImapUtility";
24   /**
25    * Apply quoting rules per IMAP RFC, quoted = DQUOTE *QUOTED-CHAR DQUOTE QUOTED-CHAR = <any
26    * TEXT-CHAR except quoted-specials> / "\" quoted-specials quoted-specials = DQUOTE / "\"
27    *
28    * <p>This is used primarily for IMAP login, but might be useful elsewhere.
29    *
30    * <p>NOTE: Not very efficient - you may wish to preflight this, or perhaps it should check for
31    * trouble chars before calling the replace functions.
32    *
33    * @param s The string to be quoted.
34    * @return A copy of the string, having undergone quoting as described above
35    */
imapQuoted(String s)36   public static String imapQuoted(String s) {
37 
38     // First, quote any backslashes by replacing \ with \\
39     // regex Pattern:  \\    (Java string const = \\\\)
40     // Substitute:     \\\\  (Java string const = \\\\\\\\)
41     String result = s.replaceAll("\\\\", "\\\\\\\\");
42 
43     // Then, quote any double-quotes by replacing " with \"
44     // regex Pattern:  "    (Java string const = \")
45     // Substitute:     \\"  (Java string const = \\\\\")
46     result = result.replaceAll("\"", "\\\\\"");
47 
48     // return string with quotes around it
49     return "\"" + result + "\"";
50   }
51 
52   /**
53    * Gets all of the values in a sequence set per RFC 3501. Any ranges are expanded into a list of
54    * individual numbers. If the set is invalid, an empty array is returned.
55    *
56    * <pre>
57    * sequence-number = nz-number / "*"
58    * sequence-range  = sequence-number ":" sequence-number
59    * sequence-set    = (sequence-number / sequence-range) *("," sequence-set)
60    * </pre>
61    */
getImapSequenceValues(String set)62   public static String[] getImapSequenceValues(String set) {
63     ArrayList<String> list = new ArrayList<String>();
64     if (set != null) {
65       String[] setItems = set.split(",");
66       for (String item : setItems) {
67         if (item.indexOf(':') == -1) {
68           // simple item
69           try {
70             Integer.parseInt(item); // Don't need the value; just ensure it's valid
71             list.add(item);
72           } catch (NumberFormatException e) {
73             LogUtils.d(TAG, "Invalid UID value", e);
74           }
75         } else {
76           // range
77           for (String rangeItem : getImapRangeValues(item)) {
78             list.add(rangeItem);
79           }
80         }
81       }
82     }
83     String[] stringList = new String[list.size()];
84     return list.toArray(stringList);
85   }
86 
87   /**
88    * Expand the given number range into a list of individual numbers. If the range is not valid, an
89    * empty array is returned.
90    *
91    * <pre>
92    * sequence-number = nz-number / "*"
93    * sequence-range  = sequence-number ":" sequence-number
94    * sequence-set    = (sequence-number / sequence-range) *("," sequence-set)
95    * </pre>
96    */
getImapRangeValues(String range)97   public static String[] getImapRangeValues(String range) {
98     ArrayList<String> list = new ArrayList<String>();
99     try {
100       if (range != null) {
101         int colonPos = range.indexOf(':');
102         if (colonPos > 0) {
103           int first = Integer.parseInt(range.substring(0, colonPos));
104           int second = Integer.parseInt(range.substring(colonPos + 1));
105           if (first < second) {
106             for (int i = first; i <= second; i++) {
107               list.add(Integer.toString(i));
108             }
109           } else {
110             for (int i = first; i >= second; i--) {
111               list.add(Integer.toString(i));
112             }
113           }
114         }
115       }
116     } catch (NumberFormatException e) {
117       LogUtils.d(TAG, "Invalid range value", e);
118     }
119     String[] stringList = new String[list.size()];
120     return list.toArray(stringList);
121   }
122 }
123