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 }