1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.net.imap; 19 20 import java.io.IOException; 21 import java.util.regex.Matcher; 22 import java.util.regex.Pattern; 23 24 import org.apache.commons.net.MalformedServerReplyException; 25 26 /** 27 * IMAPReply stores IMAP reply code constants. 28 */ 29 30 public final class IMAPReply 31 { 32 /** The reply code indicating success of an operation. */ 33 public static final int OK = 0; 34 35 /** The reply code indicating failure of an operation. */ 36 public static final int NO = 1; 37 38 /** The reply code indicating command rejection. */ 39 public static final int BAD = 2; 40 41 /** The reply code indicating command continuation. */ 42 public static final int CONT = 3; 43 44 /** The IMAP reply String indicating success of an operation. */ 45 private static final String IMAP_OK = "OK"; 46 47 /** The IMAP reply String indicating failure of an operation. */ 48 private static final String IMAP_NO = "NO"; 49 50 /** The IMAP reply String indicating command rejection. */ 51 private static final String IMAP_BAD = "BAD"; 52 53 // Start of line for untagged replies 54 private static final String IMAP_UNTAGGED_PREFIX = "* "; 55 56 // Start of line for continuation replies 57 private static final String IMAP_CONTINUATION_PREFIX = "+"; 58 59 // Cannot be instantiated. 60 private IMAPReply() 61 {} 62 63 /** 64 * Checks if the reply line is untagged - e.g. "* OK ..." 65 * @param line to be checked 66 * @return {@code true} if the line is untagged 67 */ 68 public static boolean isUntagged(String line) { 69 return line.startsWith(IMAP_UNTAGGED_PREFIX); 70 } 71 72 /** 73 * Checks if the reply line is a continuation, i.e. starts with "+" 74 * @param line the line to be checked 75 * @return {@code true} if the line is untagged 76 */ 77 public static boolean isContinuation(String line) { 78 return line.startsWith(IMAP_CONTINUATION_PREFIX); 79 } 80 81 private static final String TAGGED_RESPONSE = "^\\w+ (\\S+).*"; // TODO perhaps be less strict on tag match? 82 // tag cannot contain: + ( ) { SP CTL % * " \ ] 83 private static final Pattern TAGGED_PATTERN = Pattern.compile(TAGGED_RESPONSE); 84 85 /** 86 * Intepret the String reply code - OK, NO, BAD - in a tagged response as a integer. 87 * 88 * @param line the tagged line to be checked 89 * @return {@link #OK} or {@link #NO} or {@link #BAD} or {@link #CONT} 90 * @throws IOException if the input has an unexpected format 91 */ 92 public static int getReplyCode(String line) throws IOException { 93 return getReplyCode(line, TAGGED_PATTERN); 94 } 95 96 private static final String UNTAGGED_RESPONSE = "^\\* (\\S+).*"; 97 private static final Pattern UNTAGGED_PATTERN = Pattern.compile(UNTAGGED_RESPONSE); 98 99 private static final Pattern LITERAL_PATTERN = Pattern.compile("\\{(\\d+)\\}$"); // {dd} 100 101 /** 102 * Checks if the line introduces a literal, i.e. ends with {dd} 103 * 104 * @return the literal count, or -1 if there was no literal. 105 */ 106 public static int literalCount(String line) { 107 Matcher m = LITERAL_PATTERN.matcher(line); 108 if (m.find()) { 109 return Integer.parseInt(m.group(1)); // Should always parse because we matched \d+ 110 } 111 return -1; 112 } 113 114 /** 115 * Intepret the String reply code - OK, NO, BAD - in an untagged response as a integer. 116 * 117 * @param line the untagged line to be checked 118 * @return {@link #OK} or {@link #NO} or {@link #BAD} or {@link #CONT} 119 * @throws IOException if the input has an unexpected format 120 */ 121 public static int getUntaggedReplyCode(String line) throws IOException { 122 return getReplyCode(line, UNTAGGED_PATTERN); 123 } 124 125 // Helper method to process both tagged and untagged replies. 126 private static int getReplyCode(String line, Pattern pattern) throws IOException{ 127 if (isContinuation(line)) { 128 return CONT; 129 } 130 Matcher m = pattern.matcher(line); 131 if (m.matches()) { // TODO would lookingAt() be more efficient? If so, then drop trailing .* from patterns 132 String code = m.group(1); 133 if (code.equals(IMAP_OK)) { 134 return OK; 135 } 136 if (code.equals(IMAP_BAD)) { 137 return BAD; 138 } 139 if (code.equals(IMAP_NO)) { 140 return NO; 141 } 142 } 143 throw new MalformedServerReplyException( 144 "Received unexpected IMAP protocol response from server: '" + line + "'."); 145 } 146 147 /** 148 * Checks whether the reply code indicates success or not 149 * 150 * @param replyCode the code to check 151 * @return {@code true} if the code equals {@link #OK} 152 */ 153 public static boolean isSuccess(int replyCode) { 154 return replyCode == OK; 155 } 156 /** 157 * Checks if the reply line is a continuation, i.e. starts with "+" 158 * @param replyCode the code to be checked 159 * @return {@code true} if the response was a continuation 160 */ 161 public static boolean isContinuation(int replyCode) { 162 return replyCode == CONT; 163 } 164 165 } 166 167 /* kate: indent-width 4; replace-tabs on; */