| 1 | /*- |
|---|
| 2 | * Copyright (c) 2008, Derek Konigsberg |
|---|
| 3 | * All rights reserved. |
|---|
| 4 | * |
|---|
| 5 | * Redistribution and use in source and binary forms, with or without |
|---|
| 6 | * modification, are permitted provided that the following conditions |
|---|
| 7 | * are met: |
|---|
| 8 | * |
|---|
| 9 | * 1. Redistributions of source code must retain the above copyright |
|---|
| 10 | * notice, this list of conditions and the following disclaimer. |
|---|
| 11 | * 2. Redistributions in binary form must reproduce the above copyright |
|---|
| 12 | * notice, this list of conditions and the following disclaimer in the |
|---|
| 13 | * documentation and/or other materials provided with the distribution. |
|---|
| 14 | * 3. Neither the name of the project nor the names of its |
|---|
| 15 | * contributors may be used to endorse or promote products derived |
|---|
| 16 | * from this software without specific prior written permission. |
|---|
| 17 | * |
|---|
| 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|---|
| 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|---|
| 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|---|
| 21 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|---|
| 22 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|---|
| 23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|---|
| 24 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|---|
| 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|---|
| 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
|---|
| 27 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|---|
| 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
|---|
| 29 | * OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 30 | */ |
|---|
| 31 | |
|---|
| 32 | package org.logicprobe.LogicMail.mail; |
|---|
| 33 | |
|---|
| 34 | import java.util.Date; |
|---|
| 35 | |
|---|
| 36 | import net.rim.device.api.system.UnsupportedOperationException; |
|---|
| 37 | |
|---|
| 38 | import org.logicprobe.LogicMail.conf.AccountConfig; |
|---|
| 39 | import org.logicprobe.LogicMail.mail.imap.ImapClient; |
|---|
| 40 | import org.logicprobe.LogicMail.mail.pop.PopClient; |
|---|
| 41 | import org.logicprobe.LogicMail.message.FolderMessage; |
|---|
| 42 | import org.logicprobe.LogicMail.message.MessageFlags; |
|---|
| 43 | import org.logicprobe.LogicMail.message.MimeMessagePart; |
|---|
| 44 | |
|---|
| 45 | public class NetworkMailStore extends AbstractMailStore { |
|---|
| 46 | private IncomingMailClient client; |
|---|
| 47 | private IncomingMailConnectionHandler connectionHandler; |
|---|
| 48 | private AccountConfig accountConfig; |
|---|
| 49 | public NetworkMailStore(AccountConfig accountConfig) { |
|---|
| 50 | super(); |
|---|
| 51 | this.client = MailClientFactory.createMailClient(accountConfig); |
|---|
| 52 | this.accountConfig = accountConfig; |
|---|
| 53 | this.connectionHandler = new IncomingMailConnectionHandler(this, client); |
|---|
| 54 | this.connectionHandler.start(); |
|---|
| 55 | } |
|---|
| 56 | |
|---|
| 57 | /** |
|---|
| 58 | * Gets the account configuration associated with this network mail store. |
|---|
| 59 | * |
|---|
| 60 | * @return Account configuration. |
|---|
| 61 | */ |
|---|
| 62 | public AccountConfig getAccountConfig() { |
|---|
| 63 | return this.accountConfig; |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | public void shutdown(boolean wait) { |
|---|
| 67 | connectionHandler.shutdown(wait); |
|---|
| 68 | } |
|---|
| 69 | |
|---|
| 70 | /** |
|---|
| 71 | * Restarts the mail connection handler thread. |
|---|
| 72 | */ |
|---|
| 73 | public void restart() { |
|---|
| 74 | if(!connectionHandler.isRunning()) { |
|---|
| 75 | connectionHandler.start(); |
|---|
| 76 | } |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | public boolean isLocal() { |
|---|
| 80 | return false; |
|---|
| 81 | } |
|---|
| 82 | |
|---|
| 83 | public boolean hasFolders() { |
|---|
| 84 | return client.hasFolders(); |
|---|
| 85 | } |
|---|
| 86 | |
|---|
| 87 | public boolean hasMessageParts() { |
|---|
| 88 | return client.hasMessageParts(); |
|---|
| 89 | } |
|---|
| 90 | |
|---|
| 91 | public boolean hasFlags() { |
|---|
| 92 | return client.hasFlags(); |
|---|
| 93 | } |
|---|
| 94 | |
|---|
| 95 | public boolean hasAppend() { |
|---|
| 96 | return client.hasAppend(); |
|---|
| 97 | } |
|---|
| 98 | |
|---|
| 99 | public boolean hasCopy() { |
|---|
| 100 | return client.hasCopy(); |
|---|
| 101 | } |
|---|
| 102 | |
|---|
| 103 | public boolean hasUndelete() { |
|---|
| 104 | return client.hasUndelete(); |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | public boolean hasExpunge() { |
|---|
| 108 | return client.hasExpunge(); |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | /** |
|---|
| 112 | * Returns whether the mail store supports retrieval of a full folder |
|---|
| 113 | * message index-to-UID map. |
|---|
| 114 | * |
|---|
| 115 | * @return True if index-to-UID map retrieval is supported, false otherwise |
|---|
| 116 | * @see #requestFolderMessageIndexMap(FolderTreeItem, MailStoreRequestCallback) |
|---|
| 117 | */ |
|---|
| 118 | public boolean hasFolderMessageIndexMap() { |
|---|
| 119 | return client.hasFolderMessageIndexMap(); |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | /** |
|---|
| 123 | * Returns whether the mail store has a locked folder view while connected. |
|---|
| 124 | * If this method returns true, then an explicit refresh during an existing |
|---|
| 125 | * connection will not return new data. |
|---|
| 126 | * @return True if folder contents are locked while connected, false otherwise. |
|---|
| 127 | */ |
|---|
| 128 | public boolean hasLockedFolders() { |
|---|
| 129 | return client.hasLockedFolders(); |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | public boolean isConnected() { |
|---|
| 133 | return client.isConnected(); |
|---|
| 134 | } |
|---|
| 135 | |
|---|
| 136 | /** |
|---|
| 137 | * Gets the inbox folder, if available. |
|---|
| 138 | */ |
|---|
| 139 | public FolderTreeItem getInboxFolder() { |
|---|
| 140 | return client.getInboxFolder(); |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | /** |
|---|
| 144 | * Requests that the mail store disconnect from the mail server. |
|---|
| 145 | * <p> |
|---|
| 146 | * Unlike the <code>shutdown(boolean)</code> method, this does not cause |
|---|
| 147 | * the connection handler thread to terminate. As such, any subsequent |
|---|
| 148 | * request may cause it to reconnect. |
|---|
| 149 | * </p> |
|---|
| 150 | */ |
|---|
| 151 | public void requestDisconnect() { |
|---|
| 152 | processRequest(new NetworkDisconnectRequest(this, NetworkDisconnectRequest.REQUEST_DISCONNECT)); |
|---|
| 153 | } |
|---|
| 154 | |
|---|
| 155 | /** |
|---|
| 156 | * Creates a request to instruct the connection handler thread to start |
|---|
| 157 | * its polling timer, if the connection is closed and it is not already |
|---|
| 158 | * started. |
|---|
| 159 | */ |
|---|
| 160 | public NetworkPollingStartRequest createPollingStartRequest() { |
|---|
| 161 | NetworkPollingStartRequest request = new NetworkPollingStartRequest(this); |
|---|
| 162 | return request; |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | /** |
|---|
| 166 | * Creates a request to instruct the mail client to enable or disable its |
|---|
| 167 | * idle mode. This request is useful at the beginning and end of a batch |
|---|
| 168 | * operation, to prevent the mail client from entering and exiting idle |
|---|
| 169 | * mode between requests. |
|---|
| 170 | * |
|---|
| 171 | * @param idleEnabled whether or not the idle mode should be enabled |
|---|
| 172 | */ |
|---|
| 173 | public NetworkClientIdleModeRequest createClientIdleModeRequest(boolean idleEnabled) { |
|---|
| 174 | NetworkClientIdleModeRequest request = new NetworkClientIdleModeRequest(this, idleEnabled); |
|---|
| 175 | return request; |
|---|
| 176 | } |
|---|
| 177 | |
|---|
| 178 | public FolderTreeRequest createFolderTreeRequest() { |
|---|
| 179 | NetworkFolderTreeRequest request = new NetworkFolderTreeRequest(this); |
|---|
| 180 | return request; |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | public FolderExpungeRequest createFolderExpungeRequest(FolderTreeItem folder) { |
|---|
| 184 | NetworkFolderExpungeRequest request = new NetworkFolderExpungeRequest(this, folder); |
|---|
| 185 | return request; |
|---|
| 186 | } |
|---|
| 187 | |
|---|
| 188 | public FolderStatusRequest createFolderStatusRequest(FolderTreeItem[] folders) { |
|---|
| 189 | NetworkFolderStatusRequest request = new NetworkFolderStatusRequest(this, folders); |
|---|
| 190 | return request; |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | public FolderMessagesRequest createFolderMessagesRangeRequest(FolderTreeItem folder, MessageToken firstToken, int increment) { |
|---|
| 194 | if(firstToken == null || increment <= 0) { |
|---|
| 195 | throw new IllegalArgumentException(); |
|---|
| 196 | } |
|---|
| 197 | |
|---|
| 198 | NetworkFolderMessagesRequest request = new NetworkFolderMessagesRequest(this, folder, firstToken, increment); |
|---|
| 199 | return request; |
|---|
| 200 | } |
|---|
| 201 | |
|---|
| 202 | public FolderMessagesRequest createFolderMessagesSetRequest(FolderTreeItem folder, MessageToken[] messageTokens, boolean flagsOnly) { |
|---|
| 203 | NetworkFolderMessagesRequest request = new NetworkFolderMessagesRequest(this, folder, messageTokens, flagsOnly); |
|---|
| 204 | return request; |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | public FolderMessagesRequest createFolderMessagesSetByIndexRequest(FolderTreeItem folder, int[] messageIndices) { |
|---|
| 208 | NetworkFolderMessagesRequest request = new NetworkFolderMessagesRequest(this, folder, messageIndices); |
|---|
| 209 | return request; |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | public FolderMessagesRequest createFolderMessagesRecentRequest(FolderTreeItem folder, boolean flagsOnly) { |
|---|
| 213 | NetworkFolderMessagesRequest request = new NetworkFolderMessagesRequest(this, folder, flagsOnly); |
|---|
| 214 | return request; |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | /** |
|---|
| 218 | * Creates a request to synchronize a locally cached folder with data from |
|---|
| 219 | * the mail server. This is a complex request with protocol-dependent |
|---|
| 220 | * implementations, and is intended to be called from within cache-handling |
|---|
| 221 | * code. |
|---|
| 222 | * |
|---|
| 223 | * @param folder The folder to refresh. |
|---|
| 224 | * @param loadedMessages Collection of {@link FolderMessage} objects |
|---|
| 225 | * for messages that have already been loaded for this folder prior to |
|---|
| 226 | * the start of the refresh operation. |
|---|
| 227 | * @return the request object |
|---|
| 228 | */ |
|---|
| 229 | public MailStoreRequest createFolderRefreshRequest(FolderTreeItem folder, FolderMessage[] loadedMessages) { |
|---|
| 230 | if(client instanceof ImapClient) { |
|---|
| 231 | return new ImapFolderRefreshRequest(this, folder, loadedMessages); |
|---|
| 232 | } |
|---|
| 233 | else if(client instanceof PopClient) { |
|---|
| 234 | return new PopFolderRefreshRequest(this, folder, loadedMessages); |
|---|
| 235 | } |
|---|
| 236 | else { |
|---|
| 237 | throw new UnsupportedOperationException(); |
|---|
| 238 | } |
|---|
| 239 | } |
|---|
| 240 | |
|---|
| 241 | public MessageRequest createMessageRequest(MessageToken messageToken, boolean useLimits) { |
|---|
| 242 | NetworkMessageRequest request = new NetworkMessageRequest(this, messageToken, useLimits); |
|---|
| 243 | return request; |
|---|
| 244 | } |
|---|
| 245 | |
|---|
| 246 | public MessageRequest createMessagePartsRequest(MessageToken messageToken, MimeMessagePart[] messageParts) { |
|---|
| 247 | NetworkMessageRequest request = new NetworkMessageRequest(this, messageToken, messageParts); |
|---|
| 248 | return request; |
|---|
| 249 | } |
|---|
| 250 | |
|---|
| 251 | public MessageFlagChangeRequest createMessageFlagChangeRequest( |
|---|
| 252 | MessageToken messageToken, |
|---|
| 253 | MessageFlags messageFlags, |
|---|
| 254 | boolean addOrRemove) { |
|---|
| 255 | |
|---|
| 256 | if(messageFlags.isDeleted()) { |
|---|
| 257 | if(!addOrRemove && !client.hasUndelete()) { |
|---|
| 258 | throw new UnsupportedOperationException(); |
|---|
| 259 | } |
|---|
| 260 | } |
|---|
| 261 | else if(!this.hasFlags()) { |
|---|
| 262 | throw new UnsupportedOperationException(); |
|---|
| 263 | } |
|---|
| 264 | |
|---|
| 265 | NetworkMessageFlagChangeRequest request = new NetworkMessageFlagChangeRequest(this, messageToken, messageFlags, addOrRemove); |
|---|
| 266 | return request; |
|---|
| 267 | } |
|---|
| 268 | |
|---|
| 269 | public MessageFlagChangeRequest createMessageFlagChangeRequest( |
|---|
| 270 | MessageToken[] messageTokens, |
|---|
| 271 | MessageFlags messageFlags, |
|---|
| 272 | boolean addOrRemove) { |
|---|
| 273 | |
|---|
| 274 | if(messageFlags.isDeleted()) { |
|---|
| 275 | if(!addOrRemove && !client.hasUndelete()) { |
|---|
| 276 | throw new UnsupportedOperationException(); |
|---|
| 277 | } |
|---|
| 278 | } |
|---|
| 279 | else if(!this.hasFlags()) { |
|---|
| 280 | throw new UnsupportedOperationException(); |
|---|
| 281 | } |
|---|
| 282 | |
|---|
| 283 | NetworkMessageFlagChangeRequest request = new NetworkMessageFlagChangeRequest(this, messageTokens, messageFlags, addOrRemove); |
|---|
| 284 | return request; |
|---|
| 285 | } |
|---|
| 286 | |
|---|
| 287 | public MessageRangeFlagChangeRequest createMessageRangeFlagChangeRequest( |
|---|
| 288 | FolderTreeItem folder, |
|---|
| 289 | Date startDate, |
|---|
| 290 | MessageFlags messageFlags, |
|---|
| 291 | boolean addOrRemove) { |
|---|
| 292 | |
|---|
| 293 | throw new UnsupportedOperationException(); |
|---|
| 294 | } |
|---|
| 295 | |
|---|
| 296 | public MessageAppendRequest createMessageAppendRequest(FolderTreeItem folder, String rawMessage, MessageFlags initialFlags) { |
|---|
| 297 | if(!this.hasAppend()) { |
|---|
| 298 | throw new UnsupportedOperationException(); |
|---|
| 299 | } |
|---|
| 300 | NetworkMessageAppendRequest request = new NetworkMessageAppendRequest(this, folder, rawMessage, initialFlags); |
|---|
| 301 | return request; |
|---|
| 302 | } |
|---|
| 303 | |
|---|
| 304 | public MessageCopyRequest createMessageCopyRequest(MessageToken messageToken, FolderTreeItem destinationFolder) { |
|---|
| 305 | if(!this.hasCopy()) { |
|---|
| 306 | throw new UnsupportedOperationException(); |
|---|
| 307 | } |
|---|
| 308 | NetworkMessageCopyRequest request = new NetworkMessageCopyRequest(this, messageToken, destinationFolder); |
|---|
| 309 | return request; |
|---|
| 310 | } |
|---|
| 311 | |
|---|
| 312 | public void processRequest(MailStoreRequest request) { |
|---|
| 313 | if(request instanceof NetworkMailStoreRequest |
|---|
| 314 | && request instanceof ConnectionHandlerRequest) { |
|---|
| 315 | connectionHandler.addRequest((ConnectionHandlerRequest)request); |
|---|
| 316 | } |
|---|
| 317 | else { |
|---|
| 318 | throw new IllegalArgumentException(); |
|---|
| 319 | } |
|---|
| 320 | } |
|---|
| 321 | |
|---|
| 322 | /** |
|---|
| 323 | * Submits the request to the mail store for processing, placing it at the |
|---|
| 324 | * top of the request queue. |
|---|
| 325 | * |
|---|
| 326 | * @param request the request to process |
|---|
| 327 | */ |
|---|
| 328 | public void processRequestFirst(MailStoreRequest request) { |
|---|
| 329 | if(request instanceof NetworkMailStoreRequest |
|---|
| 330 | && request instanceof ConnectionHandlerRequest) { |
|---|
| 331 | connectionHandler.pushRequest((ConnectionHandlerRequest)request); |
|---|
| 332 | } |
|---|
| 333 | else { |
|---|
| 334 | throw new IllegalArgumentException(); |
|---|
| 335 | } |
|---|
| 336 | } |
|---|
| 337 | |
|---|
| 338 | IncomingMailConnectionHandler getConnectionHandler() { |
|---|
| 339 | return connectionHandler; |
|---|
| 340 | } |
|---|
| 341 | } |
|---|