001    /*
002     * Copyright 2010 the original author or authors.
003     * 
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     *  Unless required by applicable law or agreed to in writing, software
011     *  distributed under the License is distributed on an "AS IS" BASIS,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package com.hs.mail.imap.message.builder;
017    
018    import java.io.UnsupportedEncodingException;
019    import java.util.LinkedList;
020    
021    import javax.mail.Flags;
022    import javax.mail.Message;
023    import javax.mail.internet.MimeUtility;
024    import javax.mail.search.ComparisonTerm;
025    
026    import com.hs.mail.imap.message.SequenceRange;
027    import com.hs.mail.imap.message.request.ImapRequest;
028    import com.hs.mail.imap.message.request.SearchRequest;
029    import com.hs.mail.imap.message.search.AllKey;
030    import com.hs.mail.imap.message.search.AndKey;
031    import com.hs.mail.imap.message.search.FlagKey;
032    import com.hs.mail.imap.message.search.FromStringKey;
033    import com.hs.mail.imap.message.search.HeaderKey;
034    import com.hs.mail.imap.message.search.InternalDateKey;
035    import com.hs.mail.imap.message.search.KeywordKey;
036    import com.hs.mail.imap.message.search.NotKey;
037    import com.hs.mail.imap.message.search.OrKey;
038    import com.hs.mail.imap.message.search.RecipientStringKey;
039    import com.hs.mail.imap.message.search.SearchKey;
040    import com.hs.mail.imap.message.search.SearchKeyList;
041    import com.hs.mail.imap.message.search.SentDateKey;
042    import com.hs.mail.imap.message.search.SequenceKey;
043    import com.hs.mail.imap.message.search.SizeKey;
044    import com.hs.mail.imap.message.search.SubjectKey;
045    import com.hs.mail.imap.message.search.TextKey;
046    import com.hs.mail.imap.parser.ParseException;
047    import com.hs.mail.imap.parser.Token;
048    import com.hs.mail.imap.server.codec.DecoderUtils;
049    import com.hs.mail.imap.server.codec.ImapMessage;
050    import com.hs.mail.util.MailUtils;
051    
052    /**
053     * 
054     * @author Won Chul Doh
055     * @since 30 Jan, 2010
056     *
057     */
058    public class SearchRequestBuilder extends AbstractUidRequestBuilder {
059    
060            @Override
061            public ImapRequest createRequest(String tag, String command,
062                            ImapMessage message, boolean useUID) {
063                    LinkedList<Token> tokens = message.getTokens();
064                    String charset = parseCharset(tokens);
065                    SearchKey searchKey = createSearchKey(tag, tokens, charset);
066                    return new SearchRequest(tag, command, charset, searchKey, useUID);
067            }
068            
069            protected SearchKey createSearchKey(String tag, LinkedList<Token> tokens,
070                            String charset) {
071                    try {
072                            SearchKeyList searchKey = new AndKey();
073                            do {
074                                    searchKey.addKey(decodeSearchKey(tokens, charset));
075                            } while (tokens.peek() != null);
076                            removeAllKey(searchKey);
077                            if (searchKey.size() == 1) {
078                                    return searchKey.getSearchKeys().get(0);
079                            } else {
080                                    return searchKey;
081                            }
082                    } catch (Exception e) {
083                            throw new ParseException(tag, "Error while parsing command");
084                    }
085            }
086            
087            private void removeAllKey(SearchKeyList searchKey) {
088                    if (searchKey.size() > 1) {
089                            searchKey.getSearchKeys().remove(new AllKey());
090                    }
091            }
092    
093            protected String parseCharset(LinkedList<Token> tokens) {
094                    Token token = tokens.peek();
095                    if ("CHARSET".equals(token.value.toUpperCase())) {
096                            tokens.remove();
097                            return MimeUtility.javaCharset(tokens.remove().value);
098                    } else {
099                            return null;
100                    }
101            }
102            
103            private SearchKey decodeSearchKey(LinkedList<Token> tokens, String charset)
104                            throws Exception {
105                    Token token = tokens.peek();
106                    SearchKey key = null;
107                    if (token.type == Token.Type.KEYWORD) {
108                            tokens.remove();
109                            String value = token.value.toUpperCase();
110                            if ("ALL".equals(value)) {
111                                    key = new AllKey();
112                            } else if ("ANSWERED".equals(value)) {
113                                    key = new FlagKey(Flags.Flag.ANSWERED, true);
114                            } else if ("BCC".equals(value)) {
115                                    key = new RecipientStringKey(Message.RecipientType.BCC, decode(
116                                                    (token = tokens.remove()).value, charset));
117                            } else if ("BEFORE".equals(value)) {
118                                    key = new InternalDateKey(ComparisonTerm.LT, DecoderUtils
119                                                    .parseDate((token = tokens.remove()).value));
120                            } else if ("BODY".equals(value)) {
121                                    key = new TextKey(decode((token = tokens.remove()).value,
122                                                    charset), false);
123                            } else if ("CC".equals(value)) {
124                                    key = new RecipientStringKey(Message.RecipientType.CC, decode(
125                                                    (token = tokens.remove()).value, charset));
126                            } else if ("DELETED".equals(value)) {
127                                    key = new FlagKey(Flags.Flag.DELETED, true);
128                            } else if ("FLAGGED".equals(value)) {
129                                    key = new FlagKey(Flags.Flag.FLAGGED, true);
130                            } else if ("FROM".equals(value)) {
131                                    key = new FromStringKey(decode((token = tokens.remove()).value,
132                                                    charset));
133                            } else if ("KEYWORD".equals(value)) {
134                                    key = new KeywordKey(decode((token = tokens.remove()).value,
135                                                    charset), true);
136                            } else if ("NEW".equals(value)) {
137                                    key = new AndKey(new FlagKey(Flags.Flag.RECENT, true),
138                                                    new FlagKey(Flags.Flag.SEEN, false));
139                            } else if ("OLD".equals(value)) {
140                                    key = new FlagKey(Flags.Flag.RECENT, false);
141                            } else if ("ON".equals(value)) {
142                                    key = new InternalDateKey(ComparisonTerm.EQ, DecoderUtils
143                                                    .parseDate((token = tokens.remove()).value));
144                            } else if ("RECENT".equals(value)) {
145                                    key = new FlagKey(Flags.Flag.RECENT, true);
146                            } else if ("SEEN".equals(value)) {
147                                    key = new FlagKey(Flags.Flag.SEEN, true);
148                            } else if ("SINCE".equals(value)) {
149                                    key = new InternalDateKey(ComparisonTerm.GE, DecoderUtils
150                                                    .parseDate((token = tokens.remove()).value));
151                            } else if ("SUBJECT".equals(value)) {
152                                    key = new SubjectKey(decode((token = tokens.remove()).value,
153                                                    charset));
154                            } else if ("TEXT".equals(value)) {
155                                    key = new TextKey((token = tokens.remove()).value);
156                            } else if ("TO".equals(value)) {
157                                    key = new RecipientStringKey(Message.RecipientType.TO, decode(
158                                                    (token = tokens.remove()).value, charset));
159                            } else if ("UNANSWERED".equals(value)) {
160                                    key = new FlagKey(Flags.Flag.ANSWERED, false);
161                            } else if ("UNDELETED".equals(value)) {
162                                    key = new FlagKey(Flags.Flag.DELETED, false);
163                            } else if ("UNFLAGGED".equals(value)) {
164                                    key = new FlagKey(Flags.Flag.FLAGGED, false);
165                            } else if ("UNKEYWORD".equals(value)) {
166                                    key = new KeywordKey((token = tokens.remove()).value, false);
167                            } else if ("UNSEEN".equals(value)) {
168                                    key = new FlagKey(Flags.Flag.SEEN, false);
169                            } else if ("DRAFT".equals(value)) {
170                                    key = new FlagKey(Flags.Flag.DRAFT, true);
171                            } else if ("HEADER".equals(value)) {
172                                    String headerName = tokens.remove().value;
173                                    key = new HeaderKey(headerName, (token = tokens.remove()).value);
174                            } else if ("LARGER".equals(value)) {
175                                    key = new SizeKey(ComparisonTerm.GT, Integer
176                                                    .parseInt((token = tokens.remove()).value));
177                            } else if ("NOT".equals(value)) {
178                                    key = new NotKey(decodeSearchKey(tokens, charset));
179                            } else if ("OR".equals(value)) {
180                                    SearchKey k1 = decodeSearchKey(tokens, charset);
181                                    SearchKey k2 = decodeSearchKey(tokens, charset);
182                                    key = new OrKey(k1, k2);
183                            } else if ("SENTBEFORE".equals(value)) {
184                                    key = new SentDateKey(ComparisonTerm.LT, DecoderUtils
185                                                    .parseDate((token = tokens.remove()).value));
186                            } else if ("SENTON".equals(value)) {
187                                    key = new SentDateKey(ComparisonTerm.EQ, DecoderUtils
188                                                    .parseDate((token = tokens.remove()).value));
189                            } else if ("SENTSINCE".equals(value)) {
190                                    key = new SentDateKey(ComparisonTerm.GE, DecoderUtils
191                                                    .parseDate((token = tokens.remove()).value));
192                            } else if ("SMALLER".equals(value)) {
193                                    key = new SizeKey(ComparisonTerm.LT, Integer
194                                                    .parseInt((token = tokens.remove()).value));
195                            } else if ("UID".equals(value)) {
196                                    SequenceRange[] sequenceSet = parseSequenceSet(tokens);
197                                    key = new SequenceKey(sequenceSet, true);
198                            } else if ("UNDRAFT".equals(value)) {
199                                    key = new FlagKey(Flags.Flag.DRAFT, false);
200                            }
201                    } else if (token.type == Token.Type.SEQ_NUMBER
202                                    || token.type == Token.Type.SEQ_RANGE) {
203                            SequenceRange[] sequenceSet = parseSequenceSet(tokens);
204                            key = new SequenceKey(sequenceSet);
205                    } else if (token.type == Token.Type.LPAREN) {
206                            tokens.remove();
207                            key = new AndKey();
208                            do {
209                                    if (token.type == Token.Type.RPAREN) {
210                                            tokens.remove();
211                                            break;
212                                    }
213                                    ((AndKey) key).addKey(decodeSearchKey(tokens, charset));
214                                    token = tokens.peek();
215                            } while (true);
216                    } else {
217                            throw new Exception("Unexpected token: " + token);
218                    }
219                    return key;
220            }
221            
222            private static String decode(String s, String charset) {
223                    if (charset != null) {
224                            try {
225                                    if (!MailUtils.isAscii(s) && !"ISO8859_1".equals(charset)) {
226                                            return new String(s.getBytes("ISO8859_1"), charset);
227                                    }
228                            } catch (UnsupportedEncodingException e) {
229                            }
230                    }
231                    return s;
232            }
233    
234    }