| 
 1 | 
  
 |  | 
| 
 2 | 
  
 |  | 
| 
 3 | 
  
 |  | 
| 
 4 | 
  
 |  | 
| 
 5 | 
  
 |  | 
| 
 6 | 
  
 |  | 
| 
 7 | 
  
 |  | 
| 
 8 | 
  
 |  | 
| 
 9 | 
  
 |  | 
| 
 10 | 
  
 |  | 
| 
 11 | 
  
 |  | 
| 
 12 | 
  
 |  | 
| 
 13 | 
  
 |  | 
| 
 14 | 
  
 |  | 
| 
 15 | 
  
 |  | 
| 
 16 | 
  
 |  | 
| 
 17 | 
  
 |  | 
| 
 18 | 
  
 | package javax.mail.internet; | 
| 
 19 | 
  
 |  | 
| 
 20 | 
  
 |  | 
| 
 21 | 
  
 |  | 
| 
 22 | 
  
 |  | 
| 
 23 | 
  
 | public class HeaderTokenizer { | 
| 
 24 | 
  
 |     public static class Token { | 
| 
 25 | 
  
 |          | 
| 
 26 | 
  
 |         public static final int ATOM = -1; | 
| 
 27 | 
  
 |         public static final int COMMENT = -3; | 
| 
 28 | 
  
 |         public static final int EOF = -4; | 
| 
 29 | 
  
 |         public static final int QUOTEDSTRING = -2; | 
| 
 30 | 
  
 |         private int _type; | 
| 
 31 | 
  
 |         private String _value; | 
| 
 32 | 
  
 |  | 
| 
 33 | 
 379
 |         public Token(int type, String value) {
 | 
| 
 34 | 
 379
 |             _type = type;
 | 
| 
 35 | 
 379
 |             _value = value;
 | 
| 
 36 | 
  
 |         } | 
| 
 37 | 
  
 |  | 
| 
 38 | 
 515
 |         public int getType() {
 | 
| 
 39 | 
 515
 |             return _type;
 | 
| 
 40 | 
  
 |         } | 
| 
 41 | 
  
 |  | 
| 
 42 | 
 252
 |         public String getValue() {
 | 
| 
 43 | 
 252
 |             return _value;
 | 
| 
 44 | 
  
 |         } | 
| 
 45 | 
  
 |     } | 
| 
 46 | 
  
 |  | 
| 
 47 | 
  
 |     private static final Token EOF = new Token(Token.EOF, null); | 
| 
 48 | 
  
 |      | 
| 
 49 | 
  
 |     public static final String MIME = "()<>@,;:\\\"\t []/?="; | 
| 
 50 | 
  
 |      | 
| 
 51 | 
  
 |     public static final String RFC822 = "()<>@,;:\\\"\t .[]"; | 
| 
 52 | 
  
 |     private static final String WHITE = " \t\n\r"; | 
| 
 53 | 
  
 |     private String _delimiters; | 
| 
 54 | 
  
 |     private String _header; | 
| 
 55 | 
  
 |     private boolean _skip; | 
| 
 56 | 
  
 |     private int pos; | 
| 
 57 | 
  
 |  | 
| 
 58 | 
 11
 |     public HeaderTokenizer(String header) {
 | 
| 
 59 | 
 11
 |         this(header, RFC822);
 | 
| 
 60 | 
  
 |     } | 
| 
 61 | 
  
 |  | 
| 
 62 | 
 171
 |     public HeaderTokenizer(String header, String delimiters) {
 | 
| 
 63 | 
 171
 |         this(header, delimiters, true);
 | 
| 
 64 | 
  
 |     } | 
| 
 65 | 
  
 |  | 
| 
 66 | 
 187
 |     public HeaderTokenizer(String header,
 | 
| 
 67 | 
  
 |                            String delimiters, | 
| 
 68 | 
  
 |                            boolean skipComments) { | 
| 
 69 | 
 187
 |         _skip = skipComments;
 | 
| 
 70 | 
 187
 |         _header = header;
 | 
| 
 71 | 
 187
 |         _delimiters = delimiters;
 | 
| 
 72 | 
  
 |     } | 
| 
 73 | 
  
 |  | 
| 
 74 | 
 73
 |     public String getRemainder() {
 | 
| 
 75 | 
 73
 |         return _header.substring(pos);
 | 
| 
 76 | 
  
 |     } | 
| 
 77 | 
  
 |  | 
| 
 78 | 
 460
 |     public Token next() throws ParseException {
 | 
| 
 79 | 
 460
 |         return readToken();
 | 
| 
 80 | 
  
 |     } | 
| 
 81 | 
  
 |  | 
| 
 82 | 
 7
 |     public Token peek() throws ParseException {
 | 
| 
 83 | 
 7
 |         int start = pos;
 | 
| 
 84 | 
 7
 |         try {
 | 
| 
 85 | 
 7
 |             return readToken();
 | 
| 
 86 | 
  
 |         } finally { | 
| 
 87 | 
 7
 |             pos = start;
 | 
| 
 88 | 
  
 |         } | 
| 
 89 | 
  
 |     } | 
| 
 90 | 
  
 |  | 
| 
 91 | 
  
 |      | 
| 
 92 | 
  
 |  | 
| 
 93 | 
  
 |  | 
| 
 94 | 
  
 |  | 
| 
 95 | 
  
 |  | 
| 
 96 | 
 210
 |     private Token readAtomicToken() {
 | 
| 
 97 | 
  
 |          | 
| 
 98 | 
 210
 |         int start = pos;
 | 
| 
 99 | 
 210
 |         while (++pos < _header.length()) {
 | 
| 
 100 | 
  
 |              | 
| 
 101 | 
 975
 |             char ch = _header.charAt(pos);
 | 
| 
 102 | 
 975
 |             if (_delimiters.indexOf(_header.charAt(pos)) != -1 || ch < 32 || ch >= 127) {
 | 
| 
 103 | 
 126
 |                 break;
 | 
| 
 104 | 
  
 |             } | 
| 
 105 | 
  
 |         } | 
| 
 106 | 
  
 |  | 
| 
 107 | 
 210
 |         return new Token(Token.ATOM, _header.substring(start, pos));
 | 
| 
 108 | 
  
 |     } | 
| 
 109 | 
  
 |  | 
| 
 110 | 
  
 |      | 
| 
 111 | 
  
 |  | 
| 
 112 | 
  
 |  | 
| 
 113 | 
  
 |  | 
| 
 114 | 
  
 |  | 
| 
 115 | 
  
 |  | 
| 
 116 | 
  
 |  | 
| 
 117 | 
 493
 |     private Token readToken() throws ParseException {
 | 
| 
 118 | 
 493
 |         if (pos >= _header.length()) {
 | 
| 
 119 | 
 83
 |             return EOF;
 | 
| 
 120 | 
  
 |         } else { | 
| 
 121 | 
 410
 |             char c = _header.charAt(pos);
 | 
| 
 122 | 
  
 |              | 
| 
 123 | 
 410
 |             if (c == '(') {
 | 
| 
 124 | 
 15
 |                 Token comment = readComment();
 | 
| 
 125 | 
 11
 |                 if (_skip) {
 | 
| 
 126 | 
 2
 |                     return readToken();
 | 
| 
 127 | 
  
 |                 } else { | 
| 
 128 | 
 9
 |                     return comment;
 | 
| 
 129 | 
  
 |                 } | 
| 
 130 | 
  
 |                  | 
| 
 131 | 
 395
 |             } else if (c == '\"') {
 | 
| 
 132 | 
 26
 |                 return readQuotedString();
 | 
| 
 133 | 
  
 |              | 
| 
 134 | 
 369
 |             } else if (WHITE.indexOf(c) != -1) {
 | 
| 
 135 | 
 24
 |                 eatWhiteSpace();
 | 
| 
 136 | 
 24
 |                 return readToken();
 | 
| 
 137 | 
  
 |              | 
| 
 138 | 
 345
 |             } else if (c < 32 || c >= 127 || _delimiters.indexOf(c) != -1) {
 | 
| 
 139 | 
 135
 |                 pos++;
 | 
| 
 140 | 
 135
 |                 return new Token((int)c, String.valueOf(c));
 | 
| 
 141 | 
  
 |             } else { | 
| 
 142 | 
  
 |                  | 
| 
 143 | 
 210
 |                 return readAtomicToken();
 | 
| 
 144 | 
  
 |             } | 
| 
 145 | 
  
 |         } | 
| 
 146 | 
  
 |     } | 
| 
 147 | 
  
 |  | 
| 
 148 | 
  
 |      | 
| 
 149 | 
  
 |  | 
| 
 150 | 
  
 |  | 
| 
 151 | 
  
 |  | 
| 
 152 | 
  
 |  | 
| 
 153 | 
  
 |  | 
| 
 154 | 
  
 |  | 
| 
 155 | 
  
 |  | 
| 
 156 | 
  
 |  | 
| 
 157 | 
  
 |  | 
| 
 158 | 
 5
 |     private String getEscapedValue(int start, int end) throws ParseException {
 | 
| 
 159 | 
 5
 |         StringBuffer value = new StringBuffer();
 | 
| 
 160 | 
  
 |  | 
| 
 161 | 
 5
 |         for (int i = start; i < end; i++) {
 | 
| 
 162 | 
 15
 |             char ch = _header.charAt(i);
 | 
| 
 163 | 
  
 |              | 
| 
 164 | 
 15
 |             if (ch == '\\') {
 | 
| 
 165 | 
 3
 |                 i++;
 | 
| 
 166 | 
 3
 |                 if (i == end) {
 | 
| 
 167 | 
 0
 |                     throw new ParseException("Invalid escape character");
 | 
| 
 168 | 
  
 |                 } | 
| 
 169 | 
 3
 |                 value.append(_header.charAt(i));
 | 
| 
 170 | 
  
 |             } | 
| 
 171 | 
  
 |              | 
| 
 172 | 
  
 |              | 
| 
 173 | 
 12
 |             else if (ch == '\r') {
 | 
| 
 174 | 
  
 |                  | 
| 
 175 | 
 2
 |                 if (i < end - 1 && _header.charAt(i + 1) == '\n') {
 | 
| 
 176 | 
 2
 |                     i++;
 | 
| 
 177 | 
  
 |                 } | 
| 
 178 | 
  
 |             } | 
| 
 179 | 
  
 |             else { | 
| 
 180 | 
  
 |                  | 
| 
 181 | 
 10
 |                 value.append(ch);
 | 
| 
 182 | 
  
 |             } | 
| 
 183 | 
  
 |         } | 
| 
 184 | 
 5
 |         return value.toString();
 | 
| 
 185 | 
  
 |     } | 
| 
 186 | 
  
 |  | 
| 
 187 | 
  
 |      | 
| 
 188 | 
  
 |  | 
| 
 189 | 
  
 |  | 
| 
 190 | 
  
 |  | 
| 
 191 | 
  
 |  | 
| 
 192 | 
  
 |  | 
| 
 193 | 
  
 |  | 
| 
 194 | 
 15
 |     private Token readComment() throws ParseException {
 | 
| 
 195 | 
 15
 |         int start = pos + 1;
 | 
| 
 196 | 
 15
 |         int nesting = 1;
 | 
| 
 197 | 
  
 |  | 
| 
 198 | 
 15
 |         boolean requiresEscaping = false;
 | 
| 
 199 | 
  
 |  | 
| 
 200 | 
  
 |          | 
| 
 201 | 
 15
 |         while (++pos < _header.length()) {
 | 
| 
 202 | 
 142
 |             char ch = _header.charAt(pos);
 | 
| 
 203 | 
 142
 |             if (ch == ')') {
 | 
| 
 204 | 
 18
 |                 nesting--;
 | 
| 
 205 | 
 18
 |                 if (nesting == 0) {
 | 
| 
 206 | 
 11
 |                     break;
 | 
| 
 207 | 
  
 |                 } | 
| 
 208 | 
  
 |             } | 
| 
 209 | 
 124
 |             else if (ch == '(') {
 | 
| 
 210 | 
 7
 |                 nesting++;
 | 
| 
 211 | 
  
 |             } | 
| 
 212 | 
 117
 |             else if (ch == '\\') {
 | 
| 
 213 | 
 1
 |                 pos++;
 | 
| 
 214 | 
 1
 |                 requiresEscaping = true;
 | 
| 
 215 | 
  
 |             } | 
| 
 216 | 
  
 |              | 
| 
 217 | 
 116
 |             else if (ch == '\r') {
 | 
| 
 218 | 
 1
 |                 requiresEscaping = true;
 | 
| 
 219 | 
  
 |             } | 
| 
 220 | 
  
 |         } | 
| 
 221 | 
  
 |  | 
| 
 222 | 
 15
 |         if (nesting != 0) {
 | 
| 
 223 | 
 4
 |             throw new ParseException("Unbalanced comments");
 | 
| 
 224 | 
  
 |         } | 
| 
 225 | 
  
 |  | 
| 
 226 | 
 11
 |         String value;
 | 
| 
 227 | 
 11
 |         if (requiresEscaping) {
 | 
| 
 228 | 
 2
 |             value = getEscapedValue(start, pos);
 | 
| 
 229 | 
  
 |         } | 
| 
 230 | 
  
 |         else { | 
| 
 231 | 
 9
 |             value = _header.substring(start, pos++);
 | 
| 
 232 | 
  
 |         } | 
| 
 233 | 
 11
 |         return new Token(Token.COMMENT, value);
 | 
| 
 234 | 
  
 |     } | 
| 
 235 | 
  
 |  | 
| 
 236 | 
  
 |      | 
| 
 237 | 
  
 |  | 
| 
 238 | 
  
 |  | 
| 
 239 | 
  
 |  | 
| 
 240 | 
  
 |  | 
| 
 241 | 
  
 |  | 
| 
 242 | 
  
 |  | 
| 
 243 | 
 26
 |     private Token readQuotedString() throws ParseException {
 | 
| 
 244 | 
 26
 |         int start = pos+1;
 | 
| 
 245 | 
 26
 |         boolean requiresEscaping = false;
 | 
| 
 246 | 
  
 |  | 
| 
 247 | 
  
 |          | 
| 
 248 | 
 26
 |         while (++pos < _header.length()) {
 | 
| 
 249 | 
 326
 |             char ch = _header.charAt(pos);
 | 
| 
 250 | 
 326
 |             if (ch == '"') {
 | 
| 
 251 | 
 22
 |                 String value;
 | 
| 
 252 | 
 22
 |                 if (requiresEscaping) {
 | 
| 
 253 | 
 3
 |                     value = getEscapedValue(start, pos);
 | 
| 
 254 | 
  
 |                 } | 
| 
 255 | 
  
 |                 else { | 
| 
 256 | 
 19
 |                     value = _header.substring(start, pos++);
 | 
| 
 257 | 
  
 |                 } | 
| 
 258 | 
 22
 |                 return new Token(Token.QUOTEDSTRING, value);
 | 
| 
 259 | 
  
 |             } | 
| 
 260 | 
 304
 |             else if (ch == '\\') {
 | 
| 
 261 | 
 4
 |                 pos++;
 | 
| 
 262 | 
 4
 |                 requiresEscaping = true;
 | 
| 
 263 | 
  
 |             } | 
| 
 264 | 
  
 |              | 
| 
 265 | 
 300
 |             else if (ch == '\r') {
 | 
| 
 266 | 
 1
 |                 requiresEscaping = true;
 | 
| 
 267 | 
  
 |             } | 
| 
 268 | 
  
 |         } | 
| 
 269 | 
  
 |  | 
| 
 270 | 
 4
 |         throw new ParseException("Missing '\"'");
 | 
| 
 271 | 
  
 |     } | 
| 
 272 | 
  
 |  | 
| 
 273 | 
  
 |      | 
| 
 274 | 
  
 |  | 
| 
 275 | 
  
 |  | 
| 
 276 | 
 24
 |     private void eatWhiteSpace() {
 | 
| 
 277 | 
  
 |          | 
| 
 278 | 
 24
 |         while (++pos < _header.length()
 | 
| 
 279 | 
  
 |                 && WHITE.indexOf(_header.charAt(pos)) != -1) | 
| 
 280 | 
  
 |             ; | 
| 
 281 | 
  
 |     } | 
| 
 282 | 
  
 | } |