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; */