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 }