View Javadoc
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      /**
45       * The reply code indicating a partial response.
46       * This is used when a chunk listener is registered and the listener
47       * requests that the reply lines are cleared on return.
48       * @since 3.4
49       */
50      public static final int PARTIAL = 3;
51  
52      /** The IMAP reply String indicating success of an operation. */
53      private static final String IMAP_OK = "OK";
54  
55      /** The IMAP reply String indicating failure of an operation. */
56      private static final String IMAP_NO = "NO";
57  
58      /** The IMAP reply String indicating command rejection. */
59      private static final String IMAP_BAD = "BAD";
60  
61      // Start of line for untagged replies
62      private static final String IMAP_UNTAGGED_PREFIX = "* ";
63  
64      // Start of line for continuation replies
65      private static final String IMAP_CONTINUATION_PREFIX = "+";
66  
67      // Cannot be instantiated.
68      private IMAPReply()
69      {}
70  
71      /**
72       * Checks if the reply line is untagged - e.g. "* OK ..."
73       * @param line to be checked
74       * @return {@code true} if the line is untagged
75       */
76      public static boolean isUntagged(String line) {
77          return line.startsWith(IMAP_UNTAGGED_PREFIX);
78      }
79  
80      /**
81       * Checks if the reply line is a continuation, i.e. starts with "+"
82       * @param line the line to be checked
83       * @return {@code true} if the line is untagged
84       */
85      public static boolean isContinuation(String line) {
86          return line.startsWith(IMAP_CONTINUATION_PREFIX);
87      }
88  
89      private static final String TAGGED_RESPONSE = "^\\w+ (\\S+).*"; // TODO perhaps be less strict on tag match?
90      // tag cannot contain: + ( ) { SP CTL % * " \ ]
91      private static final Pattern TAGGED_PATTERN = Pattern.compile(TAGGED_RESPONSE);
92  
93      /**
94       * Intepret the String reply code - OK, NO, BAD - in a tagged response as a integer.
95       *
96       * @param line the tagged line to be checked
97       * @return {@link #OK} or {@link #NO} or {@link #BAD} or {@link #CONT}
98       * @throws IOException if the input has an unexpected format
99       */
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; */