mirror of
https://github.com/moparisthebest/davmail
synced 2024-08-13 16:53:51 -04:00
IMAP: implement move folder, Seen flag, only list mail folders, improve message headers retrieval
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@308 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
091ddc244f
commit
f92fa85d1a
@ -14,6 +14,7 @@ import org.apache.webdav.lib.ResponseEntity;
|
|||||||
import org.apache.webdav.lib.WebdavResource;
|
import org.apache.webdav.lib.WebdavResource;
|
||||||
import org.apache.webdav.lib.methods.PropPatchMethod;
|
import org.apache.webdav.lib.methods.PropPatchMethod;
|
||||||
import org.apache.webdav.lib.methods.SearchMethod;
|
import org.apache.webdav.lib.methods.SearchMethod;
|
||||||
|
import org.apache.webdav.lib.methods.MoveMethod;
|
||||||
import org.htmlcleaner.CommentToken;
|
import org.htmlcleaner.CommentToken;
|
||||||
import org.htmlcleaner.HtmlCleaner;
|
import org.htmlcleaner.HtmlCleaner;
|
||||||
import org.htmlcleaner.TagNode;
|
import org.htmlcleaner.TagNode;
|
||||||
@ -537,23 +538,10 @@ public class ExchangeSession {
|
|||||||
message.size = Integer.parseInt(prop.getPropertyAsString());
|
message.size = Integer.parseInt(prop.getPropertyAsString());
|
||||||
} else if ("uid".equals(localName)) {
|
} else if ("uid".equals(localName)) {
|
||||||
message.uid = prop.getPropertyAsString();
|
message.uid = prop.getPropertyAsString();
|
||||||
} else if ("date".equals(prop.getLocalName())) {
|
} else if ("read".equals(localName)) {
|
||||||
message.date = prop.getPropertyAsString();
|
message.read = "1".equals(prop.getPropertyAsString());
|
||||||
} else if ("message-id".equals(prop.getLocalName())) {
|
} else if ("message-id".equals(prop.getLocalName())) {
|
||||||
message.messageId = prop.getPropertyAsString();
|
message.messageId = prop.getPropertyAsString();
|
||||||
} else if ("from".equals(prop.getLocalName())) {
|
|
||||||
message.from = prop.getPropertyAsString();
|
|
||||||
} else if ("to".equals(prop.getLocalName())) {
|
|
||||||
message.to = prop.getPropertyAsString();
|
|
||||||
} else if ("cc".equals(prop.getLocalName())) {
|
|
||||||
message.cc = prop.getPropertyAsString();
|
|
||||||
} else if ("subject".equals(prop.getLocalName())) {
|
|
||||||
message.subject = prop.getPropertyAsString();
|
|
||||||
} else if ("priority".equals(prop.getLocalName())) {
|
|
||||||
String priorityLabel = PRIORITIES.get(prop.getPropertyAsString());
|
|
||||||
if (priorityLabel != null) {
|
|
||||||
message.priority = priorityLabel;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,11 +563,29 @@ public class ExchangeSession {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateMessage(Message message, Map<String, String> properties) throws IOException {
|
||||||
|
PropPatchMethod patchMethod = new PropPatchMethod(URIUtil.encodePathQuery(message.messageUrl));
|
||||||
|
try {
|
||||||
|
for (Map.Entry<String,String> entry:properties.entrySet()) {
|
||||||
|
if ("read".equals(entry.getKey())) {
|
||||||
|
patchMethod.addPropertyToSet("read", entry.getValue(), "e", "urn:schemas:httpmail:");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patchMethod.setDebug(4);
|
||||||
|
int statusCode = wdr.retrieveSessionInstance().executeMethod(patchMethod);
|
||||||
|
if (statusCode != HttpStatus.SC_MULTI_STATUS) {
|
||||||
|
throw new IOException("Unable to update message properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
patchMethod.releaseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
public List<Message> getAllMessages(String folderName) throws IOException {
|
public List<Message> getAllMessages(String folderName) throws IOException {
|
||||||
String folderUrl = getFolderPath(folderName);
|
String folderUrl = getFolderPath(folderName);
|
||||||
List<Message> messages = new ArrayList<Message>();
|
List<Message> messages = new ArrayList<Message>();
|
||||||
String searchRequest = "Select \"DAV:uid\", \"http://schemas.microsoft.com/mapi/proptag/x0e080003\"" +
|
String searchRequest = "Select \"DAV:uid\", \"http://schemas.microsoft.com/mapi/proptag/x0e080003\"" +
|
||||||
" ,\"urn:schemas:mailheader:from\",\"urn:schemas:mailheader:to\",\"urn:schemas:mailheader:cc\",\"urn:schemas:httpmail:subject\",\"urn:schemas:mailheader:date\",\"urn:schemas:mailheader:message-id\",\"urn:schemas:httpmail:priority\"" +
|
" ,\"urn:schemas:mailheader:message-id\", \"urn:schemas:httpmail:read\""+
|
||||||
" FROM Scope('SHALLOW TRAVERSAL OF \"" + folderUrl + "\"')\n" +
|
" FROM Scope('SHALLOW TRAVERSAL OF \"" + folderUrl + "\"')\n" +
|
||||||
" WHERE \"DAV:ishidden\" = False AND \"DAV:isfolder\" = False\n" +
|
" WHERE \"DAV:ishidden\" = False AND \"DAV:isfolder\" = False\n" +
|
||||||
" ORDER BY \"urn:schemas:httpmail:date\" ASC";
|
" ORDER BY \"urn:schemas:httpmail:date\" ASC";
|
||||||
@ -600,7 +606,8 @@ public class ExchangeSession {
|
|||||||
String searchRequest = "Select \"DAV:nosubs\", \"DAV:hassubs\"," +
|
String searchRequest = "Select \"DAV:nosubs\", \"DAV:hassubs\"," +
|
||||||
" \"DAV:hassubs\",\"urn:schemas:httpmail:unreadcount\"" +
|
" \"DAV:hassubs\",\"urn:schemas:httpmail:unreadcount\"" +
|
||||||
" FROM Scope('" + mode + " TRAVERSAL OF \"" + getFolderPath(folderName) + "\"')\n" +
|
" FROM Scope('" + mode + " TRAVERSAL OF \"" + getFolderPath(folderName) + "\"')\n" +
|
||||||
" WHERE \"DAV:ishidden\" = False AND \"DAV:isfolder\" = True \n";
|
" WHERE \"DAV:ishidden\" = False AND \"DAV:isfolder\" = True \n"+
|
||||||
|
" AND (\"DAV:contentclass\"='urn:content-classes:mailfolder' OR \"DAV:contentclass\"='urn:content-classes:folder')";
|
||||||
Enumeration folderEnum = DavGatewayHttpClientFacade.executeSearchMethod(wdr.retrieveSessionInstance(), mailPath, searchRequest);
|
Enumeration folderEnum = DavGatewayHttpClientFacade.executeSearchMethod(wdr.retrieveSessionInstance(), mailPath, searchRequest);
|
||||||
|
|
||||||
while (folderEnum.hasMoreElements()) {
|
while (folderEnum.hasMoreElements()) {
|
||||||
@ -822,6 +829,7 @@ public class ExchangeSession {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
method.addPropertyToSet("outlookfolderclass", "IPF.Note", "ex", "http://schemas.microsoft.com/exchange/");
|
method.addPropertyToSet("outlookfolderclass", "IPF.Note", "ex", "http://schemas.microsoft.com/exchange/");
|
||||||
|
try {
|
||||||
wdr.retrieveSessionInstance().executeMethod(method);
|
wdr.retrieveSessionInstance().executeMethod(method);
|
||||||
// ok or alredy exists
|
// ok or alredy exists
|
||||||
if (method.getStatusCode() != HttpStatus.SC_MULTI_STATUS && method.getStatusCode() != HttpStatus.SC_METHOD_NOT_ALLOWED) {
|
if (method.getStatusCode() != HttpStatus.SC_MULTI_STATUS && method.getStatusCode() != HttpStatus.SC_METHOD_NOT_ALLOWED) {
|
||||||
@ -830,6 +838,31 @@ public class ExchangeSession {
|
|||||||
ex.setReason(method.getStatusText());
|
ex.setReason(method.getStatusText());
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
method.releaseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveFolder(String folderName, String targetName) throws IOException {
|
||||||
|
String folderPath = getFolderPath(folderName);
|
||||||
|
String targetPath = getFolderPath(targetName);
|
||||||
|
MoveMethod method = new MoveMethod(URIUtil.encodePath(folderPath),
|
||||||
|
URIUtil.encodePath(targetPath));
|
||||||
|
method.setOverwrite(false);
|
||||||
|
//method.addRequestHeader("Allow-Rename", "t");
|
||||||
|
try {
|
||||||
|
int statusCode = wdr.retrieveSessionInstance().executeMethod(method);
|
||||||
|
if (statusCode == HttpStatus.SC_PRECONDITION_FAILED) {
|
||||||
|
throw new HttpException("Unable to move folder, target already exists");
|
||||||
|
} else if (statusCode != HttpStatus.SC_CREATED) {
|
||||||
|
HttpException ex = new HttpException();
|
||||||
|
ex.setReasonCode(method.getStatusCode());
|
||||||
|
ex.setReason(method.getStatusText());
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
method.releaseConnection();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Folder {
|
public static class Folder {
|
||||||
@ -856,13 +889,8 @@ public class ExchangeSession {
|
|||||||
public String messageUrl;
|
public String messageUrl;
|
||||||
public String uid;
|
public String uid;
|
||||||
public int size;
|
public int size;
|
||||||
public String from;
|
|
||||||
public String date;
|
|
||||||
public String messageId;
|
public String messageId;
|
||||||
public String subject;
|
public boolean read;
|
||||||
public String priority;
|
|
||||||
public String cc;
|
|
||||||
public String to;
|
|
||||||
|
|
||||||
public void write(OutputStream os) throws IOException {
|
public void write(OutputStream os) throws IOException {
|
||||||
HttpMethod method = null;
|
HttpMethod method = null;
|
||||||
|
@ -5,8 +5,11 @@ import java.net.SocketTimeoutException;
|
|||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FilterOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import davmail.AbstractConnection;
|
import davmail.AbstractConnection;
|
||||||
import davmail.tray.DavGatewayTray;
|
import davmail.tray.DavGatewayTray;
|
||||||
@ -14,6 +17,7 @@ import davmail.exchange.ExchangeSession;
|
|||||||
import davmail.exchange.ExchangeSessionFactory;
|
import davmail.exchange.ExchangeSessionFactory;
|
||||||
import com.sun.mail.imap.protocol.BASE64MailboxEncoder;
|
import com.sun.mail.imap.protocol.BASE64MailboxEncoder;
|
||||||
import com.sun.mail.imap.protocol.BASE64MailboxDecoder;
|
import com.sun.mail.imap.protocol.BASE64MailboxDecoder;
|
||||||
|
import org.apache.commons.httpclient.HttpException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dav Gateway smtp connection implementation.
|
* Dav Gateway smtp connection implementation.
|
||||||
@ -55,7 +59,7 @@ public class ImapConnection extends AbstractConnection {
|
|||||||
&& nextToken.charAt(nextToken.length() - 1) != ')') {
|
&& nextToken.charAt(nextToken.length() - 1) != ')') {
|
||||||
nextToken.append(' ').append(super.nextToken());
|
nextToken.append(' ').append(super.nextToken());
|
||||||
}
|
}
|
||||||
return nextToken.toString();
|
return removeQuotes(nextToken.toString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (tokens.hasMoreTokens()) {
|
if (tokens.hasMoreTokens()) {
|
||||||
@ -157,7 +161,7 @@ public class ImapConnection extends AbstractConnection {
|
|||||||
} else if ("close".equalsIgnoreCase(command)) {
|
} else if ("close".equalsIgnoreCase(command)) {
|
||||||
currentFolder = null;
|
currentFolder = null;
|
||||||
messages = null;
|
messages = null;
|
||||||
sendClient(commandId + " OK CLOSE unrecognized");
|
sendClient(commandId + " OK CLOSE completed");
|
||||||
} else if ("create".equalsIgnoreCase(command)) {
|
} else if ("create".equalsIgnoreCase(command)) {
|
||||||
if (tokens.hasMoreTokens()) {
|
if (tokens.hasMoreTokens()) {
|
||||||
String folderName = BASE64MailboxDecoder.decode(removeQuotes(tokens.nextToken()));
|
String folderName = BASE64MailboxDecoder.decode(removeQuotes(tokens.nextToken()));
|
||||||
@ -166,6 +170,15 @@ public class ImapConnection extends AbstractConnection {
|
|||||||
} else {
|
} else {
|
||||||
sendClient(commandId + " BAD missing create argument");
|
sendClient(commandId + " BAD missing create argument");
|
||||||
}
|
}
|
||||||
|
} else if ("rename".equalsIgnoreCase(command)) {
|
||||||
|
String folderName = BASE64MailboxDecoder.decode(removeQuotes(tokens.nextToken()));
|
||||||
|
String targetName = BASE64MailboxDecoder.decode(removeQuotes(tokens.nextToken()));
|
||||||
|
try {
|
||||||
|
session.moveFolder(folderName, targetName);
|
||||||
|
sendClient(commandId + " OK rename completed");
|
||||||
|
} catch (HttpException e) {
|
||||||
|
sendClient(commandId + " NO " + e.getReason());
|
||||||
|
}
|
||||||
} else if ("uid".equalsIgnoreCase(command)) {
|
} else if ("uid".equalsIgnoreCase(command)) {
|
||||||
if (tokens.hasMoreTokens()) {
|
if (tokens.hasMoreTokens()) {
|
||||||
String subcommand = tokens.nextToken();
|
String subcommand = tokens.nextToken();
|
||||||
@ -192,7 +205,7 @@ public class ImapConnection extends AbstractConnection {
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
for (ExchangeSession.Message message : messages) {
|
for (ExchangeSession.Message message : messages) {
|
||||||
count++;
|
count++;
|
||||||
sendClient("* " + count + " FETCH (UID " + count + " FLAGS (\\Seen))");
|
sendClient("* " + count + " FETCH (UID " + count + " FLAGS ("+(message.read?"\\Seen":"")+"))");
|
||||||
}
|
}
|
||||||
sendClient(commandId + " OK UID FETCH completed");
|
sendClient(commandId + " OK UID FETCH completed");
|
||||||
} else {
|
} else {
|
||||||
@ -208,18 +221,22 @@ public class ImapConnection extends AbstractConnection {
|
|||||||
message.write(baos);
|
message.write(baos);
|
||||||
baos.close();
|
baos.close();
|
||||||
|
|
||||||
|
DavGatewayTray.debug("Messagee size: "+message.size+" actual size:"+baos.size()+" message+headers: "+(message.size+baos.size()));
|
||||||
sendClient("* " + messageIndex + " FETCH (UID " + messageIndex + " RFC822.SIZE " + baos.size() + " BODY[]<0>" +
|
sendClient("* " + messageIndex + " FETCH (UID " + messageIndex + " RFC822.SIZE " + baos.size() + " BODY[]<0>" +
|
||||||
" {" + baos.size() + "}");
|
" {" + baos.size() + "}");
|
||||||
message.write(os);
|
message.write(os);
|
||||||
sendClient(")");
|
sendClient(")");
|
||||||
} else {
|
} else {
|
||||||
|
// write headers to byte array
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
message.write(baos);
|
HeaderOutputStream headerOutputStream = new HeaderOutputStream(baos);
|
||||||
|
message.write(headerOutputStream);
|
||||||
baos.close();
|
baos.close();
|
||||||
sendClient("* " + messageIndex + " FETCH (UID " + messageIndex + " RFC822.SIZE " + baos.size() + " BODY[HEADER.FIELDS (FROM TO CC SUBJECT DATE MESSAGE-ID PRIORITY X-PRIORITY REFERENCES NEWSGROUPS IN-REPLY-TO CONTENT-TYPE)" +
|
sendClient("* " + messageIndex + " FETCH (UID " + messageIndex + " RFC822.SIZE " + headerOutputStream.size() + " BODY[HEADER.FIELDS ()" +
|
||||||
"] {" + baos.size() + "}");
|
"] {" + baos.size() + "}");
|
||||||
message.write(os);
|
os.write(baos.toByteArray());
|
||||||
sendClient(" FLAGS (\\Seen))");
|
os.flush();
|
||||||
|
sendClient(" FLAGS ("+(message.read?"\\Seen":"")+"))");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendClient(commandId + " OK FETCH completed");
|
sendClient(commandId + " OK FETCH completed");
|
||||||
@ -249,7 +266,29 @@ public class ImapConnection extends AbstractConnection {
|
|||||||
sendClient(commandId + " OK SEARCH completed");
|
sendClient(commandId + " OK SEARCH completed");
|
||||||
|
|
||||||
} else if ("store".equalsIgnoreCase(subcommand)) {
|
} else if ("store".equalsIgnoreCase(subcommand)) {
|
||||||
|
int uid = Integer.parseInt(tokens.nextToken());
|
||||||
|
String action = tokens.nextToken();
|
||||||
|
String flags = tokens.nextToken();
|
||||||
|
HashMap<String, String> properties = new HashMap<String, String>();
|
||||||
|
if ("-Flags".equalsIgnoreCase(action)) {
|
||||||
|
StringTokenizer flagtokenizer = new StringTokenizer(flags);
|
||||||
|
while (flagtokenizer.hasMoreTokens()) {
|
||||||
|
String flag = flagtokenizer.nextToken();
|
||||||
|
if ("\\Seen".equals(flag)) {
|
||||||
|
properties.put("read", "0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ("+Flags".equalsIgnoreCase(action)) {
|
||||||
|
StringTokenizer flagtokenizer = new StringTokenizer(flags);
|
||||||
|
while (flagtokenizer.hasMoreTokens()) {
|
||||||
|
String flag = flagtokenizer.nextToken();
|
||||||
|
if ("\\Seen".equals(flag)) {
|
||||||
|
properties.put("read", "1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// TODO
|
// TODO
|
||||||
|
session.updateMessage(messages.get(uid-1), properties);
|
||||||
sendClient(commandId + " OK STORE completed");
|
sendClient(commandId + " OK STORE completed");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -293,11 +332,11 @@ public class ImapConnection extends AbstractConnection {
|
|||||||
if (subjectStartIndex >= 0) {
|
if (subjectStartIndex >= 0) {
|
||||||
int subjectEndIndex = messageBody.indexOf("\r", subjectStartIndex);
|
int subjectEndIndex = messageBody.indexOf("\r", subjectStartIndex);
|
||||||
if (subjectEndIndex >= 0) {
|
if (subjectEndIndex >= 0) {
|
||||||
subject = messageBody.substring(subjectStartIndex+"Subject: ".length(), subjectEndIndex);
|
subject = messageBody.substring(subjectStartIndex + "Subject: ".length(), subjectEndIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (subject == null) {
|
if (subject == null) {
|
||||||
subject = "mail"+System.currentTimeMillis();
|
subject = "mail" + System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
session.createMessage(session.getFolderPath(folderName), subject, null, new String(buffer), true);
|
session.createMessage(session.getFolderPath(folderName), subject, null, new String(buffer), true);
|
||||||
sendClient(commandId + " OK APPEND completed");
|
sendClient(commandId + " OK APPEND completed");
|
||||||
@ -365,14 +404,67 @@ public class ImapConnection extends AbstractConnection {
|
|||||||
|
|
||||||
protected String removeQuotes(String value) {
|
protected String removeQuotes(String value) {
|
||||||
String result = value;
|
String result = value;
|
||||||
if (result.startsWith("\"") || result.startsWith("{")) {
|
if (result.startsWith("\"") || result.startsWith("{") || result.startsWith("(")) {
|
||||||
result = result.substring(1);
|
result = result.substring(1);
|
||||||
}
|
}
|
||||||
if (result.endsWith("\"") || result.endsWith("}")) {
|
if (result.endsWith("\"") || result.endsWith("}") || result.endsWith(")")) {
|
||||||
result = result.substring(0, result.length() - 1);
|
result = result.substring(0, result.length() - 1);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter to limit output lines to max body lines after header
|
||||||
|
*/
|
||||||
|
private static class HeaderOutputStream extends FilterOutputStream {
|
||||||
|
protected static final int START = 0;
|
||||||
|
protected static final int CR = 1;
|
||||||
|
protected static final int CRLF = 2;
|
||||||
|
protected static final int CRLFCR = 3;
|
||||||
|
protected static final int BODY = 4;
|
||||||
|
|
||||||
|
protected int state = START;
|
||||||
|
protected int size = 0;
|
||||||
|
|
||||||
|
public HeaderOutputStream(OutputStream os) {
|
||||||
|
super(os);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
size++;
|
||||||
|
if (state != BODY) {
|
||||||
|
super.write(b);
|
||||||
|
}
|
||||||
|
if (state == START) {
|
||||||
|
if (b == '\r') {
|
||||||
|
state = CR;
|
||||||
|
}
|
||||||
|
} else if (state == CR) {
|
||||||
|
if (b == '\n') {
|
||||||
|
state = CRLF;
|
||||||
|
} else {
|
||||||
|
state = START;
|
||||||
|
}
|
||||||
|
} else if (state == CRLF) {
|
||||||
|
if (b == '\r') {
|
||||||
|
state = CRLFCR;
|
||||||
|
} else {
|
||||||
|
state = START;
|
||||||
|
}
|
||||||
|
} else if (state == CRLFCR) {
|
||||||
|
if (b == '\n') {
|
||||||
|
state = BODY;
|
||||||
|
} else {
|
||||||
|
state = START;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user