/** * This method parses POP3 commands read off the wire in handleConnection. * Actual processing of the command (possibly including additional back and * forth communication with the client) is delegated to one of a number of * command specific handler methods. The primary purpose of this method is * to parse the raw command string to determine exactly which handler should * be called. It returns true if expecting additional commands, false otherwise. * * @return whether additional commands are expected. */ public boolean handleRequest(InputStream input, OutputStream output, ImapSession session) throws ProtocolException { ImapRequestLineReader request = new ImapRequestLineReader(input, output); try { request.nextChar(); } catch (ProtocolException e) { return false; } ImapResponse response = new ImapResponse(output); doProcessRequest(request, response, session); // Consume the rest of the line, throwing away any extras. This allows us // to clean up after a protocol error. request.consumeLine(); return true; }
public long consumeLong(ImapRequestLineReader request) throws ProtocolException { StringBuilder atom = new StringBuilder(); char next = request.nextWordChar(); while (Character.isDigit(next)) { atom.append(next); request.consume(); next = request.nextChar(); } return Long.parseLong(atom.toString()); }
/** * Moves the request line reader to end of the line, checking that no non-space * character are found. * * @throws ProtocolException If more non-space tokens are found in this line, * or the end-of-file is reached. */ public void eol() throws ProtocolException { char next = nextChar(); // Ignore trailing spaces. while (next == ' ') { consume(); next = nextChar(); } // handle DOS and unix end-of-lines if (next == '\r') { consume(); next = nextChar(); } // Check if we found extra characters. if (next != '\n') { throw new ProtocolException("Expected end-of-line, found more character(s): "+next); } dumpLine(); }
protected byte[] consumeLiteralAsBytes(ImapRequestLineReader request) throws ProtocolException { // The 1st character must be '{' consumeChar(request, '{'); StringBuilder digits = new StringBuilder(); char next = request.nextChar(); while (next != '}' && next != '+') { digits.append(next); request.consume(); next = request.nextChar(); } // If the number is *not* suffixed with a '+', we *are* using a synchronized literal, // and we need to send command continuation request before reading data. boolean synchronizedLiteral = true; // '+' indicates a non-synchronized literal (no command continuation request) if (next == '+') { synchronizedLiteral = false; consumeChar(request, '+'); } // Consume the '}' and the newline consumeChar(request, '}'); consumeCRLF(request); if (synchronizedLiteral) { request.commandContinuationRequest(); } int size = Integer.parseInt(digits.toString()); byte[] buffer = new byte[size]; request.read(buffer); return buffer; }
/** * If the next character in the request is a '"', tries to read * a DateTime argument. If not, returns null. */ public Date optionalDateTime(ImapRequestLineReader request) throws ProtocolException { char next = request.nextWordChar(); if (next == '"') { return dateTime(request); } else { return null; } }
StoreDirective storeDirective(ImapRequestLineReader request) throws ProtocolException { int sign = 0; boolean silent = false; char next = request.nextWordChar(); if (next == '+') { sign = 1; request.consume(); } else if (next == '-') { sign = -1; request.consume(); } else { sign = 0; } String directive = consumeWord(request, new NoopCharValidator()); if ("FLAGS".equalsIgnoreCase(directive)) { silent = false; } else if ("FLAGS.SILENT".equalsIgnoreCase(directive)) { silent = true; } else { throw new ProtocolException("Invalid Store Directive: '" + directive + '\''); } return new StoreDirective(sign, silent); } }
/** * Consumes the current character in the reader, so that subsequent calls to the request will * provide a new character. This method does *not* read the new character, or check if * such a character exists. If no current character has been seen, the method moves to * the next character, consumes it, and moves on to the subsequent one. * * @throws ProtocolException if a the current character can't be obtained (eg we're at * end-of-file). */ public char consume() throws ProtocolException { char current = nextChar(); nextSeen = false; nextChar = 0; return current; }
/** * Consumes the request up to and including the eno-of-line. * * @param request The request * @throws ProtocolException If characters are encountered before the endLine. */ public void endLine(ImapRequestLineReader request) throws ProtocolException { request.eol(); }
/** * Consumes the next character in the request, checking that it matches the * expected one. This method should be used when the */ protected void consumeChar(ImapRequestLineReader request, char expected) throws ProtocolException { char consumed = request.consume(); if (consumed != expected) { throw new ProtocolException("Expected:'" + expected + "' found:'" + consumed + '\''); } }
/** * Reads the next character in the current line. This method will continue to return * the same character until the {@link #consume()} method is called. * * @return The next character. * @throws ProtocolException If the end-of-stream is reached. */ public char nextChar() throws ProtocolException { if (!nextSeen) { try { final int read = input.read(); final char c = (char) read; buf.append(c); if(read == -1) { dumpLine(); throw new ProtocolException("End of stream"); } nextChar = c; nextSeen = true; } catch (IOException e) { throw new ProtocolException("Error reading from stream.", e); } } return nextChar; }
/** * If the next character in the request is a '(', tries to read * a "flag_list" argument from the request. If not, returns a * MessageFlags with no flags set. */ public Flags optionalAppendFlags(ImapRequestLineReader request) throws ProtocolException { char next = request.nextWordChar(); if (next == '(') { return flagList(request); } else { return null; } }
/** * Consumes a CRLF from the request. * TODO we're being liberal, the spec insists on \r\n for new lines. * * @param request the imap request * @throws ProtocolException */ private void consumeCRLF(ImapRequestLineReader request) throws ProtocolException { char next = request.nextChar(); if (next != '\n') { consumeChar(request, '\r'); } consumeChar(request, '\n'); }
/** * Reads the next "word from the request, comprising all characters up to the next SPACE. * Characters are tested by the supplied CharacterValidator, and an exception is thrown * if invalid characters are encountered. */ protected String consumeWord(ImapRequestLineReader request, CharacterValidator validator) throws ProtocolException { StringBuilder atom = new StringBuilder(); char next = request.nextWordChar(); while (!isWhitespace(next)) { if (validator.isValid(next)) { atom.append(next); request.consume(); } else { throw new ProtocolException("Invalid character: '" + next + '\''); } next = request.nextChar(); } return atom.toString(); }
/** * Reads the next regular, non-space character in the current line. Spaces are skipped * over, but end-of-line characters will cause a {@link ProtocolException} to be thrown. * This method will continue to return * the same character until the {@link #consume()} method is called. * * @return The next non-space character. * @throws ProtocolException If the end-of-line or end-of-stream is reached. */ public char nextWordChar() throws ProtocolException { char next = nextChar(); while (next == ' ') { consume(); next = nextChar(); } if (next == '\r' || next == '\n') { throw new ProtocolException("Missing argument."); } return next; }
/** * Reads a "date-time" argument from the request. */ public Date dateTime(ImapRequestLineReader request) throws ProtocolException { char next = request.nextWordChar(); String dateString; // From https://tools.ietf.org/html/rfc3501 : // date-time = DQUOTE date-day-fixed "-" date-month "-" date-year // SP time SP zone DQUOTE // zone = ("+" / "-") 4DIGIT if (next == '"') { dateString = consumeQuoted(request); } else { throw new ProtocolException("DateTime values must be quoted."); } try { // You can use Z or zzzz return new SimpleDateFormat("dd-MMM-yyyy hh:mm:ss Z", Locale.US).parse(dateString); } catch (ParseException e) { throw new ProtocolException("Invalid date format <" + dateString + ">, should comply to dd-MMM-yyyy hh:mm:ss Z"); } }
private Partial parsePartial(ImapRequestLineReader command) throws ProtocolException { consumeChar(command, '<'); int size = (int) consumeLong(command); // Assume <start> int start = 0; if (command.nextChar() == '.') { consumeChar(command, '.'); start = size; // Assume <start.size> , so switch fields size = (int) consumeLong(command); } consumeChar(command, '>'); return Partial.as(start, size); }
char next = request.nextChar(); StringBuilder sb = new StringBuilder(); boolean quoted = false; request.consume(); next = request.nextChar(); if (next == '\"') { quoted = !quoted; next = request.nextChar(); while (next == CHR_SPACE || Character.isDigit(next) || next == ':') { request.consume(); sb.append(next); next = request.nextChar(); request.nextWordChar(); // Skip spaces String c = this.atom(request); log.debug("Searching with given CHARSET <{}>", c); next = request.nextWordChar(); if (next == '{') { String textOfCharset = new String(consumeLiteralAsBytes(request), charset); request.consume(); // \n next = request.nextChar(); final Integer capacity = Integer.valueOf(sb.substring(1, sb.length() - 1)); ByteBuffer bb = ByteBuffer.allocate(capacity); while (next != CHR_CR) { request.consume(); // \n