org.apache.james.imap.decode.parser.FetchCommandParser Maven / Gradle / Ivy
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/
package org.apache.james.imap.decode.parser;
import static org.apache.james.imap.api.message.FetchData.Item.BODY;
import static org.apache.james.imap.api.message.FetchData.Item.BODY_STRUCTURE;
import static org.apache.james.imap.api.message.FetchData.Item.EMAILID;
import static org.apache.james.imap.api.message.FetchData.Item.ENVELOPE;
import static org.apache.james.imap.api.message.FetchData.Item.FLAGS;
import static org.apache.james.imap.api.message.FetchData.Item.INTERNAL_DATE;
import static org.apache.james.imap.api.message.FetchData.Item.MODSEQ;
import static org.apache.james.imap.api.message.FetchData.Item.SAVEDATE;
import static org.apache.james.imap.api.message.FetchData.Item.SIZE;
import static org.apache.james.imap.api.message.FetchData.Item.THREADID;
import static org.apache.james.imap.api.message.FetchData.Item.UID;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import org.apache.james.imap.api.ImapConstants;
import org.apache.james.imap.api.ImapMessage;
import org.apache.james.imap.api.Tag;
import org.apache.james.imap.api.display.HumanReadableText;
import org.apache.james.imap.api.message.BodyFetchElement;
import org.apache.james.imap.api.message.FetchData;
import org.apache.james.imap.api.message.IdRange;
import org.apache.james.imap.api.message.SectionType;
import org.apache.james.imap.api.message.response.StatusResponseFactory;
import org.apache.james.imap.api.process.ImapSession;
import org.apache.james.imap.decode.DecodingException;
import org.apache.james.imap.decode.FetchPartPathDecoder;
import org.apache.james.imap.decode.ImapRequestLineReader;
import org.apache.james.imap.decode.ImapRequestLineReader.StringMatcherCharacterValidator;
import org.apache.james.imap.message.request.FetchRequest;
import com.google.common.base.CharMatcher;
/**
* Parse FETCH commands
*/
public class FetchCommandParser extends AbstractUidCommandParser {
private static final String CHANGEDSINCE = "CHANGEDSINCE";
private static final String VANISHED = "VANISHED";
private static final CharMatcher CLOSING_BRACKET = CharMatcher.is(']');
private static final CharMatcher NEXT_ELEMENT_END = CharMatcher.anyOf(" [)\r\n");
@Inject
public FetchCommandParser(StatusResponseFactory statusResponseFactory) {
super(ImapConstants.FETCH_COMMAND, statusResponseFactory);
}
/**
* Create a {@link FetchData} by reading from the
* {@link ImapRequestLineReader}
*
* @return fetchData
*/
private FetchData fetchRequest(ImapRequestLineReader request, boolean useUid) throws DecodingException {
FetchData.Builder fetch = FetchData.builder();
if (useUid) {
fetch.fetch(UID);
}
char next = nextNonSpaceChar(request);
if (request.nextChar() == '(') {
request.consumeChar('(');
next = nextNonSpaceChar(request);
while (next != ')') {
addNextElement(request, fetch);
next = nextNonSpaceChar(request);
}
request.consumeChar(')');
next = nextNonSpaceChar(request);
if (next == '(') {
request.consumeChar('(');
while (parseModifier(request, fetch)) {
nextNonSpaceChar(request);
}
request.consumeChar(')');
}
} else {
addNextElement(request, fetch);
}
return fetch.build();
}
private boolean parseModifier(ImapRequestLineReader request, FetchData.Builder fetch) throws DecodingException {
char next = request.nextChar();
switch (next) {
case 'C':
// Now check for the CHANGEDSINCE option which is part of CONDSTORE
request.consumeWord(StringMatcherCharacterValidator.ignoreCase(CHANGEDSINCE));
fetch.changedSince(request.number(true));
return true;
case 'V':
// Check for the VANISHED option which is part of QRESYNC
request.consumeWord(StringMatcherCharacterValidator.ignoreCase(VANISHED), true);
fetch.vanished(true);
return true;
default:
return false;
}
}
private void addNextElement(ImapRequestLineReader reader, FetchData.Builder fetch) throws DecodingException {
String name = reader.readUntil(NEXT_ELEMENT_END);
char next = reader.nextChar();
// Simple elements with no '[]' parameters.
if (next != '[') {
addNextName(fetch, name);
} else {
reader.consumeChar('[');
String parameter = reader.readUntil(CLOSING_BRACKET);
reader.consumeChar(']');
Long firstOctet;
Long numberOfOctets;
if (reader.nextChar() == '<') {
reader.consumeChar('<');
firstOctet = reader.number();
if (reader.nextChar() == '.') {
reader.consumeChar('.');
numberOfOctets = reader.nzNumber();
} else {
numberOfOctets = null;
}
reader.consumeChar('>');
} else {
firstOctet = null;
numberOfOctets = null;
}
BodyFetchElement bodyFetchElement = createBodyElement(parameter, firstOctet, numberOfOctets);
boolean isPeek = isPeek(name);
fetch.add(bodyFetchElement, isPeek);
}
}
private FetchData.Builder addNextName(FetchData.Builder fetch, String name) throws DecodingException {
String capitalizedName = name.toUpperCase(Locale.US);
switch (capitalizedName) {
case "FAST":
return fetch.fetch(FLAGS, INTERNAL_DATE, SIZE);
case "FULL":
return fetch.fetch(FLAGS, INTERNAL_DATE, SIZE, ENVELOPE, BODY);
case "ALL":
return fetch.fetch(FLAGS, INTERNAL_DATE, SIZE, ENVELOPE);
case "FLAGS":
return fetch.fetch(FLAGS);
case "RFC822.SIZE":
return fetch.fetch(SIZE);
case "ENVELOPE":
return fetch.fetch(ENVELOPE);
case "INTERNALDATE":
return fetch.fetch(INTERNAL_DATE);
case "BODY":
return fetch.fetch(BODY);
case "BODYSTRUCTURE":
return fetch.fetch(BODY_STRUCTURE);
case "UID":
return fetch.fetch(UID);
case "RFC822":
return fetch.add(BodyFetchElement.createRFC822(), false);
case "RFC822.HEADER":
return fetch.add(BodyFetchElement.createRFC822Header(), true);
case "RFC822.TEXT":
return fetch.add(BodyFetchElement.createRFC822Text(), false);
case "MODSEQ":
return fetch.fetch(MODSEQ);
case "EMAILID":
return fetch.fetch(EMAILID);
case "THREADID":
return fetch.fetch(THREADID);
case "SAVEDATE":
return fetch.fetch(SAVEDATE);
default:
throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Invalid fetch attribute: " + name);
}
}
private boolean isPeek(String name) throws DecodingException {
switch (name.toUpperCase(Locale.US)) {
case "BODY":
return false;
case "BODY.PEEK":
return true;
default:
throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Invalid fetch attibute: " + name + "[]");
}
}
private BodyFetchElement createBodyElement(String parameter, Long firstOctet, Long numberOfOctets) throws DecodingException {
String responseName = "BODY[" + parameter + "]";
FetchPartPathDecoder decoder = new FetchPartPathDecoder();
decoder.decode(parameter);
SectionType sectionType = decoder.getSpecifier();
List names = decoder.getNames();
int[] path = decoder.getPath();
return new BodyFetchElement(responseName, sectionType, path, names, firstOctet, numberOfOctets);
}
private char nextNonSpaceChar(ImapRequestLineReader request) throws DecodingException {
char next = request.nextChar();
while (next == ' ') {
request.consume();
next = request.nextChar();
}
return next;
}
@Override
protected ImapMessage decode(ImapRequestLineReader request, Tag tag, boolean useUids, ImapSession session) throws DecodingException {
IdRange[] idSet = request.parseIdRange(session);
FetchData fetch = fetchRequest(request, useUids);
// Check if we have VANISHED and and UID FETCH as its only allowed there
//
// See RFC5162 3.2. VANISHED UID FETCH Modifier
if (fetch.getVanished() && !useUids) {
throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "VANISHED only allowed in UID FETCH");
}
request.eol();
return new FetchRequest(useUids, idSet, fetch, tag);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy