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.util.ArrayList;
019    import java.util.LinkedList;
020    import java.util.List;
021    
022    import javax.mail.FetchProfile;
023    
024    import com.hs.mail.imap.message.BodyFetchItem;
025    import com.hs.mail.imap.message.FetchData;
026    import com.hs.mail.imap.message.SequenceRange;
027    import com.hs.mail.imap.message.request.FetchRequest;
028    import com.hs.mail.imap.message.request.ImapRequest;
029    import com.hs.mail.imap.parser.Token;
030    import com.hs.mail.imap.server.codec.ImapMessage;
031    
032    /**
033     * 
034     * @author Won Chul Doh
035     * @since Jan 29, 2010
036     *
037     */
038    public class FetchRequestBuilder extends AbstractUidRequestBuilder {
039    
040            @Override
041            public ImapRequest createRequest(String tag, String command,
042                            ImapMessage message, boolean useUID) {
043                    LinkedList<Token> tokens = message.getTokens();
044                    SequenceRange[] sequenceSet = parseSequenceSet(tokens);
045                    FetchProfile fp = decodeFetchData(tokens);
046                    if (useUID && !fp.contains(FetchData.FetchProfileItem.UID)) {
047                            // RFC2060 says that a UID FETCH must include a UID in the response
048                            // even if the fetch did not ask for it.
049                            fp.add(FetchData.FetchProfileItem.UID);
050                    }
051                    return new FetchRequest(tag, command, sequenceSet, fp, useUID);
052            }
053            
054            private String parseSection(LinkedList<Token> tokens, BodyFetchItem item) {
055                    StringBuilder sb = new StringBuilder();
056                    tokens.remove(); // consume '['
057                    Token token = tokens.remove();
058                    if (token.type == Token.Type.NUMBER) {
059                            item.setSectionType(BodyFetchItem.CONTENT);
060                            do {
061                                    item.addPath(Integer.parseInt(token.value));
062                                    sb.append(token.value);
063                                    if (!".".equals((token = tokens.remove()).value))
064                                            break;
065                                    sb.append(".");
066                            } while ((token = tokens.remove()).type == Token.Type.NUMBER);
067                    }
068                    if (!"]".equals(token.value)) {
069                            sb.append(token.value);
070                            if ("HEADER.FIELDS.NOT".equals(token.value)
071                                            || "HEADER.FIELDS".equals(token.value)) {
072                                    item.setSectionType(
073                                                    "HEADER.FIELDS".equals(token.value) ? BodyFetchItem.HEADER_FIELDS
074                                                                    : BodyFetchItem.HEADER_FIELDS_NOT);
075                                    item.setHeaders(parseHeaderList(sb, tokens));
076                            } else if ("HEADER".equals(token.value)) {
077                                    item.setSectionType(BodyFetchItem.HEADER);
078                            } else if ("TEXT".equals(token.value)) {
079                                    item.setSectionType(BodyFetchItem.TEXT);
080                            } else if ("MIME".equals(token.value)) {
081                                    item.setSectionType(BodyFetchItem.MIME);
082                            }
083                            token = tokens.remove(); // consume ']'
084                            assert "]".equals(token.value);
085                    } else {
086                            // An empty section specification refers to the entire message,
087                            // including the header.
088                            item.setSectionType(BodyFetchItem.CONTENT);
089                    }
090                    return sb.toString();
091            }
092            
093            private String[] parseHeaderList(StringBuilder sb, LinkedList<Token> tokens) {
094                    Token token = tokens.peek();
095                    List<String> headers = new ArrayList<String>();
096                    if (token.type == Token.Type.LPAREN) {
097                            sb.append(" (");
098                            tokens.remove(); // consume '('
099                            do {
100                                    if ((token = tokens.remove()).type == Token.Type.RPAREN) {
101                                            sb.append(')');
102                                            break;
103                                    }
104                                    if (headers.size() > 0)
105                                            sb.append(' ');
106                                    headers.add(token.value);
107                                    sb.append(token.value);
108                            } while (true);
109                    }
110                    return headers.toArray(new String[headers.size()]);
111            }
112            
113            private void parsePartial(LinkedList<Token> tokens, BodyFetchItem item) {
114                    Token token = tokens.peek();
115                    if ("<".equals(token.value)) {
116                            tokens.remove(); // consume '<'
117                            item.setFirstOctet(Long.parseLong((token = tokens.remove()).value));
118                            token = tokens.remove(); // consume '.'
119                            assert ".".equals(token.value);
120                            item.setNumberOfOctets(Long
121                                            .parseLong((token = tokens.remove()).value));
122                            token = tokens.remove(); // consume '>'
123                            assert ">".equals(token.value);
124                    }
125            }
126            
127            private FetchProfile decodeFetchData(LinkedList<Token> tokens) {
128                    FetchProfile fp = new FetchProfile();
129                    Token token = tokens.peek();
130                    if (token.type == Token.Type.LPAREN) {
131                            tokens.remove(); // consume '('
132                            do {
133                                    if ((token = tokens.peek()).type == Token.Type.RPAREN) {
134                                            tokens.remove(); // consume ')'
135                                            break;
136                                    }
137                                    decodeFetchAtt(tokens, fp);
138                            } while (true);
139                    } else {
140                            decodeFetchAtt(tokens, fp);
141                    }
142                    return fp;
143            }
144            
145            private FetchProfile decodeFetchAtt(LinkedList<Token> tokens,
146                            FetchProfile fp) {
147                    String value = (tokens.remove()).value.toUpperCase();
148                    Token next = tokens.peek();
149                    if (next == null || !"[".equals(next.value)) {
150                            if ("ALL".equals(value)) {
151                                    // equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)
152                                    fp.add(FetchProfile.Item.FLAGS);
153                                    fp.add(FetchData.FetchProfileItem.INTERNALDATE);
154                                    fp.add(FetchData.FetchProfileItem.SIZE);
155                                    fp.add(FetchProfile.Item.ENVELOPE);
156                            } else if ("FAST".equals(value)) {
157                                    // equivalent to: (FLAGS INTERNALDATE RFC822.SIZE)
158                                    fp.add(FetchProfile.Item.FLAGS);
159                                    fp.add(FetchData.FetchProfileItem.INTERNALDATE);
160                                    fp.add(FetchData.FetchProfileItem.SIZE);
161                            } else if ("FULL".equals(value)) {
162                                    // equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)
163                                    fp.add(FetchProfile.Item.FLAGS);
164                                    fp.add(FetchData.FetchProfileItem.INTERNALDATE);
165                                    fp.add(FetchData.FetchProfileItem.SIZE);
166                                    fp.add(FetchProfile.Item.ENVELOPE);
167                                    fp.add(FetchData.FetchProfileItem.BODY);
168                            } else if ("FLAGS".equals(value)) {
169                                    fp.add(FetchProfile.Item.FLAGS);
170                            } else if ("INTERNALDATE".equals(value)) {
171                                    fp.add(FetchData.FetchProfileItem.INTERNALDATE);
172                            } else if ("ENVELOPE".equals(value)) {
173                                    fp.add(FetchProfile.Item.ENVELOPE);
174                            } else if ("RFC822".equals(value)) {
175                                    // equivalent to: BODY[]
176                                    fp.add(new BodyFetchItem("RFC822", false, BodyFetchItem.CONTENT));
177                            } else if ("RFC822.HEADER".equals(value)) {
178                                    // equivalent to: BODY.PEEK[HEADER]
179                                    fp.add(new BodyFetchItem("RFC822.HEADER", true, BodyFetchItem.HEADER));
180                            } else if ("RFC822.SIZE".equals(value)) {
181                                    fp.add(FetchData.FetchProfileItem.SIZE);
182                            } else if ("RFC822.TEXT".equals(value)) {
183                                    // equivalent to: BODY[TEXT]
184                                    fp.add(new BodyFetchItem("RFC822.TEXT", false, BodyFetchItem.TEXT));
185                            } else if ("BODY".equals(value)) {
186                                    fp.add(FetchData.FetchProfileItem.BODY);
187                            } else if ("BODYSTRUCTURE".equals(value)) {
188                                    fp.add(FetchData.FetchProfileItem.BODYSTRUCTURE);
189                            } else if ("UID".equals(value)) {
190                                    fp.add(FetchData.FetchProfileItem.UID);
191                            }
192                    } else {
193                            // BODY || BODY.PEEK
194                            BodyFetchItem body = new BodyFetchItem("BODY", !"BODY".equals(value));
195                            String parameter = parseSection(tokens, body);
196                            parsePartial(tokens, body);
197                            body.setName("BODY[" + parameter + "]");
198                            fp.add(body);
199                    }
200                    return fp;
201            }
202    
203    }