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{ 032 /** The reply code indicating success of an operation. */ 033 public static final int OK = 0; 034 035 /** The reply code indicating failure of an operation. */ 036 public static final int NO = 1; 037 038 /** The reply code indicating command rejection. */ 039 public static final int BAD = 2; 040 041 /** The reply code indicating command continuation. */ 042 public static final int CONT = 3; 043 044 /** 045 * The reply code indicating a partial response. 046 * This is used when a chunk listener is registered and the listener 047 * requests that the reply lines are cleared on return. 048 * @since 3.4 049 */ 050 public static final int PARTIAL = 3; 051 052 /** The IMAP reply String indicating success of an operation. */ 053 private static final String IMAP_OK = "OK"; 054 055 /** The IMAP reply String indicating failure of an operation. */ 056 private static final String IMAP_NO = "NO"; 057 058 /** The IMAP reply String indicating command rejection. */ 059 private static final String IMAP_BAD = "BAD"; 060 061 // Start of line for untagged replies 062 private static final String IMAP_UNTAGGED_PREFIX = "* "; 063 064 // Start of line for continuation replies 065 private static final String IMAP_CONTINUATION_PREFIX = "+"; 066 067 // Cannot be instantiated. 068 private IMAPReply() 069 {} 070 071 /** 072 * Checks if the reply line is untagged - e.g. "* OK ..." 073 * @param line to be checked 074 * @return {@code true} if the line is untagged 075 */ 076 public static boolean isUntagged(String line) { 077 return line.startsWith(IMAP_UNTAGGED_PREFIX); 078 } 079 080 /** 081 * Checks if the reply line is a continuation, i.e. starts with "+" 082 * @param line the line to be checked 083 * @return {@code true} if the line is untagged 084 */ 085 public static boolean isContinuation(String line) { 086 return line.startsWith(IMAP_CONTINUATION_PREFIX); 087 } 088 089 private static final String TAGGED_RESPONSE = "^\\w+ (\\S+).*"; // TODO perhaps be less strict on tag match? 090 // tag cannot contain: + ( ) { SP CTL % * " \ ] 091 private static final Pattern TAGGED_PATTERN = Pattern.compile(TAGGED_RESPONSE); 092 093 /** 094 * Intepret the String reply code - OK, NO, BAD - in a tagged response as a integer. 095 * 096 * @param line the tagged line to be checked 097 * @return {@link #OK} or {@link #NO} or {@link #BAD} or {@link #CONT} 098 * @throws IOException if the input has an unexpected format 099 */ 100 public static int getReplyCode(String line) throws IOException { 101 return getReplyCode(line, TAGGED_PATTERN); 102 } 103 104 private static final String UNTAGGED_RESPONSE = "^\\* (\\S+).*"; 105 private static final Pattern UNTAGGED_PATTERN = Pattern.compile(UNTAGGED_RESPONSE); 106 107 private static final Pattern LITERAL_PATTERN = Pattern.compile("\\{(\\d+)\\}$"); // {dd} 108 109 /** 110 * Checks if the line introduces a literal, i.e. ends with {dd} 111 * @param line the line to check 112 * 113 * @return the literal count, or -1 if there was no literal. 114 */ 115 public static int literalCount(String line) { 116 Matcher m = LITERAL_PATTERN.matcher(line); 117 if (m.find()) { 118 return Integer.parseInt(m.group(1)); // Should always parse because we matched \d+ 119 } 120 return -1; 121 } 122 123 /** 124 * Intepret the String reply code - OK, NO, BAD - in an untagged response as a integer. 125 * 126 * @param line the untagged line to be checked 127 * @return {@link #OK} or {@link #NO} or {@link #BAD} or {@link #CONT} 128 * @throws IOException if the input has an unexpected format 129 */ 130 public static int getUntaggedReplyCode(String line) throws IOException { 131 return getReplyCode(line, UNTAGGED_PATTERN); 132 } 133 134 // Helper method to process both tagged and untagged replies. 135 private static int getReplyCode(String line, Pattern pattern) throws IOException{ 136 if (isContinuation(line)) { 137 return CONT; 138 } 139 Matcher m = pattern.matcher(line); 140 if (m.matches()) { // TODO would lookingAt() be more efficient? If so, then drop trailing .* from patterns 141 String code = m.group(1); 142 if (code.equals(IMAP_OK)) { 143 return OK; 144 } 145 if (code.equals(IMAP_BAD)) { 146 return BAD; 147 } 148 if (code.equals(IMAP_NO)) { 149 return NO; 150 } 151 } 152 throw new MalformedServerReplyException( 153 "Received unexpected IMAP protocol response from server: '" + line + "'."); 154 } 155 156 /** 157 * Checks whether the reply code indicates success or not 158 * 159 * @param replyCode the code to check 160 * @return {@code true} if the code equals {@link #OK} 161 */ 162 public static boolean isSuccess(int replyCode) { 163 return replyCode == OK; 164 } 165 /** 166 * Checks if the reply line is a continuation, i.e. starts with "+" 167 * @param replyCode the code to be checked 168 * @return {@code true} if the response was a continuation 169 */ 170 public static boolean isContinuation(int replyCode) { 171 return replyCode == CONT; 172 } 173 174} 175 176/* kate: indent-width 4; replace-tabs on; */