001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.net.imap; 019 020import java.io.IOException; 021import java.util.regex.Matcher; 022import java.util.regex.Pattern; 023 024import org.apache.commons.net.MalformedServerReplyException; 025 026/** 027 * IMAPReply stores IMAP reply code constants. 028 */ 029 030public final class IMAPReply { 031 /** The reply code indicating success of an operation. */ 032 public static final int OK = 0; 033 034 /** The reply code indicating failure of an operation. */ 035 public static final int NO = 1; 036 037 /** The reply code indicating command rejection. */ 038 public static final int BAD = 2; 039 040 /** The reply code indicating command continuation. */ 041 public static final int CONT = 3; 042 043 /** 044 * The reply code indicating a partial response. This is used when a chunk listener is registered and the listener requests that the reply lines are cleared 045 * on return. 046 * 047 * @since 3.4 048 */ 049 public static final int PARTIAL = 3; 050 051 /** The IMAP reply String indicating success of an operation. */ 052 private static final String IMAP_OK = "OK"; 053 054 /** The IMAP reply String indicating failure of an operation. */ 055 private static final String IMAP_NO = "NO"; 056 057 /** The IMAP reply String indicating command rejection. */ 058 private static final String IMAP_BAD = "BAD"; 059 060 // Start of line for untagged replies 061 private static final String IMAP_UNTAGGED_PREFIX = "* "; 062 063 // Start of line for continuation replies 064 private static final String IMAP_CONTINUATION_PREFIX = "+"; 065 066 private static final String TAGGED_RESPONSE = "^\\w+ (\\S+).*"; // TODO perhaps be less strict on tag match? 067 068 // tag cannot contain: + ( ) { SP CTL % * " \ ] 069 private static final Pattern TAGGED_PATTERN = Pattern.compile(TAGGED_RESPONSE); 070 071 private static final String UNTAGGED_RESPONSE = "^\\* (\\S+).*"; 072 073 private static final Pattern UNTAGGED_PATTERN = Pattern.compile(UNTAGGED_RESPONSE); 074 private static final Pattern LITERAL_PATTERN = Pattern.compile("\\{(\\d+)\\}$"); // {dd} 075 076 /** 077 * Intepret the String reply code - OK, NO, BAD - in a tagged response as a integer. 078 * 079 * @param line the tagged line to be checked 080 * @return {@link #OK} or {@link #NO} or {@link #BAD} or {@link #CONT} 081 * @throws IOException if the input has an unexpected format 082 */ 083 public static int getReplyCode(final String line) throws IOException { 084 return getReplyCode(line, TAGGED_PATTERN); 085 } 086 087 // Helper method to process both tagged and untagged replies. 088 private static int getReplyCode(final String line, final Pattern pattern) throws IOException { 089 if (isContinuation(line)) { 090 return CONT; 091 } 092 final Matcher m = pattern.matcher(line); 093 if (m.matches()) { // TODO would lookingAt() be more efficient? If so, then drop trailing .* from patterns 094 final String code = m.group(1); 095 if (code.equals(IMAP_OK)) { 096 return OK; 097 } 098 if (code.equals(IMAP_BAD)) { 099 return BAD; 100 } 101 if (code.equals(IMAP_NO)) { 102 return NO; 103 } 104 } 105 throw new MalformedServerReplyException("Received unexpected IMAP protocol response from server: '" + line + "'."); 106 } 107 108 /** 109 * Intepret the String reply code - OK, NO, BAD - in an untagged response as a integer. 110 * 111 * @param line the untagged line to be checked 112 * @return {@link #OK} or {@link #NO} or {@link #BAD} or {@link #CONT} 113 * @throws IOException if the input has an unexpected format 114 */ 115 public static int getUntaggedReplyCode(final String line) throws IOException { 116 return getReplyCode(line, UNTAGGED_PATTERN); 117 } 118 119 /** 120 * Checks if the reply line is a continuation, i.e. starts with "+" 121 * 122 * @param replyCode the code to be checked 123 * @return {@code true} if the response was a continuation 124 */ 125 public static boolean isContinuation(final int replyCode) { 126 return replyCode == CONT; 127 } 128 129 /** 130 * Checks if the reply line is a continuation, i.e. starts with "+" 131 * 132 * @param line the line to be checked 133 * @return {@code true} if the line is untagged 134 */ 135 public static boolean isContinuation(final String line) { 136 return line.startsWith(IMAP_CONTINUATION_PREFIX); 137 } 138 139 /** 140 * Checks whether the reply code indicates success or not 141 * 142 * @param replyCode the code to check 143 * @return {@code true} if the code equals {@link #OK} 144 */ 145 public static boolean isSuccess(final int replyCode) { 146 return replyCode == OK; 147 } 148 149 /** 150 * Checks if the reply line is untagged - e.g. "* OK ..." 151 * 152 * @param line to be checked 153 * @return {@code true} if the line is untagged 154 */ 155 public static boolean isUntagged(final String line) { 156 return line.startsWith(IMAP_UNTAGGED_PREFIX); 157 } 158 159 /** 160 * Checks if the line introduces a literal, i.e. ends with {dd} 161 * 162 * @param line the line to check 163 * 164 * @return the literal count, or -1 if there was no literal. 165 */ 166 public static int literalCount(final String line) { 167 final Matcher m = LITERAL_PATTERN.matcher(line); 168 if (m.find()) { 169 return Integer.parseInt(m.group(1)); // Should always parse because we matched \d+ 170 } 171 return -1; 172 } 173 174 // Cannot be instantiated. 175 private IMAPReply() { 176 } 177 178} 179 180/* kate: indent-width 4; replace-tabs on; */