2009-12-14 21:50:53 -05:00
|
|
|
package com.fsck.k9.mail.store;
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-04-08 07:10:20 -04:00
|
|
|
import android.util.Log;
|
2010-03-03 23:00:30 -05:00
|
|
|
|
2011-04-08 07:10:20 -04:00
|
|
|
import com.fsck.k9.Account;
|
|
|
|
import com.fsck.k9.K9;
|
|
|
|
import com.fsck.k9.controller.MessageRetrievalListener;
|
2014-09-28 06:59:11 -04:00
|
|
|
import com.fsck.k9.helper.UrlEncodingHelper;
|
2011-04-08 07:10:20 -04:00
|
|
|
import com.fsck.k9.helper.Utility;
|
2009-12-14 21:50:53 -05:00
|
|
|
import com.fsck.k9.mail.*;
|
2011-04-08 07:10:20 -04:00
|
|
|
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
|
|
|
import com.fsck.k9.mail.internet.MimeMessage;
|
2014-05-25 16:45:14 -04:00
|
|
|
|
2011-10-31 23:51:02 -04:00
|
|
|
import org.apache.commons.io.IOUtils;
|
2011-04-08 07:10:20 -04:00
|
|
|
import org.apache.http.*;
|
|
|
|
import org.apache.http.client.CookieStore;
|
|
|
|
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
|
|
|
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
|
|
|
import org.apache.http.client.methods.HttpGet;
|
|
|
|
import org.apache.http.client.methods.HttpUriRequest;
|
|
|
|
import org.apache.http.client.protocol.ClientContext;
|
|
|
|
import org.apache.http.conn.scheme.Scheme;
|
|
|
|
import org.apache.http.conn.scheme.SchemeRegistry;
|
|
|
|
import org.apache.http.entity.StringEntity;
|
|
|
|
import org.apache.http.impl.client.BasicCookieStore;
|
|
|
|
import org.apache.http.impl.client.DefaultHttpClient;
|
|
|
|
import org.apache.http.message.BasicNameValuePair;
|
|
|
|
import org.apache.http.protocol.BasicHttpContext;
|
|
|
|
import org.apache.http.protocol.HttpContext;
|
|
|
|
import org.xml.sax.Attributes;
|
|
|
|
import org.xml.sax.InputSource;
|
|
|
|
import org.xml.sax.SAXException;
|
|
|
|
import org.xml.sax.XMLReader;
|
|
|
|
import org.xml.sax.helpers.DefaultHandler;
|
|
|
|
|
|
|
|
import javax.net.ssl.SSLException;
|
|
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
|
|
import javax.xml.parsers.SAXParser;
|
|
|
|
import javax.xml.parsers.SAXParserFactory;
|
2014-05-25 16:45:14 -04:00
|
|
|
|
2011-04-08 07:10:20 -04:00
|
|
|
import java.io.*;
|
|
|
|
import java.net.URI;
|
|
|
|
import java.net.URISyntaxException;
|
|
|
|
import java.net.URLDecoder;
|
|
|
|
import java.net.URLEncoder;
|
|
|
|
import java.security.KeyManagementException;
|
|
|
|
import java.security.NoSuchAlgorithmException;
|
|
|
|
import java.text.DateFormat;
|
|
|
|
import java.text.SimpleDateFormat;
|
2011-11-01 16:54:55 -04:00
|
|
|
import java.util.*;
|
2011-04-08 07:10:20 -04:00
|
|
|
import java.util.zip.GZIPInputStream;
|
2009-01-04 19:05:43 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/**
|
|
|
|
* <pre>
|
2010-10-23 21:02:46 -04:00
|
|
|
* Uses WebDAV formatted HTTP calls to an MS Exchange server to fetch email
|
2010-12-14 13:02:39 -05:00
|
|
|
* and email information.
|
2008-12-06 19:29:11 -05:00
|
|
|
* </pre>
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public class WebDavStore extends Store {
|
2011-06-08 23:50:43 -04:00
|
|
|
public static final String STORE_TYPE = "WebDAV";
|
|
|
|
|
2010-10-23 21:03:29 -04:00
|
|
|
// Authentication types
|
|
|
|
private static final short AUTH_TYPE_NONE = 0;
|
|
|
|
private static final short AUTH_TYPE_BASIC = 1;
|
|
|
|
private static final short AUTH_TYPE_FORM_BASED = 2;
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2010-08-07 11:10:07 -04:00
|
|
|
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
|
|
|
|
|
|
|
private static final Message[] EMPTY_MESSAGE_ARRAY = new Message[0];
|
|
|
|
|
2011-04-08 07:10:20 -04:00
|
|
|
// These are the ids used from Exchange server to identify the special folders
|
|
|
|
// http://social.technet.microsoft.com/Forums/en/exchangesvrdevelopment/thread/1cd2e98c-8a12-44bd-a3e3-9c5ee9e4e14d
|
|
|
|
private static final String DAV_MAIL_INBOX_FOLDER = "inbox";
|
|
|
|
private static final String DAV_MAIL_DRAFTS_FOLDER = "drafts";
|
|
|
|
private static final String DAV_MAIL_SPAM_FOLDER = "junkemail";
|
2010-10-23 21:03:29 -04:00
|
|
|
private static final String DAV_MAIL_SEND_FOLDER = "##DavMailSubmissionURI##";
|
2011-04-12 08:17:22 -04:00
|
|
|
private static final String DAV_MAIL_TRASH_FOLDER = "deleteditems";
|
|
|
|
private static final String DAV_MAIL_OUTBOX_FOLDER = "outbox";
|
|
|
|
private static final String DAV_MAIL_SENT_FOLDER = "sentitems";
|
2010-10-23 21:03:29 -04:00
|
|
|
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/**
|
2011-06-06 18:08:43 -04:00
|
|
|
* Decodes a WebDavStore URI.
|
|
|
|
*
|
|
|
|
* <p>Possible forms:</p>
|
|
|
|
* <pre>
|
Eliminate the 'if available' connection security options
These options originated in the AOSP email client from which K-9 Mail was
forked. They provide an odd combination of 2 features:
1. Don't bother to authenticate the server's certificate (applies to both
SSL/TLS and STARTTLS); i.e., blindly accept all certificates. This is
generally a bad security policy which is susceptible to MITM attacks.
2. If STARTTLS is selected but the server doesn't claim to support
STARTTLS, then proceed without using encryption. This, too, is a bad
security policy which is susceptible to MITM attacks.
Since the time that K-9 Mail was forked, a couple things have changed:
> K-9 Mail has implemented the ability for users to review and permanently
accept individual certificates that would otherwise fail authentication.
With this ability, there is no need for a user to subject themselves to
the ongoing risks of feature 1. above. Hence, this commit removes feature
1.
> The AOSP email client has changed its behavior and no longer permits a
security downgrade to an unencrypted connection if the server doesn't
claim to support STARTTLS (i.e., they eliminated feature 2. above). K-9
Mail should do the same. It's unlikely that a server is going to provide
STARTTLS on an intermittent basis, so providing a contingency for such
unusual behavior is an unnecessary risk. Hence, this commit removes that
feature as well.
Effect on existing users:
If the old connection security setting was "SSL/TLS (if available)" (which
now gets remapped to "SSL/TLS"), and the server does not provide a
certificate that can be authenticated, then a "Certificate error for
<account name>" notification is generated telling the user to check their
server settings. Tapping the notification takes the user to the relevant
server settings, where the user can tap "Next" to review the certificate
and choose to permanently accept it. This process would occur during the
first syncing of folders after application upgrade or (in the case of
SMTP) during the first attempt to send a message.
If the connection security setting was "STARTTLS (if available)" (which
now gets remapped to "STARTTLS"), and the server does not provide a
certificate that can be authenticated, then the same process as above
would occur.
If the old connection security setting was "STARTTLS (if available)", and
the server doesn't claim to support STARTTLS, then the user would get a
certificate error notification which would lead them to the server's
settings. There they would need to choose a different connection security
-- most likely "NONE". If they didn't change anything but instead just
tapped "Next", the server settings would be checked again and a dialog
would pop up saying, "Cannot connect to server. (STARTTLS connection
security not available)". (The implementation of notifications when
STARTTLS is not available is not actually included here -- it's in the
commit that follows.)
Regarding the changes to providers.xml: in cases where the scheme ended
with "+ssl", the schemes were simply updated by appending "+". In cases
where the scheme ended with "+tls", a check of the server was made to
assure that STARTTLS was available before appending "+" to the scheme.
Domains paran.com and nate.com failed the check and were removed because
no current information could be found. Domains me.com and mac.com also
failed and were updated based on http://support.apple.com/kb/ht4864.
2014-02-26 16:50:21 -05:00
|
|
|
* webdav://user:password@server:port ConnectionSecurity.NONE
|
|
|
|
* webdav+ssl+://user:password@server:port ConnectionSecurity.SSL_TLS_REQUIRED
|
2011-06-06 18:08:43 -04:00
|
|
|
* </pre>
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-06-06 18:08:43 -04:00
|
|
|
public static WebDavStoreSettings decodeUri(String uri) {
|
|
|
|
String host;
|
|
|
|
int port;
|
|
|
|
ConnectionSecurity connectionSecurity;
|
|
|
|
String username = null;
|
|
|
|
String password = null;
|
|
|
|
String alias = null;
|
|
|
|
String path = null;
|
|
|
|
String authPath = null;
|
|
|
|
String mailboxPath = null;
|
|
|
|
|
2010-03-03 23:00:30 -05:00
|
|
|
|
2011-06-06 18:08:43 -04:00
|
|
|
URI webDavUri;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2011-06-06 18:08:43 -04:00
|
|
|
webDavUri = new URI(uri);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (URISyntaxException use) {
|
2011-06-06 18:08:43 -04:00
|
|
|
throw new IllegalArgumentException("Invalid WebDavStore URI", use);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2010-10-23 21:03:29 -04:00
|
|
|
|
2011-06-06 18:08:43 -04:00
|
|
|
String scheme = webDavUri.getScheme();
|
Eliminate the 'if available' connection security options
These options originated in the AOSP email client from which K-9 Mail was
forked. They provide an odd combination of 2 features:
1. Don't bother to authenticate the server's certificate (applies to both
SSL/TLS and STARTTLS); i.e., blindly accept all certificates. This is
generally a bad security policy which is susceptible to MITM attacks.
2. If STARTTLS is selected but the server doesn't claim to support
STARTTLS, then proceed without using encryption. This, too, is a bad
security policy which is susceptible to MITM attacks.
Since the time that K-9 Mail was forked, a couple things have changed:
> K-9 Mail has implemented the ability for users to review and permanently
accept individual certificates that would otherwise fail authentication.
With this ability, there is no need for a user to subject themselves to
the ongoing risks of feature 1. above. Hence, this commit removes feature
1.
> The AOSP email client has changed its behavior and no longer permits a
security downgrade to an unencrypted connection if the server doesn't
claim to support STARTTLS (i.e., they eliminated feature 2. above). K-9
Mail should do the same. It's unlikely that a server is going to provide
STARTTLS on an intermittent basis, so providing a contingency for such
unusual behavior is an unnecessary risk. Hence, this commit removes that
feature as well.
Effect on existing users:
If the old connection security setting was "SSL/TLS (if available)" (which
now gets remapped to "SSL/TLS"), and the server does not provide a
certificate that can be authenticated, then a "Certificate error for
<account name>" notification is generated telling the user to check their
server settings. Tapping the notification takes the user to the relevant
server settings, where the user can tap "Next" to review the certificate
and choose to permanently accept it. This process would occur during the
first syncing of folders after application upgrade or (in the case of
SMTP) during the first attempt to send a message.
If the connection security setting was "STARTTLS (if available)" (which
now gets remapped to "STARTTLS"), and the server does not provide a
certificate that can be authenticated, then the same process as above
would occur.
If the old connection security setting was "STARTTLS (if available)", and
the server doesn't claim to support STARTTLS, then the user would get a
certificate error notification which would lead them to the server's
settings. There they would need to choose a different connection security
-- most likely "NONE". If they didn't change anything but instead just
tapped "Next", the server settings would be checked again and a dialog
would pop up saying, "Cannot connect to server. (STARTTLS connection
security not available)". (The implementation of notifications when
STARTTLS is not available is not actually included here -- it's in the
commit that follows.)
Regarding the changes to providers.xml: in cases where the scheme ended
with "+ssl", the schemes were simply updated by appending "+". In cases
where the scheme ended with "+tls", a check of the server was made to
assure that STARTTLS was available before appending "+" to the scheme.
Domains paran.com and nate.com failed the check and were removed because
no current information could be found. Domains me.com and mac.com also
failed and were updated based on http://support.apple.com/kb/ht4864.
2014-02-26 16:50:21 -05:00
|
|
|
/*
|
|
|
|
* Currently available schemes are:
|
|
|
|
* webdav
|
|
|
|
* webdav+ssl+
|
|
|
|
*
|
|
|
|
* The following are obsolete schemes that may be found in pre-existing
|
|
|
|
* settings from earlier versions or that may be found when imported. We
|
|
|
|
* continue to recognize them and re-map them appropriately:
|
|
|
|
* webdav+tls
|
2014-02-26 17:59:29 -05:00
|
|
|
* webdav+tls+
|
Eliminate the 'if available' connection security options
These options originated in the AOSP email client from which K-9 Mail was
forked. They provide an odd combination of 2 features:
1. Don't bother to authenticate the server's certificate (applies to both
SSL/TLS and STARTTLS); i.e., blindly accept all certificates. This is
generally a bad security policy which is susceptible to MITM attacks.
2. If STARTTLS is selected but the server doesn't claim to support
STARTTLS, then proceed without using encryption. This, too, is a bad
security policy which is susceptible to MITM attacks.
Since the time that K-9 Mail was forked, a couple things have changed:
> K-9 Mail has implemented the ability for users to review and permanently
accept individual certificates that would otherwise fail authentication.
With this ability, there is no need for a user to subject themselves to
the ongoing risks of feature 1. above. Hence, this commit removes feature
1.
> The AOSP email client has changed its behavior and no longer permits a
security downgrade to an unencrypted connection if the server doesn't
claim to support STARTTLS (i.e., they eliminated feature 2. above). K-9
Mail should do the same. It's unlikely that a server is going to provide
STARTTLS on an intermittent basis, so providing a contingency for such
unusual behavior is an unnecessary risk. Hence, this commit removes that
feature as well.
Effect on existing users:
If the old connection security setting was "SSL/TLS (if available)" (which
now gets remapped to "SSL/TLS"), and the server does not provide a
certificate that can be authenticated, then a "Certificate error for
<account name>" notification is generated telling the user to check their
server settings. Tapping the notification takes the user to the relevant
server settings, where the user can tap "Next" to review the certificate
and choose to permanently accept it. This process would occur during the
first syncing of folders after application upgrade or (in the case of
SMTP) during the first attempt to send a message.
If the connection security setting was "STARTTLS (if available)" (which
now gets remapped to "STARTTLS"), and the server does not provide a
certificate that can be authenticated, then the same process as above
would occur.
If the old connection security setting was "STARTTLS (if available)", and
the server doesn't claim to support STARTTLS, then the user would get a
certificate error notification which would lead them to the server's
settings. There they would need to choose a different connection security
-- most likely "NONE". If they didn't change anything but instead just
tapped "Next", the server settings would be checked again and a dialog
would pop up saying, "Cannot connect to server. (STARTTLS connection
security not available)". (The implementation of notifications when
STARTTLS is not available is not actually included here -- it's in the
commit that follows.)
Regarding the changes to providers.xml: in cases where the scheme ended
with "+ssl", the schemes were simply updated by appending "+". In cases
where the scheme ended with "+tls", a check of the server was made to
assure that STARTTLS was available before appending "+" to the scheme.
Domains paran.com and nate.com failed the check and were removed because
no current information could be found. Domains me.com and mac.com also
failed and were updated based on http://support.apple.com/kb/ht4864.
2014-02-26 16:50:21 -05:00
|
|
|
* webdav+ssl
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
if (scheme.equals("webdav")) {
|
2011-06-06 18:08:43 -04:00
|
|
|
connectionSecurity = ConnectionSecurity.NONE;
|
2014-02-26 17:59:29 -05:00
|
|
|
} else if (scheme.startsWith("webdav+")) {
|
2011-06-06 18:08:43 -04:00
|
|
|
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2011-06-06 18:08:43 -04:00
|
|
|
throw new IllegalArgumentException("Unsupported protocol (" + scheme + ")");
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
2011-06-06 18:08:43 -04:00
|
|
|
host = webDavUri.getHost();
|
|
|
|
if (host.startsWith("http")) {
|
|
|
|
String[] hostParts = host.split("://", 2);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (hostParts.length > 1) {
|
2011-06-06 18:08:43 -04:00
|
|
|
host = hostParts[1];
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-06 18:08:43 -04:00
|
|
|
port = webDavUri.getPort();
|
|
|
|
|
|
|
|
String userInfo = webDavUri.getUserInfo();
|
|
|
|
if (userInfo != null) {
|
2014-09-28 06:59:11 -04:00
|
|
|
String[] userInfoParts = userInfo.split(":");
|
|
|
|
username = com.fsck.k9.helper.UrlEncodingHelper.decodeUtf8(userInfoParts[0]);
|
|
|
|
String userParts[] = username.split("\\\\", 2);
|
2010-10-23 21:03:29 -04:00
|
|
|
|
2014-09-28 06:59:11 -04:00
|
|
|
if (userParts.length > 1) {
|
|
|
|
alias = userParts[1];
|
|
|
|
} else {
|
|
|
|
alias = username;
|
|
|
|
}
|
|
|
|
if (userInfoParts.length > 1) {
|
|
|
|
password = com.fsck.k9.helper.UrlEncodingHelper.decodeUtf8(userInfoParts[1]);
|
2010-10-23 21:03:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-06 18:08:43 -04:00
|
|
|
String[] pathParts = webDavUri.getPath().split("\\|");
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = pathParts.length; i < count; i++) {
|
|
|
|
if (i == 0) {
|
2009-01-31 17:09:16 -05:00
|
|
|
if (pathParts[0] != null &&
|
2011-02-06 17:09:48 -05:00
|
|
|
pathParts[0].length() > 1) {
|
2011-06-06 18:08:43 -04:00
|
|
|
path = pathParts[0];
|
2009-01-25 02:37:10 -05:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (i == 1) {
|
2009-01-31 17:09:16 -05:00
|
|
|
if (pathParts[1] != null &&
|
2011-02-06 17:09:48 -05:00
|
|
|
pathParts[1].length() > 1) {
|
2011-06-06 18:08:43 -04:00
|
|
|
authPath = pathParts[1];
|
2009-01-25 02:37:10 -05:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (i == 2) {
|
2009-01-31 17:09:16 -05:00
|
|
|
if (pathParts[2] != null &&
|
2011-02-06 17:09:48 -05:00
|
|
|
pathParts[2].length() > 1) {
|
2011-06-06 18:08:43 -04:00
|
|
|
mailboxPath = pathParts[2];
|
2009-01-25 02:37:10 -05:00
|
|
|
}
|
|
|
|
}
|
2009-01-21 00:27:22 -05:00
|
|
|
}
|
2010-10-23 21:03:29 -04:00
|
|
|
|
2011-06-06 21:01:02 -04:00
|
|
|
return new WebDavStoreSettings(host, port, connectionSecurity, null, username, password,
|
2014-05-25 16:45:14 -04:00
|
|
|
null, alias, path, authPath, mailboxPath);
|
2011-06-06 18:08:43 -04:00
|
|
|
}
|
|
|
|
|
2011-06-08 23:50:43 -04:00
|
|
|
/**
|
|
|
|
* Creates a WebDavStore URI with the supplied settings.
|
|
|
|
*
|
|
|
|
* @param server
|
|
|
|
* The {@link ServerSettings} object that holds the server settings.
|
|
|
|
*
|
|
|
|
* @return A WebDavStore URI that holds the same information as the {@code server} parameter.
|
|
|
|
*
|
|
|
|
* @see Account#getStoreUri()
|
|
|
|
* @see WebDavStore#decodeUri(String)
|
|
|
|
*/
|
|
|
|
public static String createUri(ServerSettings server) {
|
2014-09-28 06:59:11 -04:00
|
|
|
String userEnc = com.fsck.k9.helper.UrlEncodingHelper.encodeUtf8(server.username);
|
|
|
|
String passwordEnc = (server.password != null) ?
|
|
|
|
com.fsck.k9.helper.UrlEncodingHelper.encodeUtf8(server.password) : "";
|
2011-06-08 23:50:43 -04:00
|
|
|
|
|
|
|
String scheme;
|
|
|
|
switch (server.connectionSecurity) {
|
|
|
|
case SSL_TLS_REQUIRED:
|
|
|
|
scheme = "webdav+ssl+";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case NONE:
|
|
|
|
scheme = "webdav";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
String userInfo = userEnc + ":" + passwordEnc;
|
2011-10-12 20:35:08 -04:00
|
|
|
|
|
|
|
String uriPath;
|
|
|
|
Map<String, String> extra = server.getExtra();
|
|
|
|
if (extra != null) {
|
|
|
|
String path = extra.get(WebDavStoreSettings.PATH_KEY);
|
|
|
|
path = (path != null) ? path : "";
|
|
|
|
String authPath = extra.get(WebDavStoreSettings.AUTH_PATH_KEY);
|
|
|
|
authPath = (authPath != null) ? authPath : "";
|
|
|
|
String mailboxPath = extra.get(WebDavStoreSettings.MAILBOX_PATH_KEY);
|
|
|
|
mailboxPath = (mailboxPath != null) ? mailboxPath : "";
|
2011-11-29 15:45:52 -05:00
|
|
|
uriPath = "/" + path + "|" + authPath + "|" + mailboxPath;
|
2011-10-12 20:35:08 -04:00
|
|
|
} else {
|
2011-11-29 15:45:52 -05:00
|
|
|
uriPath = "/||";
|
2011-10-12 20:35:08 -04:00
|
|
|
}
|
|
|
|
|
2011-06-08 23:50:43 -04:00
|
|
|
try {
|
|
|
|
return new URI(scheme, userInfo, server.host, server.port, uriPath,
|
|
|
|
null, null).toString();
|
|
|
|
} catch (URISyntaxException e) {
|
|
|
|
throw new IllegalArgumentException("Can't create WebDavStore URI", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-06 18:08:43 -04:00
|
|
|
/**
|
|
|
|
* This class is used to store the decoded contents of an WebDavStore URI.
|
|
|
|
*
|
|
|
|
* @see WebDavStore#decodeUri(String)
|
|
|
|
*/
|
2011-11-30 20:28:57 -05:00
|
|
|
public static class WebDavStoreSettings extends ServerSettings {
|
|
|
|
public static final String ALIAS_KEY = "alias";
|
|
|
|
public static final String PATH_KEY = "path";
|
|
|
|
public static final String AUTH_PATH_KEY = "authPath";
|
|
|
|
public static final String MAILBOX_PATH_KEY = "mailboxPath";
|
2011-06-06 18:08:43 -04:00
|
|
|
|
|
|
|
public final String alias;
|
|
|
|
public final String path;
|
|
|
|
public final String authPath;
|
|
|
|
public final String mailboxPath;
|
|
|
|
|
|
|
|
protected WebDavStoreSettings(String host, int port, ConnectionSecurity connectionSecurity,
|
2014-05-25 16:45:14 -04:00
|
|
|
AuthType authenticationType, String username, String password, String clientCertificateAlias, String alias,
|
2011-06-06 21:01:02 -04:00
|
|
|
String path, String authPath, String mailboxPath) {
|
2011-06-06 22:07:50 -04:00
|
|
|
super(STORE_TYPE, host, port, connectionSecurity, authenticationType, username,
|
2014-05-25 16:45:14 -04:00
|
|
|
password, clientCertificateAlias);
|
2011-06-06 18:08:43 -04:00
|
|
|
this.alias = alias;
|
|
|
|
this.path = path;
|
|
|
|
this.authPath = authPath;
|
|
|
|
this.mailboxPath = mailboxPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Map<String, String> getExtra() {
|
|
|
|
Map<String, String> extra = new HashMap<String, String>();
|
2011-06-06 22:07:50 -04:00
|
|
|
putIfNotNull(extra, ALIAS_KEY, alias);
|
|
|
|
putIfNotNull(extra, PATH_KEY, path);
|
|
|
|
putIfNotNull(extra, AUTH_PATH_KEY, authPath);
|
|
|
|
putIfNotNull(extra, MAILBOX_PATH_KEY, mailboxPath);
|
2011-06-06 18:08:43 -04:00
|
|
|
return extra;
|
|
|
|
}
|
2011-10-16 22:34:26 -04:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public ServerSettings newPassword(String newPassword) {
|
|
|
|
return new WebDavStoreSettings(host, port, connectionSecurity, authenticationType,
|
2014-05-25 16:45:14 -04:00
|
|
|
username, newPassword, clientCertificateAlias, alias, path, authPath, mailboxPath);
|
2011-10-16 22:34:26 -04:00
|
|
|
}
|
2011-06-06 18:08:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-13 19:43:24 -05:00
|
|
|
private ConnectionSecurity mConnectionSecurity;
|
2011-06-06 18:08:43 -04:00
|
|
|
private String mUsername; /* Stores the username for authentications */
|
|
|
|
private String mAlias; /* Stores the alias for the user's mailbox */
|
|
|
|
private String mPassword; /* Stores the password for authentications */
|
|
|
|
private String mUrl; /* Stores the base URL for the server */
|
|
|
|
private String mHost; /* Stores the host name for the server */
|
2011-06-06 21:01:02 -04:00
|
|
|
private int mPort;
|
2011-06-06 18:08:43 -04:00
|
|
|
private String mPath; /* Stores the path for the server */
|
|
|
|
private String mAuthPath; /* Stores the path off of the server to post data to for form based authentication */
|
|
|
|
private String mMailboxPath; /* Stores the user specified path to the mailbox */
|
|
|
|
|
|
|
|
private WebDavHttpClient mHttpClient = null;
|
|
|
|
private HttpContext mContext = null;
|
|
|
|
private String mAuthString;
|
|
|
|
private CookieStore mAuthCookies = null;
|
|
|
|
private short mAuthentication = AUTH_TYPE_NONE;
|
|
|
|
private String mCachedLoginUrl;
|
|
|
|
|
2011-10-14 14:33:25 -04:00
|
|
|
private Folder mSendFolder = null;
|
2011-06-06 18:08:43 -04:00
|
|
|
private HashMap<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
|
|
|
|
|
2011-10-14 14:33:25 -04:00
|
|
|
|
2011-06-06 18:08:43 -04:00
|
|
|
public WebDavStore(Account account) throws MessagingException {
|
|
|
|
super(account);
|
|
|
|
|
|
|
|
WebDavStoreSettings settings;
|
|
|
|
try {
|
|
|
|
settings = decodeUri(mAccount.getStoreUri());
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
throw new MessagingException("Error while decoding store URI", e);
|
|
|
|
}
|
|
|
|
|
|
|
|
mHost = settings.host;
|
2011-06-06 21:01:02 -04:00
|
|
|
mPort = settings.port;
|
2011-06-06 18:08:43 -04:00
|
|
|
|
2014-02-13 19:43:24 -05:00
|
|
|
mConnectionSecurity = settings.connectionSecurity;
|
2011-06-06 18:08:43 -04:00
|
|
|
|
|
|
|
mUsername = settings.username;
|
|
|
|
mPassword = settings.password;
|
|
|
|
mAlias = settings.alias;
|
|
|
|
|
|
|
|
mPath = settings.path;
|
|
|
|
mAuthPath = settings.authPath;
|
|
|
|
mMailboxPath = settings.mailboxPath;
|
|
|
|
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mPath == null || mPath.equals("")) {
|
2010-12-17 09:40:19 -05:00
|
|
|
mPath = "/Exchange";
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (!mPath.startsWith("/")) {
|
2010-10-23 21:03:29 -04:00
|
|
|
mPath = "/" + mPath;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mMailboxPath == null || mMailboxPath.equals("")) {
|
2010-12-15 12:00:54 -05:00
|
|
|
mMailboxPath = "/" + mAlias;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (!mMailboxPath.startsWith("/")) {
|
2010-10-23 21:03:29 -04:00
|
|
|
mMailboxPath = "/" + mMailboxPath;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2010-10-23 21:03:29 -04:00
|
|
|
|
2010-12-17 09:40:19 -05:00
|
|
|
if (mAuthPath != null &&
|
|
|
|
!mAuthPath.equals("") &&
|
2011-02-06 17:09:48 -05:00
|
|
|
!mAuthPath.startsWith("/")) {
|
2010-12-17 09:40:19 -05:00
|
|
|
mAuthPath = "/" + mAuthPath;
|
2010-12-15 12:00:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// The URL typically looks like the following: "https://mail.domain.com/Exchange/alias".
|
|
|
|
// The inbox path would look like: "https://mail.domain.com/Exchange/alias/Inbox".
|
|
|
|
mUrl = getRoot() + mPath + mMailboxPath;
|
2010-10-23 21:03:29 -04:00
|
|
|
|
2010-12-15 12:00:54 -05:00
|
|
|
mAuthString = "Basic " + Utility.base64Encode(mUsername + ":" + mPassword);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String getRoot() {
|
2009-10-24 22:58:26 -04:00
|
|
|
String root;
|
2014-02-26 17:59:29 -05:00
|
|
|
if (mConnectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) {
|
2009-11-24 19:40:29 -05:00
|
|
|
root = "https";
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-11-24 19:40:29 -05:00
|
|
|
root = "http";
|
|
|
|
}
|
2011-06-06 21:01:02 -04:00
|
|
|
root += "://" + mHost + ":" + mPort;
|
2009-10-24 22:58:26 -04:00
|
|
|
return root;
|
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void checkSettings() throws MessagingException {
|
2010-10-23 21:03:29 -04:00
|
|
|
authenticate();
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException {
|
2010-04-21 22:20:35 -04:00
|
|
|
LinkedList<Folder> folderList = new LinkedList<Folder>();
|
2011-04-05 05:31:17 -04:00
|
|
|
/**
|
|
|
|
* We have to check authentication here so we have the proper URL stored
|
|
|
|
*/
|
|
|
|
getHttpClient();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Firstly we get the "special" folders list (inbox, outbox, etc)
|
|
|
|
* and setup the account accordingly
|
|
|
|
*/
|
2008-12-26 01:47:00 -05:00
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
2009-01-17 21:43:15 -05:00
|
|
|
DataSet dataset = new DataSet();
|
2011-04-05 05:31:17 -04:00
|
|
|
headers.put("Depth", "0");
|
|
|
|
headers.put("Brief", "t");
|
|
|
|
dataset = processRequest(this.mUrl, "PROPFIND", getSpecialFoldersList(), headers);
|
|
|
|
|
|
|
|
HashMap<String, String> specialFoldersMap = dataset.getSpecialFolderToUrl();
|
|
|
|
String folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_INBOX_FOLDER));
|
2011-04-12 08:17:22 -04:00
|
|
|
if (folderName != null) {
|
|
|
|
mAccount.setAutoExpandFolderName(folderName);
|
|
|
|
mAccount.setInboxFolderName(folderName);
|
2011-04-05 05:31:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_DRAFTS_FOLDER));
|
2011-04-12 08:17:22 -04:00
|
|
|
if (folderName != null)
|
|
|
|
mAccount.setDraftsFolderName(folderName);
|
2008-12-26 01:47:00 -05:00
|
|
|
|
2011-04-05 05:31:17 -04:00
|
|
|
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_TRASH_FOLDER));
|
2011-04-12 08:17:22 -04:00
|
|
|
if (folderName != null)
|
|
|
|
mAccount.setTrashFolderName(folderName);
|
2011-04-05 05:31:17 -04:00
|
|
|
|
|
|
|
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SPAM_FOLDER));
|
2011-04-12 08:17:22 -04:00
|
|
|
if (folderName != null)
|
|
|
|
mAccount.setSpamFolderName(folderName);
|
2011-04-05 05:31:17 -04:00
|
|
|
|
2011-05-07 17:57:47 -04:00
|
|
|
// K-9 Mail's outbox is a special local folder and different from Exchange/WebDAV's outbox.
|
|
|
|
/*
|
2011-04-05 05:31:17 -04:00
|
|
|
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_OUTBOX_FOLDER));
|
2011-04-12 08:17:22 -04:00
|
|
|
if (folderName != null)
|
|
|
|
mAccount.setOutboxFolderName(folderName);
|
2011-05-07 17:57:47 -04:00
|
|
|
*/
|
2011-04-05 05:31:17 -04:00
|
|
|
|
|
|
|
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SENT_FOLDER));
|
2011-04-12 08:17:22 -04:00
|
|
|
if (folderName != null)
|
|
|
|
mAccount.setSentFolderName(folderName);
|
2011-02-15 19:05:08 -05:00
|
|
|
|
2008-12-26 01:47:00 -05:00
|
|
|
/**
|
2011-04-05 05:31:17 -04:00
|
|
|
* Next we get all the folders (including "special" ones)
|
2008-12-26 01:47:00 -05:00
|
|
|
*/
|
2011-04-05 05:31:17 -04:00
|
|
|
headers = new HashMap<String, String>();
|
|
|
|
dataset = new DataSet();
|
2008-12-26 01:47:00 -05:00
|
|
|
headers.put("Brief", "t");
|
2011-04-05 05:31:17 -04:00
|
|
|
dataset = processRequest(this.mUrl, "SEARCH", getFolderListXml(), headers);
|
|
|
|
String[] folderUrls = dataset.getHrefs();
|
2008-12-26 01:47:00 -05:00
|
|
|
|
2014-02-15 16:42:20 -05:00
|
|
|
for (String tempUrl : folderUrls) {
|
2011-04-08 07:10:20 -04:00
|
|
|
WebDavFolder folder = createFolder(tempUrl);
|
2011-04-12 08:17:22 -04:00
|
|
|
if (folder != null)
|
|
|
|
folderList.add(folder);
|
2008-12-26 01:47:00 -05:00
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2010-04-21 22:20:35 -04:00
|
|
|
return folderList;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
2011-04-08 07:10:20 -04:00
|
|
|
/**
|
|
|
|
* Creates a folder using the URL passed as parameter (only if it has not been
|
|
|
|
* already created) and adds this to our store folder map.
|
|
|
|
*
|
|
|
|
* @param folderUrl
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
private WebDavFolder createFolder(String folderUrl) {
|
2011-04-12 08:17:22 -04:00
|
|
|
if (folderUrl == null)
|
|
|
|
return null;
|
2011-04-05 05:31:17 -04:00
|
|
|
|
2011-04-12 08:17:22 -04:00
|
|
|
WebDavFolder wdFolder = null;
|
|
|
|
String folderName = getFolderName(folderUrl);
|
|
|
|
if (folderName != null) {
|
|
|
|
if (!this.mFolderList.containsKey(folderName)) {
|
2011-04-05 05:31:17 -04:00
|
|
|
wdFolder = new WebDavFolder(this, folderName);
|
|
|
|
wdFolder.setUrl(folderUrl);
|
2011-04-12 08:17:22 -04:00
|
|
|
mFolderList.put(folderName, wdFolder);
|
|
|
|
}
|
2011-04-05 05:31:17 -04:00
|
|
|
}
|
2011-04-12 08:17:22 -04:00
|
|
|
// else: Unknown URL format => NO Folder created
|
2011-04-05 05:31:17 -04:00
|
|
|
|
2011-04-12 08:17:22 -04:00
|
|
|
return wdFolder;
|
|
|
|
}
|
2011-04-05 05:31:17 -04:00
|
|
|
|
|
|
|
private String getFolderName(String folderUrl) {
|
2011-04-12 08:17:22 -04:00
|
|
|
if (folderUrl == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
// Here we extract the folder name starting from the complete url.
|
|
|
|
// folderUrl is in the form http://mail.domain.com/exchange/username/foldername
|
|
|
|
// so we need "foldername" which is the string after the fifth slash
|
|
|
|
int folderSlash = -1;
|
|
|
|
for (int j = 0; j < 5; j++) {
|
|
|
|
folderSlash = folderUrl.indexOf('/', folderSlash + 1);
|
|
|
|
if (folderSlash < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (folderSlash > 0) {
|
|
|
|
String fullPathName;
|
|
|
|
|
|
|
|
// Removes the final slash if present
|
|
|
|
if (folderUrl.charAt(folderUrl.length() - 1) == '/')
|
|
|
|
fullPathName = folderUrl.substring(folderSlash + 1, folderUrl.length() - 1);
|
|
|
|
else
|
|
|
|
fullPathName = folderUrl.substring(folderSlash + 1);
|
|
|
|
|
|
|
|
// Decodes the url-encoded folder name (i.e. "My%20folder" => "My Folder"
|
|
|
|
|
2014-09-28 06:59:11 -04:00
|
|
|
return UrlEncodingHelper.decodeUtf8(fullPathName);
|
2011-04-12 08:17:22 -04:00
|
|
|
}
|
2011-04-05 05:31:17 -04:00
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Folder getFolder(String name) {
|
2008-12-06 19:29:11 -05:00
|
|
|
WebDavFolder folder;
|
2008-12-14 21:18:02 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if ((folder = this.mFolderList.get(name)) == null) {
|
2009-10-24 22:58:26 -04:00
|
|
|
folder = new WebDavFolder(this, name);
|
2008-12-14 21:18:02 -05:00
|
|
|
}
|
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
return folder;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public Folder getSendSpoolFolder() throws MessagingException {
|
2011-04-12 08:17:22 -04:00
|
|
|
if (mSendFolder == null)
|
|
|
|
mSendFolder = getFolder(DAV_MAIL_SEND_FOLDER);
|
2011-04-05 05:31:17 -04:00
|
|
|
|
2011-04-12 08:17:22 -04:00
|
|
|
return mSendFolder;
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isMoveCapable() {
|
2009-11-24 19:40:29 -05:00
|
|
|
return true;
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isCopyCapable() {
|
2009-11-24 19:40:29 -05:00
|
|
|
return true;
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-04-12 08:17:22 -04:00
|
|
|
private String getSpecialFoldersList() {
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder buffer = new StringBuilder(200);
|
2011-04-12 08:17:22 -04:00
|
|
|
buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>");
|
|
|
|
buffer.append("<propfind xmlns=\"DAV:\">");
|
|
|
|
buffer.append("<prop>");
|
|
|
|
buffer.append("<").append(DAV_MAIL_INBOX_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>");
|
|
|
|
buffer.append("<").append(DAV_MAIL_DRAFTS_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>");
|
|
|
|
buffer.append("<").append(DAV_MAIL_OUTBOX_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>");
|
|
|
|
buffer.append("<").append(DAV_MAIL_SENT_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>");
|
|
|
|
buffer.append("<").append(DAV_MAIL_TRASH_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>");
|
|
|
|
// This should always be ##DavMailSubmissionURI## for which we already have a constant
|
|
|
|
// buffer.append("<sendmsg xmlns=\"urn:schemas:httpmail:\"/>");
|
|
|
|
|
|
|
|
buffer.append("<").append(DAV_MAIL_SPAM_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>");
|
|
|
|
|
|
|
|
buffer.append("</prop>");
|
|
|
|
buffer.append("</propfind>");
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
2011-04-05 05:31:17 -04:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/***************************************************************
|
|
|
|
* WebDAV XML Request body retrieval functions
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
private String getFolderListXml() {
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder buffer = new StringBuilder(200);
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append("<?xml version='1.0' ?>");
|
|
|
|
buffer.append("<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n");
|
2009-01-17 21:43:15 -05:00
|
|
|
buffer.append("SELECT \"DAV:uid\", \"DAV:ishidden\"\r\n");
|
2010-11-30 22:06:50 -05:00
|
|
|
buffer.append(" FROM SCOPE('hierarchical traversal of \"").append(this.mUrl).append("\"')\r\n");
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=True\r\n");
|
|
|
|
buffer.append("</a:sql></a:searchrequest>\r\n");
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String getMessageCountXml(String messageState) {
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder buffer = new StringBuilder(200);
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append("<?xml version='1.0' ?>");
|
|
|
|
buffer.append("<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n");
|
|
|
|
buffer.append("SELECT \"DAV:visiblecount\"\r\n");
|
|
|
|
buffer.append(" FROM \"\"\r\n");
|
2010-12-17 09:40:19 -05:00
|
|
|
buffer.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=False AND \"urn:schemas:httpmail:read\"=")
|
2010-12-24 13:55:05 -05:00
|
|
|
.append(messageState).append("\r\n");
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append(" GROUP BY \"DAV:ishidden\"\r\n");
|
|
|
|
buffer.append("</a:sql></a:searchrequest>\r\n");
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String getMessageEnvelopeXml(String[] uids) {
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder buffer = new StringBuilder(200);
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append("<?xml version='1.0' ?>");
|
|
|
|
buffer.append("<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n");
|
|
|
|
buffer.append("SELECT \"DAV:uid\", \"DAV:getcontentlength\",");
|
|
|
|
buffer.append(" \"urn:schemas:mailheader:mime-version\",");
|
|
|
|
buffer.append(" \"urn:schemas:mailheader:content-type\",");
|
|
|
|
buffer.append(" \"urn:schemas:mailheader:subject\",");
|
|
|
|
buffer.append(" \"urn:schemas:mailheader:date\",");
|
|
|
|
buffer.append(" \"urn:schemas:mailheader:thread-topic\",");
|
|
|
|
buffer.append(" \"urn:schemas:mailheader:thread-index\",");
|
|
|
|
buffer.append(" \"urn:schemas:mailheader:from\",");
|
|
|
|
buffer.append(" \"urn:schemas:mailheader:to\",");
|
|
|
|
buffer.append(" \"urn:schemas:mailheader:in-reply-to\",");
|
|
|
|
buffer.append(" \"urn:schemas:mailheader:cc\",");
|
|
|
|
buffer.append(" \"urn:schemas:httpmail:read\"");
|
|
|
|
buffer.append(" \r\n");
|
|
|
|
buffer.append(" FROM \"\"\r\n");
|
|
|
|
buffer.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=False AND ");
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = uids.length; i < count; i++) {
|
|
|
|
if (i != 0) {
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append(" OR ");
|
|
|
|
}
|
2010-11-30 22:06:50 -05:00
|
|
|
buffer.append(" \"DAV:uid\"='").append(uids[i]).append("' ");
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
buffer.append("\r\n");
|
|
|
|
buffer.append("</a:sql></a:searchrequest>\r\n");
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String getMessagesXml() {
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder buffer = new StringBuilder(200);
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append("<?xml version='1.0' ?>");
|
|
|
|
buffer.append("<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n");
|
|
|
|
buffer.append("SELECT \"DAV:uid\"\r\n");
|
|
|
|
buffer.append(" FROM \"\"\r\n");
|
|
|
|
buffer.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=False\r\n");
|
|
|
|
buffer.append("</a:sql></a:searchrequest>\r\n");
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String getMessageUrlsXml(String[] uids) {
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder buffer = new StringBuilder(600);
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append("<?xml version='1.0' ?>");
|
|
|
|
buffer.append("<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n");
|
2009-01-17 21:43:15 -05:00
|
|
|
buffer.append("SELECT \"urn:schemas:httpmail:read\", \"DAV:uid\"\r\n");
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append(" FROM \"\"\r\n");
|
|
|
|
buffer.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=False AND ");
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = uids.length; i < count; i++) {
|
|
|
|
if (i != 0) {
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append(" OR ");
|
|
|
|
}
|
|
|
|
|
2010-11-30 22:06:50 -05:00
|
|
|
buffer.append(" \"DAV:uid\"='").append(uids[i]).append("' ");
|
2008-12-06 19:29:11 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
buffer.append("\r\n");
|
|
|
|
buffer.append("</a:sql></a:searchrequest>\r\n");
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String getMessageFlagsXml(String[] uids) throws MessagingException {
|
|
|
|
if (uids.length == 0) {
|
2008-12-06 19:29:11 -05:00
|
|
|
throw new MessagingException("Attempt to get flags on 0 length array for uids");
|
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder buffer = new StringBuilder(200);
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append("<?xml version='1.0' ?>");
|
|
|
|
buffer.append("<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n");
|
|
|
|
buffer.append("SELECT \"urn:schemas:httpmail:read\", \"DAV:uid\"\r\n");
|
|
|
|
buffer.append(" FROM \"\"\r\n");
|
|
|
|
buffer.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=False AND ");
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = uids.length; i < count; i++) {
|
|
|
|
if (i != 0) {
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append(" OR ");
|
|
|
|
}
|
2010-11-30 22:06:50 -05:00
|
|
|
buffer.append(" \"DAV:uid\"='").append(uids[i]).append("' ");
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
buffer.append("\r\n");
|
|
|
|
buffer.append("</a:sql></a:searchrequest>\r\n");
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String getMarkMessagesReadXml(String[] urls, boolean read) {
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder buffer = new StringBuilder(600);
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append("<?xml version='1.0' ?>\r\n");
|
|
|
|
buffer.append("<a:propertyupdate xmlns:a='DAV:' xmlns:b='urn:schemas:httpmail:'>\r\n");
|
|
|
|
buffer.append("<a:target>\r\n");
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String url : urls) {
|
2010-11-30 22:06:50 -05:00
|
|
|
buffer.append(" <a:href>").append(url).append("</a:href>\r\n");
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
buffer.append("</a:target>\r\n");
|
|
|
|
buffer.append("<a:set>\r\n");
|
|
|
|
buffer.append(" <a:prop>\r\n");
|
2010-11-30 22:06:50 -05:00
|
|
|
buffer.append(" <b:read>").append(read ? "1" : "0").append("</b:read>\r\n");
|
2008-12-06 19:29:11 -05:00
|
|
|
buffer.append(" </a:prop>\r\n");
|
|
|
|
buffer.append("</a:set>\r\n");
|
|
|
|
buffer.append("</a:propertyupdate>\r\n");
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
// For flag:
|
2010-12-17 09:40:19 -05:00
|
|
|
// http://www.devnewsgroups.net/group/microsoft.public.exchange.development/topic27175.aspx
|
2010-10-23 21:02:46 -04:00
|
|
|
// "<m:0x10900003>1</m:0x10900003>" & _
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String getMoveOrCopyMessagesReadXml(String[] urls, boolean isMove) {
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
String action = (isMove ? "move" : "copy");
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder buffer = new StringBuilder(600);
|
2009-10-24 22:58:26 -04:00
|
|
|
buffer.append("<?xml version='1.0' ?>\r\n");
|
2010-11-30 22:06:50 -05:00
|
|
|
buffer.append("<a:").append(action).append(" xmlns:a='DAV:' xmlns:b='urn:schemas:httpmail:'>\r\n");
|
2009-10-24 22:58:26 -04:00
|
|
|
buffer.append("<a:target>\r\n");
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String url : urls) {
|
2010-11-30 22:06:50 -05:00
|
|
|
buffer.append(" <a:href>").append(url).append("</a:href>\r\n");
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
|
|
|
buffer.append("</a:target>\r\n");
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-11-30 22:06:50 -05:00
|
|
|
buffer.append("</a:").append(action).append(">\r\n");
|
2009-10-24 22:58:26 -04:00
|
|
|
return buffer.toString();
|
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/***************************************************************
|
|
|
|
* Authentication related methods
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* Determines which type of authentication Exchange is using and authenticates appropriately.
|
2010-12-24 13:55:05 -05:00
|
|
|
*
|
2009-11-24 19:40:29 -05:00
|
|
|
* @throws MessagingException
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2010-12-17 09:40:19 -05:00
|
|
|
public boolean authenticate()
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
|
|
|
try {
|
|
|
|
if (mAuthentication == AUTH_TYPE_NONE) {
|
2010-10-23 21:03:29 -04:00
|
|
|
ConnectionInfo info = doInitialConnection();
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (info.requiredAuthType == AUTH_TYPE_BASIC) {
|
2010-10-23 21:03:29 -04:00
|
|
|
HttpGeneric request = new HttpGeneric(mUrl);
|
|
|
|
request.setMethod("GET");
|
|
|
|
request.setHeader("Authorization", mAuthString);
|
|
|
|
|
|
|
|
WebDavHttpClient httpClient = new WebDavHttpClient();
|
|
|
|
HttpResponse response = httpClient.executeOverride(request, mContext);
|
|
|
|
|
|
|
|
int statusCode = response.getStatusLine().getStatusCode();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (statusCode >= 200 && statusCode < 300) {
|
2010-10-23 21:03:29 -04:00
|
|
|
mAuthentication = AUTH_TYPE_BASIC;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (statusCode == 401) {
|
2010-10-23 21:03:29 -04:00
|
|
|
throw new MessagingException("Invalid username or password for authentication.");
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-10-23 21:03:29 -04:00
|
|
|
throw new MessagingException("Error with code " + response.getStatusLine().getStatusCode() +
|
2010-12-24 13:55:05 -05:00
|
|
|
" during request processing: " + response.getStatusLine().toString());
|
2010-10-23 21:03:29 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (info.requiredAuthType == AUTH_TYPE_FORM_BASED) {
|
2010-10-23 21:03:29 -04:00
|
|
|
doFBA(info);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (mAuthentication == AUTH_TYPE_BASIC) {
|
2010-10-23 21:03:29 -04:00
|
|
|
// Nothing to do, we authenticate with every request when
|
|
|
|
// using basic authentication.
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (mAuthentication == AUTH_TYPE_FORM_BASED) {
|
2010-10-23 21:03:29 -04:00
|
|
|
// Our cookie expired, re-authenticate.
|
|
|
|
doFBA(null);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Error during authentication: " + ioe + "\nStack: " + processException(ioe));
|
2009-10-24 22:58:26 -04:00
|
|
|
throw new MessagingException("Error during authentication", ioe);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2010-10-23 21:03:29 -04:00
|
|
|
return mAuthentication != AUTH_TYPE_NONE;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* Makes the initial connection to Exchange for authentication. Determines the type of authentication necessary for
|
|
|
|
* the server.
|
2010-12-24 13:55:05 -05:00
|
|
|
*
|
2010-10-23 21:03:29 -04:00
|
|
|
* @throws MessagingException
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2010-12-17 09:40:19 -05:00
|
|
|
private ConnectionInfo doInitialConnection()
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2010-10-23 21:03:29 -04:00
|
|
|
// For our initial connection we are sending an empty GET request to
|
|
|
|
// the configured URL, which should be in the following form:
|
|
|
|
// https://mail.server.com/Exchange/alias
|
|
|
|
//
|
|
|
|
// Possible status codes include:
|
2010-12-17 09:40:19 -05:00
|
|
|
// 401 - the server uses basic authentication
|
|
|
|
// 30x - the server is trying to redirect us to an OWA login
|
|
|
|
// 20x - success
|
2010-10-23 21:03:29 -04:00
|
|
|
//
|
|
|
|
// The latter two indicate form-based authentication.
|
|
|
|
ConnectionInfo info = new ConnectionInfo();
|
|
|
|
|
|
|
|
WebDavHttpClient httpClient = getHttpClient();
|
|
|
|
|
|
|
|
HttpGeneric request = new HttpGeneric(mUrl);
|
|
|
|
request.setMethod("GET");
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-10-23 21:03:29 -04:00
|
|
|
HttpResponse response = httpClient.executeOverride(request, mContext);
|
|
|
|
info.statusCode = response.getStatusLine().getStatusCode();
|
2009-01-08 00:47:10 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (info.statusCode == 401) {
|
2010-10-23 21:03:29 -04:00
|
|
|
// 401 is the "Unauthorized" status code, meaning the server wants
|
|
|
|
// an authentication header for basic authentication.
|
|
|
|
info.requiredAuthType = AUTH_TYPE_BASIC;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if ((info.statusCode >= 200 && info.statusCode < 300) || // Success
|
|
|
|
(info.statusCode >= 300 && info.statusCode < 400) || // Redirect
|
2011-02-13 19:47:30 -05:00
|
|
|
(info.statusCode == 440)) { // Unauthorized
|
2010-10-23 21:03:29 -04:00
|
|
|
// We will handle all 3 situations the same. First we take an educated
|
|
|
|
// guess at where the authorization DLL is located. If this is this
|
|
|
|
// doesn't work, then we'll use the redirection URL for OWA login given
|
2010-12-14 13:02:39 -05:00
|
|
|
// to us by exchange. We can use this to scrape the location of the
|
2010-10-23 21:03:29 -04:00
|
|
|
// authorization URL.
|
|
|
|
info.requiredAuthType = AUTH_TYPE_FORM_BASED;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mAuthPath != null && !mAuthPath.equals("")) {
|
2010-10-23 21:03:29 -04:00
|
|
|
// The user specified their own authentication path, use that.
|
|
|
|
info.guessedAuthUrl = getRoot() + mAuthPath;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-10-23 21:03:29 -04:00
|
|
|
// Use the default path to the authentication dll.
|
|
|
|
info.guessedAuthUrl = getRoot() + "/exchweb/bin/auth/owaauth.dll";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine where the server is trying to redirect us.
|
|
|
|
Header location = response.getFirstHeader("Location");
|
2011-02-06 17:09:48 -05:00
|
|
|
if (location != null) {
|
2010-10-23 21:03:29 -04:00
|
|
|
info.redirectUrl = location.getValue();
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-12-17 09:40:19 -05:00
|
|
|
throw new IOException("Error with code " + info.statusCode + " during request processing: " +
|
2010-12-24 13:55:05 -05:00
|
|
|
response.getStatusLine().toString());
|
2010-10-23 21:03:29 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (SSLException e) {
|
2013-01-17 09:24:22 -05:00
|
|
|
throw new CertificateValidationException(e.getMessage(), e);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2010-10-23 21:03:29 -04:00
|
|
|
Log.e(K9.LOG_TAG, "IOException: " + ioe + "\nTrace: " + processException(ioe));
|
|
|
|
throw new MessagingException("IOException", ioe);
|
2009-01-08 00:47:10 -05:00
|
|
|
}
|
2009-01-05 20:31:57 -05:00
|
|
|
|
2010-10-23 21:03:29 -04:00
|
|
|
return info;
|
2009-01-05 20:31:57 -05:00
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/**
|
2010-10-23 21:03:29 -04:00
|
|
|
* Performs form-based authentication.
|
2010-12-24 13:55:05 -05:00
|
|
|
*
|
2009-11-24 19:40:29 -05:00
|
|
|
* @throws MessagingException
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2010-12-17 09:40:19 -05:00
|
|
|
public void doFBA(ConnectionInfo info)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws IOException, MessagingException {
|
2010-12-14 13:02:39 -05:00
|
|
|
// Clear out cookies from any previous authentication.
|
|
|
|
mAuthCookies.clear();
|
2010-12-17 09:40:19 -05:00
|
|
|
|
2010-10-23 21:03:29 -04:00
|
|
|
WebDavHttpClient httpClient = getHttpClient();
|
2010-12-17 09:40:19 -05:00
|
|
|
|
2011-02-12 16:10:12 -05:00
|
|
|
String loginUrl;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (info != null) {
|
2010-12-14 13:02:39 -05:00
|
|
|
loginUrl = info.guessedAuthUrl;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (mCachedLoginUrl != null && !mCachedLoginUrl.equals("")) {
|
2010-12-14 13:02:39 -05:00
|
|
|
loginUrl = mCachedLoginUrl;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-12-14 13:02:39 -05:00
|
|
|
throw new MessagingException("No valid login URL available for form-based authentication.");
|
|
|
|
}
|
2010-10-23 21:03:29 -04:00
|
|
|
|
2010-12-14 13:02:39 -05:00
|
|
|
HttpGeneric request = new HttpGeneric(loginUrl);
|
2010-10-23 21:03:29 -04:00
|
|
|
request.setMethod("POST");
|
|
|
|
|
|
|
|
// Build the POST data.
|
|
|
|
ArrayList<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>();
|
|
|
|
pairs.add(new BasicNameValuePair("destination", mUrl));
|
|
|
|
pairs.add(new BasicNameValuePair("username", mUsername));
|
|
|
|
pairs.add(new BasicNameValuePair("password", mPassword));
|
|
|
|
pairs.add(new BasicNameValuePair("flags", "0"));
|
|
|
|
pairs.add(new BasicNameValuePair("SubmitCreds", "Log+On"));
|
|
|
|
pairs.add(new BasicNameValuePair("forcedownlevel", "0"));
|
|
|
|
pairs.add(new BasicNameValuePair("trusted", "0"));
|
|
|
|
|
|
|
|
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(pairs);
|
|
|
|
request.setEntity(formEntity);
|
|
|
|
|
|
|
|
HttpResponse response = httpClient.executeOverride(request, mContext);
|
2011-02-12 16:10:12 -05:00
|
|
|
boolean authenticated = testAuthenticationResponse(response);
|
|
|
|
if (!authenticated) {
|
|
|
|
// Check the response from the authentication request above for a form action.
|
2010-12-17 09:40:19 -05:00
|
|
|
String formAction = findFormAction(WebDavHttpClient.getUngzippedContent(response.getEntity()));
|
2011-02-06 17:09:48 -05:00
|
|
|
if (formAction == null) {
|
2010-12-17 09:40:19 -05:00
|
|
|
// If there is no form action, try using our redirect URL from the initial connection.
|
2011-02-06 17:09:48 -05:00
|
|
|
if (info != null && info.redirectUrl != null && !info.redirectUrl.equals("")) {
|
2010-12-17 09:40:19 -05:00
|
|
|
loginUrl = info.redirectUrl;
|
2010-10-23 21:03:29 -04:00
|
|
|
|
2010-12-17 09:40:19 -05:00
|
|
|
request = new HttpGeneric(loginUrl);
|
|
|
|
request.setMethod("GET");
|
2009-01-08 00:47:10 -05:00
|
|
|
|
2010-12-17 09:40:19 -05:00
|
|
|
response = httpClient.executeOverride(request, mContext);
|
|
|
|
formAction = findFormAction(WebDavHttpClient.getUngzippedContent(response.getEntity()));
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (formAction != null) {
|
|
|
|
try {
|
2010-12-17 09:40:19 -05:00
|
|
|
URI formActionUri = new URI(formAction);
|
|
|
|
URI loginUri = new URI(loginUrl);
|
2009-01-08 00:47:10 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (formActionUri.isAbsolute()) {
|
2010-12-17 09:40:19 -05:00
|
|
|
// The form action is an absolute URL, just use it.
|
|
|
|
loginUrl = formAction;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-12-17 09:40:19 -05:00
|
|
|
// Append the form action to our current URL, minus the file name.
|
2011-02-12 16:10:12 -05:00
|
|
|
String urlPath;
|
|
|
|
if (formAction.startsWith("/")) {
|
|
|
|
urlPath = formAction;
|
|
|
|
} else {
|
|
|
|
urlPath = loginUri.getPath();
|
|
|
|
int lastPathPos = urlPath.lastIndexOf('/');
|
|
|
|
if (lastPathPos > -1) {
|
|
|
|
urlPath = urlPath.substring(0, lastPathPos + 1);
|
|
|
|
urlPath = urlPath.concat(formAction);
|
|
|
|
}
|
2009-01-08 00:47:10 -05:00
|
|
|
}
|
2010-12-17 09:40:19 -05:00
|
|
|
|
|
|
|
// Reconstruct the login URL based on the original login URL and the form action.
|
|
|
|
URI finalUri = new URI(loginUri.getScheme(),
|
2010-12-24 13:55:05 -05:00
|
|
|
loginUri.getUserInfo(),
|
|
|
|
loginUri.getHost(),
|
|
|
|
loginUri.getPort(),
|
|
|
|
urlPath,
|
|
|
|
null,
|
|
|
|
null);
|
2010-12-17 09:40:19 -05:00
|
|
|
loginUrl = finalUri.toString();
|
2009-01-08 00:47:10 -05:00
|
|
|
}
|
|
|
|
|
2010-12-17 09:40:19 -05:00
|
|
|
// Retry the login using our new URL.
|
2010-10-23 21:03:29 -04:00
|
|
|
request = new HttpGeneric(loginUrl);
|
|
|
|
request.setMethod("POST");
|
|
|
|
request.setEntity(formEntity);
|
2011-02-13 19:47:30 -05:00
|
|
|
|
2011-02-12 16:10:12 -05:00
|
|
|
response = httpClient.executeOverride(request, mContext);
|
|
|
|
authenticated = testAuthenticationResponse(response);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (URISyntaxException e) {
|
2010-12-17 09:40:19 -05:00
|
|
|
Log.e(K9.LOG_TAG, "URISyntaxException caught " + e + "\nTrace: " + processException(e));
|
|
|
|
throw new MessagingException("URISyntaxException caught", e);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-12-17 09:40:19 -05:00
|
|
|
throw new MessagingException("A valid URL for Exchange authentication could not be found.");
|
2009-01-08 00:47:10 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
2010-10-23 21:03:29 -04:00
|
|
|
|
2011-02-12 16:10:12 -05:00
|
|
|
if (authenticated) {
|
2010-10-23 21:03:29 -04:00
|
|
|
mAuthentication = AUTH_TYPE_FORM_BASED;
|
2010-12-14 13:02:39 -05:00
|
|
|
mCachedLoginUrl = loginUrl;
|
2011-02-12 16:10:12 -05:00
|
|
|
} else {
|
|
|
|
throw new MessagingException("Invalid credentials provided for authentication.");
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-17 09:40:19 -05:00
|
|
|
/**
|
|
|
|
* Searches the specified stream for an HTML form and returns the form's action target.
|
2010-12-24 13:55:05 -05:00
|
|
|
*
|
2010-12-17 09:40:19 -05:00
|
|
|
* @throws IOException
|
|
|
|
*/
|
|
|
|
private String findFormAction(InputStream istream)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws IOException {
|
2010-12-17 09:40:19 -05:00
|
|
|
String formAction = null;
|
|
|
|
|
|
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(istream), 4096);
|
|
|
|
String tempText;
|
|
|
|
|
|
|
|
// Read line by line until we find something like: <form action="owaauth.dll"...>.
|
|
|
|
while ((tempText = reader.readLine()) != null &&
|
2011-02-06 17:09:48 -05:00
|
|
|
formAction == null) {
|
|
|
|
if (tempText.indexOf(" action=") > -1) {
|
2010-12-17 09:40:19 -05:00
|
|
|
String[] actionParts = tempText.split(" action=");
|
2011-02-06 17:09:48 -05:00
|
|
|
if (actionParts.length > 1 && actionParts[1].length() > 1) {
|
2010-12-17 09:40:19 -05:00
|
|
|
char openQuote = actionParts[1].charAt(0);
|
|
|
|
int closePos = actionParts[1].indexOf(openQuote, 1);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (closePos > 1) {
|
2010-12-17 09:40:19 -05:00
|
|
|
formAction = actionParts[1].substring(1, closePos);
|
|
|
|
// Remove any GET parameters.
|
|
|
|
int quesPos = formAction.indexOf('?');
|
2011-02-06 17:09:48 -05:00
|
|
|
if (quesPos != -1) {
|
2010-12-17 09:40:19 -05:00
|
|
|
formAction = formAction.substring(0, quesPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return formAction;
|
|
|
|
}
|
2011-02-13 19:47:30 -05:00
|
|
|
|
|
|
|
private boolean testAuthenticationResponse(HttpResponse response)
|
2011-02-12 16:10:12 -05:00
|
|
|
throws MessagingException {
|
|
|
|
boolean authenticated = false;
|
|
|
|
int statusCode = response.getStatusLine().getStatusCode();
|
|
|
|
|
|
|
|
// Exchange 2007 will return a 302 status code no matter what.
|
|
|
|
if (((statusCode >= 200 && statusCode < 300) || statusCode == 302) &&
|
|
|
|
mAuthCookies != null && !mAuthCookies.getCookies().isEmpty()) {
|
|
|
|
// We may be authenticated, we need to send a test request to know for sure.
|
|
|
|
// Exchange 2007 adds the same cookies whether the username and password were valid or not.
|
|
|
|
ConnectionInfo info = doInitialConnection();
|
|
|
|
if (info.statusCode >= 200 && info.statusCode < 300) {
|
|
|
|
authenticated = true;
|
|
|
|
} else if (info.statusCode == 302) {
|
|
|
|
// If we are successfully authenticated, Exchange will try to redirect us to our OWA inbox.
|
|
|
|
// Otherwise, it will redirect us to a logon page.
|
|
|
|
// Our URL is in the form: https://hostname:port/Exchange/alias.
|
|
|
|
// The redirect is in the form: https://hostname:port/owa/alias.
|
|
|
|
// Do a simple replace and compare the resulting strings.
|
|
|
|
try {
|
|
|
|
String thisPath = new URI(mUrl).getPath();
|
|
|
|
String redirectPath = new URI(info.redirectUrl).getPath();
|
2011-02-13 19:47:30 -05:00
|
|
|
|
2011-02-12 16:10:12 -05:00
|
|
|
if (!thisPath.endsWith("/")) {
|
|
|
|
thisPath = thisPath.concat("/");
|
|
|
|
}
|
|
|
|
if (!redirectPath.endsWith("/")) {
|
|
|
|
redirectPath = redirectPath.concat("/");
|
|
|
|
}
|
2011-02-13 19:47:30 -05:00
|
|
|
|
2011-02-12 16:10:12 -05:00
|
|
|
if (redirectPath.equalsIgnoreCase(thisPath)) {
|
|
|
|
authenticated = true;
|
|
|
|
} else {
|
|
|
|
int found = thisPath.indexOf('/', 1);
|
|
|
|
if (found != -1) {
|
|
|
|
String replace = thisPath.substring(0, found + 1);
|
|
|
|
redirectPath = redirectPath.replace("/owa/", replace);
|
|
|
|
if (redirectPath.equalsIgnoreCase(thisPath)) {
|
|
|
|
authenticated = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (URISyntaxException e) {
|
|
|
|
Log.e(K9.LOG_TAG, "URISyntaxException caught " + e + "\nTrace: " + processException(e));
|
|
|
|
throw new MessagingException("URISyntaxException caught", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-02-13 19:47:30 -05:00
|
|
|
|
2011-02-12 16:10:12 -05:00
|
|
|
return authenticated;
|
|
|
|
}
|
2010-12-17 09:40:19 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public CookieStore getAuthCookies() {
|
2008-12-06 19:29:11 -05:00
|
|
|
return mAuthCookies;
|
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getAlias() {
|
2010-10-23 21:03:29 -04:00
|
|
|
return mAlias;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getUrl() {
|
2008-12-06 19:29:11 -05:00
|
|
|
return mUrl;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public WebDavHttpClient getHttpClient() throws MessagingException {
|
|
|
|
if (mHttpClient == null) {
|
2009-11-15 10:19:39 -05:00
|
|
|
mHttpClient = new WebDavHttpClient();
|
2010-12-17 09:40:19 -05:00
|
|
|
// Disable automatic redirects on the http client.
|
|
|
|
mHttpClient.getParams().setBooleanParameter("http.protocol.handle-redirects", false);
|
2009-01-27 00:39:41 -05:00
|
|
|
|
2010-10-23 21:03:29 -04:00
|
|
|
// Setup a cookie store for forms-based authentication.
|
|
|
|
mContext = new BasicHttpContext();
|
|
|
|
mAuthCookies = new BasicCookieStore();
|
|
|
|
mContext.setAttribute(ClientContext.COOKIE_STORE, mAuthCookies);
|
2009-05-06 23:17:26 -04:00
|
|
|
|
2009-01-17 21:43:15 -05:00
|
|
|
SchemeRegistry reg = mHttpClient.getConnectionManager().getSchemeRegistry();
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2014-03-05 09:56:43 -05:00
|
|
|
Scheme s = new Scheme("https", new WebDavSocketFactory(mHost, 443), 443);
|
2010-10-23 21:03:29 -04:00
|
|
|
reg.register(s);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (NoSuchAlgorithmException nsa) {
|
2010-10-23 21:03:29 -04:00
|
|
|
Log.e(K9.LOG_TAG, "NoSuchAlgorithmException in getHttpClient: " + nsa);
|
|
|
|
throw new MessagingException("NoSuchAlgorithmException in getHttpClient: " + nsa);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (KeyManagementException kme) {
|
2010-10-23 21:03:29 -04:00
|
|
|
Log.e(K9.LOG_TAG, "KeyManagementException in getHttpClient: " + kme);
|
|
|
|
throw new MessagingException("KeyManagementException in getHttpClient: " + kme);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
2009-01-04 19:05:43 -05:00
|
|
|
return mHttpClient;
|
2009-01-01 03:56:19 -05:00
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2010-12-17 09:40:19 -05:00
|
|
|
private InputStream sendRequest(String url, String method, StringEntity messageBody,
|
2010-12-24 13:55:05 -05:00
|
|
|
HashMap<String, String> headers, boolean tryAuth)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2009-05-06 23:17:26 -04:00
|
|
|
InputStream istream = null;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (url == null || method == null) {
|
2009-05-06 23:17:26 -04:00
|
|
|
return istream;
|
|
|
|
}
|
|
|
|
|
2010-10-23 21:03:29 -04:00
|
|
|
WebDavHttpClient httpclient = getHttpClient();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-05-06 23:17:26 -04:00
|
|
|
int statusCode = -1;
|
|
|
|
HttpGeneric httpmethod = new HttpGeneric(url);
|
|
|
|
HttpResponse response;
|
|
|
|
HttpEntity entity;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (messageBody != null) {
|
2009-05-06 23:17:26 -04:00
|
|
|
httpmethod.setEntity(messageBody);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (headers != null) {
|
2011-11-01 00:42:12 -04:00
|
|
|
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
|
|
|
httpmethod.setHeader(entry.getKey(), entry.getValue());
|
2010-12-17 09:40:19 -05:00
|
|
|
}
|
2009-05-06 23:17:26 -04:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mAuthentication == AUTH_TYPE_NONE) {
|
|
|
|
if (!tryAuth || !authenticate()) {
|
2010-10-23 21:03:29 -04:00
|
|
|
throw new MessagingException("Unable to authenticate in sendRequest().");
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (mAuthentication == AUTH_TYPE_BASIC) {
|
2009-05-06 23:17:26 -04:00
|
|
|
httpmethod.setHeader("Authorization", mAuthString);
|
|
|
|
}
|
|
|
|
|
|
|
|
httpmethod.setMethod(method);
|
2010-10-23 21:03:29 -04:00
|
|
|
response = httpclient.executeOverride(httpmethod, mContext);
|
2009-05-06 23:17:26 -04:00
|
|
|
statusCode = response.getStatusLine().getStatusCode();
|
|
|
|
|
|
|
|
entity = response.getEntity();
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (statusCode == 401) {
|
2010-10-23 21:03:29 -04:00
|
|
|
throw new MessagingException("Invalid username or password for Basic authentication.");
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (statusCode == 440) {
|
|
|
|
if (tryAuth && mAuthentication == AUTH_TYPE_FORM_BASED) {
|
2010-10-23 21:03:29 -04:00
|
|
|
// Our cookie expired, re-authenticate.
|
|
|
|
doFBA(null);
|
2009-05-06 23:17:26 -04:00
|
|
|
sendRequest(url, method, messageBody, headers, false);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-10-23 21:03:29 -04:00
|
|
|
throw new MessagingException("Authentication failure in sendRequest().");
|
2009-05-06 23:17:26 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (statusCode < 200 || statusCode >= 300) {
|
2010-10-23 21:03:29 -04:00
|
|
|
throw new IOException("Error with code " + statusCode + " during request processing: " +
|
2010-12-24 13:55:05 -05:00
|
|
|
response.getStatusLine().toString());
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
2009-05-06 23:17:26 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (entity != null) {
|
2009-11-15 10:19:39 -05:00
|
|
|
istream = WebDavHttpClient.getUngzippedContent(entity);
|
2009-05-06 23:17:26 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (UnsupportedEncodingException uee) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "UnsupportedEncodingException: " + uee + "\nTrace: " + processException(uee));
|
2009-10-24 22:58:26 -04:00
|
|
|
throw new MessagingException("UnsupportedEncodingException", uee);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "IOException: " + ioe + "\nTrace: " + processException(ioe));
|
2009-10-24 22:58:26 -04:00
|
|
|
throw new MessagingException("IOException", ioe);
|
2009-05-06 23:17:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return istream;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getAuthString() {
|
2009-10-24 22:58:26 -04:00
|
|
|
return mAuthString;
|
|
|
|
}
|
2009-05-06 23:17:26 -04:00
|
|
|
|
2008-12-26 01:47:00 -05:00
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* Performs an httprequest to the supplied url using the supplied method. messageBody and headers are optional as
|
|
|
|
* not all requests will need them. There are two signatures to support calls that don't require parsing of the
|
|
|
|
* response.
|
2008-12-26 01:47:00 -05:00
|
|
|
*/
|
2009-05-06 23:17:26 -04:00
|
|
|
private DataSet processRequest(String url, String method, String messageBody, HashMap<String, String> headers)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2008-12-26 01:47:00 -05:00
|
|
|
return processRequest(url, method, messageBody, headers, true);
|
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2010-12-17 09:40:19 -05:00
|
|
|
private DataSet processRequest(String url, String method, String messageBody, HashMap<String, String> headers,
|
2010-12-24 13:55:05 -05:00
|
|
|
boolean needsParsing)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2009-01-17 21:43:15 -05:00
|
|
|
DataSet dataset = new DataSet();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG && K9.DEBUG_PROTOCOL_WEBDAV) {
|
2010-12-17 09:40:19 -05:00
|
|
|
Log.v(K9.LOG_TAG, "processRequest url = '" + url + "', method = '" + method + "', messageBody = '"
|
2010-12-24 13:55:05 -05:00
|
|
|
+ messageBody + "'");
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-26 01:47:00 -05:00
|
|
|
if (url == null ||
|
2011-02-06 17:09:48 -05:00
|
|
|
method == null) {
|
2008-12-26 01:47:00 -05:00
|
|
|
return dataset;
|
|
|
|
}
|
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
getHttpClient();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-01-05 20:31:57 -05:00
|
|
|
StringEntity messageEntity = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (messageBody != null) {
|
2008-12-26 01:47:00 -05:00
|
|
|
messageEntity = new StringEntity(messageBody);
|
|
|
|
messageEntity.setContentType("text/xml");
|
|
|
|
}
|
2009-05-06 23:17:26 -04:00
|
|
|
InputStream istream = sendRequest(url, method, messageEntity, headers, true);
|
|
|
|
if (istream != null &&
|
2011-02-06 17:09:48 -05:00
|
|
|
needsParsing) {
|
|
|
|
try {
|
2008-12-26 01:47:00 -05:00
|
|
|
SAXParserFactory spf = SAXParserFactory.newInstance();
|
|
|
|
SAXParser sp = spf.newSAXParser();
|
|
|
|
XMLReader xr = sp.getXMLReader();
|
|
|
|
WebDavHandler myHandler = new WebDavHandler();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-26 01:47:00 -05:00
|
|
|
xr.setContentHandler(myHandler);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-26 01:47:00 -05:00
|
|
|
xr.parse(new InputSource(istream));
|
|
|
|
|
|
|
|
dataset = myHandler.getDataSet();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (SAXException se) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "SAXException in processRequest() " + se + "\nTrace: " + processException(se));
|
2009-10-24 22:58:26 -04:00
|
|
|
throw new MessagingException("SAXException in processRequest() ", se);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (ParserConfigurationException pce) {
|
2010-12-17 09:40:19 -05:00
|
|
|
Log.e(K9.LOG_TAG, "ParserConfigurationException in processRequest() " + pce + "\nTrace: "
|
2010-12-24 13:55:05 -05:00
|
|
|
+ processException(pce));
|
2009-10-24 22:58:26 -04:00
|
|
|
throw new MessagingException("ParserConfigurationException in processRequest() ", pce);
|
2008-12-26 01:47:00 -05:00
|
|
|
}
|
2009-05-06 23:17:26 -04:00
|
|
|
|
|
|
|
istream.close();
|
2008-12-26 01:47:00 -05:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (UnsupportedEncodingException uee) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "UnsupportedEncodingException: " + uee + "\nTrace: " + processException(uee));
|
2009-10-24 22:58:26 -04:00
|
|
|
throw new MessagingException("UnsupportedEncodingException in processRequest() ", uee);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "IOException: " + ioe + "\nTrace: " + processException(ioe));
|
2009-10-24 22:58:26 -04:00
|
|
|
throw new MessagingException("IOException in processRequest() ", ioe);
|
2008-12-26 01:47:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return dataset;
|
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a string of the stacktrace for a Throwable to allow for easy inline printing of errors.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
private String processException(Throwable t) {
|
2009-01-17 21:43:15 -05:00
|
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
PrintStream ps = new PrintStream(baos);
|
|
|
|
|
|
|
|
t.printStackTrace(ps);
|
|
|
|
ps.close();
|
|
|
|
|
|
|
|
return baos.toString();
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isSendCapable() {
|
2009-10-24 22:58:26 -04:00
|
|
|
return true;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void sendMessages(Message[] messages) throws MessagingException {
|
2011-04-12 08:17:22 -04:00
|
|
|
WebDavFolder tmpFolder = (WebDavStore.WebDavFolder) getFolder(mAccount.getDraftsFolderName());
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2013-07-30 16:36:47 -04:00
|
|
|
tmpFolder.open(Folder.OPEN_MODE_RW);
|
2009-10-24 22:58:26 -04:00
|
|
|
Message[] retMessages = tmpFolder.appendWebDavMessages(messages);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
tmpFolder.moveMessages(retMessages, getSendSpoolFolder());
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
|
|
|
if (tmpFolder != null) {
|
Implementation of complete IMAP two-phase "delete/expunge" behavior.
On each IMAP account, the expunge behavior can be set to expunge
messages in a folder as soon as a move or delete is performed on the
folder ("immediately"), each time the folder is polled, or only when
executed manually.
In the Message List, there is now an Expunge action in the option
menu.
In the Folder List, there is now an Expunge action in the context
menu (long-press on the folder).
For IMAP accounts, it is also possible to disable the copying of deleted messages to the
Trash folder, by setting the Trash folder to -NONE-.
Fixes Issue 536.
Separately, in WebDAV accounts, the user can now choose the
server-side equivalents of the special folders, just like for IMAP.
2009-12-20 18:13:49 -05:00
|
|
|
tmpFolder.close();
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/*************************************************************************
|
|
|
|
* Helper and Inner classes
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A WebDav Folder
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
class WebDavFolder extends Folder {
|
2008-12-06 19:29:11 -05:00
|
|
|
private String mName;
|
|
|
|
private String mFolderUrl;
|
|
|
|
private boolean mIsOpen = false;
|
|
|
|
private int mMessageCount = 0;
|
|
|
|
private int mUnreadMessageCount = 0;
|
2009-10-24 22:58:26 -04:00
|
|
|
private WebDavStore store;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected WebDavStore getStore() {
|
2009-10-24 22:58:26 -04:00
|
|
|
return store;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public WebDavFolder(WebDavStore nStore, String name) {
|
2010-03-03 23:00:30 -05:00
|
|
|
super(nStore.getAccount());
|
2009-10-24 22:58:26 -04:00
|
|
|
store = nStore;
|
2008-12-06 19:29:11 -05:00
|
|
|
this.mName = name;
|
2008-12-23 22:09:37 -05:00
|
|
|
|
2011-04-05 05:31:17 -04:00
|
|
|
String encodedName = "";
|
2014-09-28 06:59:11 -04:00
|
|
|
String[] urlParts = name.split("/");
|
|
|
|
String url = "";
|
|
|
|
for (int i = 0, count = urlParts.length; i < count; i++) {
|
|
|
|
if (i != 0) {
|
|
|
|
url = url + "/" + com.fsck.k9.helper.UrlEncodingHelper.encodeUtf8(urlParts[i]);
|
|
|
|
} else {
|
|
|
|
url = com.fsck.k9.helper.UrlEncodingHelper.encodeUtf8(urlParts[i]);
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
2011-04-05 05:31:17 -04:00
|
|
|
}
|
2014-09-28 06:59:11 -04:00
|
|
|
encodedName = url;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-04-05 05:31:17 -04:00
|
|
|
encodedName = encodedName.replaceAll("\\+", "%20");
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-04-05 05:31:17 -04:00
|
|
|
this.mFolderUrl = WebDavStore.this.mUrl;
|
|
|
|
if (!WebDavStore.this.mUrl.endsWith("/")) {
|
|
|
|
this.mFolderUrl += "/";
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
2011-04-05 05:31:17 -04:00
|
|
|
this.mFolderUrl += encodedName;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setUrl(String url) {
|
|
|
|
if (url != null) {
|
2008-12-14 21:18:02 -05:00
|
|
|
this.mFolderUrl = url;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
@Override
|
2013-07-30 16:36:47 -04:00
|
|
|
public void open(int mode) throws MessagingException {
|
2009-01-27 00:39:41 -05:00
|
|
|
getHttpClient();
|
2008-12-06 19:29:11 -05:00
|
|
|
|
|
|
|
this.mIsOpen = true;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
@Override
|
2011-06-28 07:20:48 -04:00
|
|
|
public Map<String, String> copyMessages(Message[] messages, Folder folder) throws MessagingException {
|
2009-11-29 13:33:42 -05:00
|
|
|
moveOrCopyMessages(messages, folder.getName(), false);
|
2011-06-28 07:20:48 -04:00
|
|
|
return null;
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
@Override
|
2011-06-28 07:20:48 -04:00
|
|
|
public Map<String, String> moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
2009-11-29 13:33:42 -05:00
|
|
|
moveOrCopyMessages(messages, folder.getName(), true);
|
2011-06-28 07:20:48 -04:00
|
|
|
return null;
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2009-11-29 13:33:42 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void delete(Message[] msgs, String trashFolderName) throws MessagingException {
|
2009-11-29 13:33:42 -05:00
|
|
|
moveOrCopyMessages(msgs, trashFolderName, true);
|
|
|
|
}
|
2010-12-17 09:40:19 -05:00
|
|
|
|
|
|
|
private void moveOrCopyMessages(Message[] messages, String folderName, boolean isMove)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2009-10-24 22:58:26 -04:00
|
|
|
String[] uids = new String[messages.length];
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = messages.length; i < count; i++) {
|
2009-10-24 22:58:26 -04:00
|
|
|
uids[i] = messages[i].getUid();
|
|
|
|
}
|
2009-12-09 22:16:51 -05:00
|
|
|
String messageBody = "";
|
2009-10-24 22:58:26 -04:00
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
|
|
|
HashMap<String, String> uidToUrl = getMessageUrls(uids);
|
|
|
|
String[] urls = new String[uids.length];
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = uids.length; i < count; i++) {
|
2009-10-24 22:58:26 -04:00
|
|
|
urls[i] = uidToUrl.get(uids[i]);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (urls[i] == null && messages[i] instanceof WebDavMessage) {
|
2010-12-17 09:40:19 -05:00
|
|
|
WebDavMessage wdMessage = (WebDavMessage) messages[i];
|
2009-10-24 22:58:26 -04:00
|
|
|
urls[i] = wdMessage.getUrl();
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
messageBody = getMoveOrCopyMessagesReadXml(urls, isMove);
|
2010-12-17 09:40:19 -05:00
|
|
|
WebDavFolder destFolder = (WebDavFolder) store.getFolder(folderName);
|
2009-10-24 22:58:26 -04:00
|
|
|
headers.put("Destination", destFolder.mFolderUrl);
|
|
|
|
headers.put("Brief", "t");
|
|
|
|
headers.put("If-Match", "*");
|
|
|
|
String action = (isMove ? "BMOVE" : "BCOPY");
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.i(K9.LOG_TAG, "Moving " + messages.length + " messages to " + destFolder.mFolderUrl);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
processRequest(mFolderUrl, action, messageBody, headers, false);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private int getMessageCount(boolean read) throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
String isRead;
|
|
|
|
int messageCount = 0;
|
2008-12-26 01:47:00 -05:00
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
2008-12-06 19:29:11 -05:00
|
|
|
String messageBody;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (read) {
|
2009-12-09 22:16:51 -05:00
|
|
|
isRead = "True";
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-12-09 22:16:51 -05:00
|
|
|
isRead = "False";
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
messageBody = getMessageCountXml(isRead);
|
2008-12-26 01:47:00 -05:00
|
|
|
headers.put("Brief", "t");
|
2011-11-03 01:02:41 -04:00
|
|
|
DataSet dataset = processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (dataset != null) {
|
2009-10-24 22:58:26 -04:00
|
|
|
messageCount = dataset.getMessageCount();
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2011-07-09 16:24:18 -04:00
|
|
|
if (K9.DEBUG && K9.DEBUG_PROTOCOL_WEBDAV) {
|
|
|
|
Log.v(K9.LOG_TAG, "Counted messages and webdav returned: "+messageCount);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
return messageCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getMessageCount() throws MessagingException {
|
2013-07-30 16:36:47 -04:00
|
|
|
open(Folder.OPEN_MODE_RW);
|
2010-10-23 21:03:29 -04:00
|
|
|
this.mMessageCount = getMessageCount(true);
|
2008-12-06 19:29:11 -05:00
|
|
|
return this.mMessageCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getUnreadMessageCount() throws MessagingException {
|
2013-07-30 16:36:47 -04:00
|
|
|
open(Folder.OPEN_MODE_RW);
|
2010-10-23 21:03:29 -04:00
|
|
|
this.mUnreadMessageCount = getMessageCount(false);
|
2008-12-06 19:29:11 -05:00
|
|
|
return this.mUnreadMessageCount;
|
|
|
|
}
|
2010-10-23 21:03:29 -04:00
|
|
|
|
2010-04-16 10:33:54 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getFlaggedMessageCount() throws MessagingException {
|
2010-04-16 10:33:54 -04:00
|
|
|
return -1;
|
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isOpen() {
|
2008-12-06 19:29:11 -05:00
|
|
|
return this.mIsOpen;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-07-30 16:36:47 -04:00
|
|
|
public int getMode() {
|
|
|
|
return Folder.OPEN_MODE_RW;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getName() {
|
2008-12-06 19:29:11 -05:00
|
|
|
return this.mName;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean exists() {
|
2008-12-06 19:29:11 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void close() {
|
2008-12-06 19:29:11 -05:00
|
|
|
this.mMessageCount = 0;
|
|
|
|
this.mUnreadMessageCount = 0;
|
|
|
|
this.mIsOpen = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean create(FolderType type) throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void delete(boolean recursive) throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
throw new Error("WebDavFolder.delete() not implemeneted");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Message getMessage(String uid) throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
return new WebDavMessage(uid, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2010-05-30 17:20:47 -04:00
|
|
|
public Message[] getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
ArrayList<Message> messages = new ArrayList<Message>();
|
|
|
|
String[] uids;
|
2008-12-26 01:47:00 -05:00
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
|
|
|
int uidsLength = -1;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
String messageBody;
|
|
|
|
int prevStart = start;
|
|
|
|
|
|
|
|
/** Reverse the message range since 0 index is newest */
|
|
|
|
start = this.mMessageCount - end;
|
2009-10-24 22:58:26 -04:00
|
|
|
end = start + (end - prevStart);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (start < 0 || end < 0 || end < start) {
|
2014-03-03 10:40:23 -05:00
|
|
|
throw new MessagingException(String.format(Locale.US, "Invalid message set %d %d", start, end));
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (start == 0 && end < 10) {
|
2009-02-08 20:50:31 -05:00
|
|
|
end = 10;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/** Verify authentication */
|
|
|
|
messageBody = getMessagesXml();
|
|
|
|
|
2008-12-26 01:47:00 -05:00
|
|
|
headers.put("Brief", "t");
|
|
|
|
headers.put("Range", "rows=" + start + "-" + end);
|
2011-11-03 01:02:41 -04:00
|
|
|
DataSet dataset = processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2008-12-26 01:47:00 -05:00
|
|
|
uids = dataset.getUids();
|
|
|
|
HashMap<String, String> uidToUrl = dataset.getUidToUrl();
|
|
|
|
uidsLength = uids.length;
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0; i < uidsLength; i++) {
|
|
|
|
if (listener != null) {
|
2008-12-26 01:47:00 -05:00
|
|
|
listener.messageStarted(uids[i], i, uidsLength);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2008-12-26 01:47:00 -05:00
|
|
|
WebDavMessage message = new WebDavMessage(uids[i], this);
|
|
|
|
message.setUrl(uidToUrl.get(uids[i]));
|
|
|
|
messages.add(message);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2008-12-26 01:47:00 -05:00
|
|
|
listener.messageFinished(message, i, uidsLength);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-07 11:10:07 -04:00
|
|
|
return messages.toArray(EMPTY_MESSAGE_ARRAY);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
return getMessages(null, listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Message[] getMessages(String[] uids, MessageRetrievalListener listener) throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
ArrayList<Message> messageList = new ArrayList<Message>();
|
|
|
|
Message[] messages;
|
2008-12-11 00:25:59 -05:00
|
|
|
|
|
|
|
if (uids == null ||
|
2011-02-06 17:09:48 -05:00
|
|
|
uids.length == 0) {
|
2010-08-07 11:10:07 -04:00
|
|
|
return messageList.toArray(EMPTY_MESSAGE_ARRAY);
|
2008-12-11 00:25:59 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = uids.length; i < count; i++) {
|
|
|
|
if (listener != null) {
|
2008-12-11 00:25:59 -05:00
|
|
|
listener.messageStarted(uids[i], i, count);
|
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2008-12-11 00:25:59 -05:00
|
|
|
WebDavMessage message = new WebDavMessage(uids[i], this);
|
|
|
|
messageList.add(message);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2008-12-11 00:25:59 -05:00
|
|
|
listener.messageFinished(message, i, count);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
}
|
2010-08-07 11:10:07 -04:00
|
|
|
messages = messageList.toArray(EMPTY_MESSAGE_ARRAY);
|
2008-12-06 19:29:11 -05:00
|
|
|
|
|
|
|
return messages;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private HashMap<String, String> getMessageUrls(String[] uids) throws MessagingException {
|
2008-12-26 01:47:00 -05:00
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
2008-12-06 19:29:11 -05:00
|
|
|
String messageBody;
|
|
|
|
|
|
|
|
/** Retrieve and parse the XML entity for our messages */
|
|
|
|
messageBody = getMessageUrlsXml(uids);
|
2008-12-26 01:47:00 -05:00
|
|
|
headers.put("Brief", "t");
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-11-03 01:02:41 -04:00
|
|
|
DataSet dataset = processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);
|
|
|
|
HashMap<String, String> uidToUrl = dataset.getUidToUrl();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
return uidToUrl;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
@Override
|
|
|
|
public void fetch(Message[] messages, FetchProfile fp, MessageRetrievalListener listener)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
if (messages == null ||
|
2011-02-06 17:09:48 -05:00
|
|
|
messages.length == 0) {
|
2008-12-06 19:29:11 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
/**
|
|
|
|
* Fetch message envelope information for the array
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fp.contains(FetchProfile.Item.ENVELOPE)) {
|
2009-10-24 22:58:26 -04:00
|
|
|
fetchEnvelope(messages, listener);
|
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
/**
|
2008-12-10 18:30:00 -05:00
|
|
|
* Fetch message flag info for the array
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fp.contains(FetchProfile.Item.FLAGS)) {
|
2008-12-10 18:30:00 -05:00
|
|
|
fetchFlags(messages, listener);
|
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fp.contains(FetchProfile.Item.BODY_SANE)) {
|
2011-07-02 15:29:49 -04:00
|
|
|
if (mAccount.getMaximumAutoDownloadMessageSize() > 0) {
|
|
|
|
fetchMessages(messages, listener, (mAccount.getMaximumAutoDownloadMessageSize() / 76));
|
|
|
|
} else {
|
|
|
|
fetchMessages(messages, listener, -1);
|
|
|
|
}
|
2008-12-28 19:25:19 -05:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fp.contains(FetchProfile.Item.BODY)) {
|
2008-12-28 19:25:19 -05:00
|
|
|
fetchMessages(messages, listener, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetches the full messages or up to lines lines and passes them to the message parser.
|
|
|
|
*/
|
2010-12-17 09:40:19 -05:00
|
|
|
private void fetchMessages(Message[] messages, MessageRetrievalListener listener, int lines)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2009-11-15 10:19:39 -05:00
|
|
|
WebDavHttpClient httpclient;
|
2009-01-27 00:39:41 -05:00
|
|
|
httpclient = getHttpClient();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-28 19:25:19 -05:00
|
|
|
/**
|
|
|
|
* We can't hand off to processRequest() since we need the stream to parse.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = messages.length; i < count; i++) {
|
2008-12-28 19:25:19 -05:00
|
|
|
WebDavMessage wdMessage;
|
|
|
|
int statusCode = 0;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!(messages[i] instanceof WebDavMessage)) {
|
2008-12-06 19:29:11 -05:00
|
|
|
throw new MessagingException("WebDavStore fetch called with non-WebDavMessage");
|
|
|
|
}
|
2008-12-28 19:25:19 -05:00
|
|
|
|
|
|
|
wdMessage = (WebDavMessage) messages[i];
|
2008-12-07 15:55:26 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2008-12-06 19:29:11 -05:00
|
|
|
listener.messageStarted(wdMessage.getUid(), i, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* If fetch is called outside of the initial list (ie, a locally stored message), it may not have a URL
|
|
|
|
* associated. Verify and fix that
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
if (wdMessage.getUrl().equals("")) {
|
2010-12-17 09:40:19 -05:00
|
|
|
wdMessage.setUrl(getMessageUrls(new String[] { wdMessage.getUid() }).get(wdMessage.getUid()));
|
|
|
|
Log.i(K9.LOG_TAG, "Fetching messages with UID = '" + wdMessage.getUid() + "', URL = '"
|
2010-12-24 13:55:05 -05:00
|
|
|
+ wdMessage.getUrl() + "'");
|
2011-02-06 17:09:48 -05:00
|
|
|
if (wdMessage.getUrl().equals("")) {
|
2008-12-28 19:25:19 -05:00
|
|
|
throw new MessagingException("Unable to get URL for message");
|
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-12-17 09:40:19 -05:00
|
|
|
Log.i(K9.LOG_TAG, "Fetching message with UID = '" + wdMessage.getUid() + "', URL = '"
|
2010-12-24 13:55:05 -05:00
|
|
|
+ wdMessage.getUrl() + "'");
|
2008-12-28 19:25:19 -05:00
|
|
|
HttpGet httpget = new HttpGet(new URI(wdMessage.getUrl()));
|
2008-12-06 19:29:11 -05:00
|
|
|
HttpResponse response;
|
2008-12-28 19:25:19 -05:00
|
|
|
HttpEntity entity;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-28 19:25:19 -05:00
|
|
|
httpget.setHeader("translate", "f");
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mAuthentication == AUTH_TYPE_BASIC) {
|
2009-10-24 22:58:26 -04:00
|
|
|
httpget.setHeader("Authorization", mAuthString);
|
|
|
|
}
|
2010-10-23 21:03:29 -04:00
|
|
|
response = httpclient.executeOverride(httpget, mContext);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-28 19:25:19 -05:00
|
|
|
statusCode = response.getStatusLine().getStatusCode();
|
2008-12-10 18:30:00 -05:00
|
|
|
|
2009-01-05 20:31:57 -05:00
|
|
|
entity = response.getEntity();
|
|
|
|
|
2008-12-28 19:25:19 -05:00
|
|
|
if (statusCode < 200 ||
|
2011-02-06 17:09:48 -05:00
|
|
|
statusCode > 300) {
|
2010-03-17 22:50:05 -04:00
|
|
|
throw new IOException("Error during with code " + statusCode + " during fetch: "
|
2010-12-24 13:55:05 -05:00
|
|
|
+ response.getStatusLine().toString());
|
2008-12-28 19:25:19 -05:00
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (entity != null) {
|
2008-12-28 19:25:19 -05:00
|
|
|
InputStream istream = null;
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder buffer = new StringBuilder();
|
2009-12-09 22:16:51 -05:00
|
|
|
String tempText = "";
|
|
|
|
String resultText = "";
|
2011-10-31 23:51:02 -04:00
|
|
|
BufferedReader reader = null;
|
2008-12-28 19:25:19 -05:00
|
|
|
int currentLines = 0;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-10-31 23:51:02 -04:00
|
|
|
try {
|
|
|
|
istream = WebDavHttpClient.getUngzippedContent(entity);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-10-31 23:51:02 -04:00
|
|
|
if (lines != -1) {
|
|
|
|
reader = new BufferedReader(new InputStreamReader(istream), 8192);
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-10-31 23:51:02 -04:00
|
|
|
while ((tempText = reader.readLine()) != null &&
|
|
|
|
(currentLines < lines)) {
|
|
|
|
buffer.append(tempText).append("\r\n");
|
|
|
|
currentLines++;
|
|
|
|
}
|
|
|
|
|
|
|
|
istream.close();
|
|
|
|
resultText = buffer.toString();
|
|
|
|
istream = new ByteArrayInputStream(resultText.getBytes("UTF-8"));
|
2008-12-10 18:30:00 -05:00
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-10-31 23:51:02 -04:00
|
|
|
wdMessage.parse(istream);
|
2008-12-28 19:25:19 -05:00
|
|
|
|
2011-10-31 23:51:02 -04:00
|
|
|
} finally {
|
|
|
|
IOUtils.closeQuietly(reader);
|
|
|
|
IOUtils.closeQuietly(istream);
|
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IllegalArgumentException iae) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "IllegalArgumentException caught " + iae + "\nTrace: " + processException(iae));
|
2009-10-24 22:58:26 -04:00
|
|
|
throw new MessagingException("IllegalArgumentException caught", iae);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (URISyntaxException use) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "URISyntaxException caught " + use + "\nTrace: " + processException(use));
|
2009-10-24 22:58:26 -04:00
|
|
|
throw new MessagingException("URISyntaxException caught", use);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2010-12-17 09:40:19 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Non-success response code loading message, response code was " + statusCode
|
2010-12-24 13:55:05 -05:00
|
|
|
+ "\nURL: " + wdMessage.getUrl() + "\nError: " + ioe.getMessage() + "\nTrace: "
|
|
|
|
+ processException(ioe));
|
2009-10-24 22:58:26 -04:00
|
|
|
throw new MessagingException("Failure code " + statusCode, ioe);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2008-12-06 19:29:11 -05:00
|
|
|
listener.messageFinished(wdMessage, i, count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-10 18:30:00 -05:00
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* Fetches and sets the message flags for the supplied messages. The idea is to have this be recursive so that
|
|
|
|
* we do a series of medium calls instead of one large massive call or a large number of smaller calls.
|
2008-12-10 18:30:00 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
private void fetchFlags(Message[] startMessages, MessageRetrievalListener listener) throws MessagingException {
|
2008-12-26 01:47:00 -05:00
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
2009-12-09 22:16:51 -05:00
|
|
|
String messageBody = "";
|
2008-12-10 18:30:00 -05:00
|
|
|
Message[] messages = new Message[20];
|
|
|
|
String[] uids;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-10 18:30:00 -05:00
|
|
|
if (startMessages == null ||
|
2011-02-06 17:09:48 -05:00
|
|
|
startMessages.length == 0) {
|
2008-12-10 18:30:00 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (startMessages.length > 20) {
|
2008-12-10 18:30:00 -05:00
|
|
|
Message[] newMessages = new Message[startMessages.length - 20];
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = startMessages.length; i < count; i++) {
|
|
|
|
if (i < 20) {
|
2008-12-10 18:30:00 -05:00
|
|
|
messages[i] = startMessages[i];
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-12-10 18:30:00 -05:00
|
|
|
newMessages[i - 20] = startMessages[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fetchFlags(newMessages, listener);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-12-10 18:30:00 -05:00
|
|
|
messages = startMessages;
|
|
|
|
}
|
|
|
|
|
|
|
|
uids = new String[messages.length];
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = messages.length; i < count; i++) {
|
2008-12-10 18:30:00 -05:00
|
|
|
uids[i] = messages[i].getUid();
|
|
|
|
}
|
|
|
|
|
|
|
|
messageBody = getMessageFlagsXml(uids);
|
2008-12-26 01:47:00 -05:00
|
|
|
headers.put("Brief", "t");
|
2011-11-03 01:02:41 -04:00
|
|
|
DataSet dataset = processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);
|
2008-12-10 18:30:00 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (dataset == null) {
|
2009-02-26 01:56:30 -05:00
|
|
|
throw new MessagingException("Data Set from request was null");
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-11-03 01:02:41 -04:00
|
|
|
HashMap<String, Boolean> uidToReadStatus = dataset.getUidToRead();
|
2008-12-10 18:30:00 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = messages.length; i < count; i++) {
|
|
|
|
if (!(messages[i] instanceof WebDavMessage)) {
|
2008-12-10 18:30:00 -05:00
|
|
|
throw new MessagingException("WebDavStore fetch called with non-WebDavMessage");
|
|
|
|
}
|
|
|
|
WebDavMessage wdMessage = (WebDavMessage) messages[i];
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2011-07-09 18:44:14 -04:00
|
|
|
listener.messageStarted(wdMessage.getUid(), i, count);
|
2008-12-10 18:30:00 -05:00
|
|
|
}
|
|
|
|
|
2011-10-27 11:17:43 -04:00
|
|
|
try {
|
2011-07-19 22:43:32 -04:00
|
|
|
wdMessage.setFlagInternal(Flag.SEEN, uidToReadStatus.get(wdMessage.getUid()));
|
|
|
|
} catch (NullPointerException e) {
|
|
|
|
Log.v(K9.LOG_TAG,"Under some weird circumstances, setting the read status when syncing from webdav threw an NPE. Skipping.");
|
|
|
|
}
|
2008-12-10 18:30:00 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2011-07-09 18:44:14 -04:00
|
|
|
listener.messageFinished(wdMessage, i, count);
|
2008-12-10 18:30:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-07 15:55:26 -05:00
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* Fetches and parses the message envelopes for the supplied messages. The idea is to have this be recursive so
|
|
|
|
* that we do a series of medium calls instead of one large massive call or a large number of smaller calls.
|
2008-12-07 15:55:26 -05:00
|
|
|
* Call it a happy balance
|
|
|
|
*/
|
2010-12-17 09:40:19 -05:00
|
|
|
private void fetchEnvelope(Message[] startMessages, MessageRetrievalListener listener)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2008-12-26 01:47:00 -05:00
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
2009-12-09 22:16:51 -05:00
|
|
|
String messageBody = "";
|
2008-12-26 01:47:00 -05:00
|
|
|
String[] uids;
|
2008-12-07 15:55:26 -05:00
|
|
|
Message[] messages = new Message[10];
|
2008-12-26 01:47:00 -05:00
|
|
|
|
2008-12-07 15:55:26 -05:00
|
|
|
if (startMessages == null ||
|
2011-02-06 17:09:48 -05:00
|
|
|
startMessages.length == 0) {
|
2008-12-07 15:55:26 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (startMessages.length > 10) {
|
2008-12-07 15:55:26 -05:00
|
|
|
Message[] newMessages = new Message[startMessages.length - 10];
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = startMessages.length; i < count; i++) {
|
|
|
|
if (i < 10) {
|
2008-12-07 15:55:26 -05:00
|
|
|
messages[i] = startMessages[i];
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-12-07 15:55:26 -05:00
|
|
|
newMessages[i - 10] = startMessages[i];
|
|
|
|
}
|
|
|
|
}
|
2008-12-26 01:47:00 -05:00
|
|
|
|
2008-12-07 15:55:26 -05:00
|
|
|
fetchEnvelope(newMessages, listener);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-12-07 15:55:26 -05:00
|
|
|
messages = startMessages;
|
|
|
|
}
|
|
|
|
|
2008-12-26 01:47:00 -05:00
|
|
|
uids = new String[messages.length];
|
2008-12-07 15:55:26 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = messages.length; i < count; i++) {
|
2008-12-07 15:55:26 -05:00
|
|
|
uids[i] = messages[i].getUid();
|
|
|
|
}
|
|
|
|
|
|
|
|
messageBody = getMessageEnvelopeXml(uids);
|
2008-12-26 01:47:00 -05:00
|
|
|
headers.put("Brief", "t");
|
2011-11-03 01:02:41 -04:00
|
|
|
DataSet dataset = processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);
|
2008-12-07 15:55:26 -05:00
|
|
|
|
2011-11-03 01:02:41 -04:00
|
|
|
Map<String, ParsedMessageEnvelope> envelopes = dataset.getMessageEnvelopes();
|
2008-12-07 15:55:26 -05:00
|
|
|
|
2008-12-14 14:33:34 -05:00
|
|
|
int count = messages.length;
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = messages.length - 1; i >= 0; i--) {
|
|
|
|
if (!(messages[i] instanceof WebDavMessage)) {
|
2008-12-07 15:55:26 -05:00
|
|
|
throw new MessagingException("WebDavStore fetch called with non-WebDavMessage");
|
|
|
|
}
|
|
|
|
WebDavMessage wdMessage = (WebDavMessage) messages[i];
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2008-12-07 15:55:26 -05:00
|
|
|
listener.messageStarted(messages[i].getUid(), i, count);
|
|
|
|
}
|
2008-12-10 18:30:00 -05:00
|
|
|
|
2011-07-09 15:26:30 -04:00
|
|
|
ParsedMessageEnvelope envelope = envelopes.get(wdMessage.getUid());
|
|
|
|
if (envelope != null) {
|
|
|
|
wdMessage.setNewHeaders(envelope);
|
|
|
|
wdMessage.setFlagInternal(Flag.SEEN, envelope.getReadStatus());
|
|
|
|
} else {
|
|
|
|
Log.e(K9.LOG_TAG,"Asked to get metadata for a non-existent message: "+wdMessage.getUid());
|
|
|
|
}
|
2008-12-07 15:55:26 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2008-12-07 15:55:26 -05:00
|
|
|
listener.messageFinished(messages[i], i, count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-12-24 20:08:34 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
@Override
|
|
|
|
public void setFlags(Message[] messages, Flag[] flags, boolean value)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
String[] uids = new String[messages.length];
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = messages.length; i < count; i++) {
|
2008-12-06 19:29:11 -05:00
|
|
|
uids[i] = messages[i].getUid();
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (Flag flag : flags) {
|
|
|
|
if (flag == Flag.SEEN) {
|
2009-10-24 22:58:26 -04:00
|
|
|
markServerMessagesRead(uids, value);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (flag == Flag.DELETED) {
|
2008-12-06 19:29:11 -05:00
|
|
|
deleteServerMessages(uids);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private void markServerMessagesRead(String[] uids, boolean read) throws MessagingException {
|
2009-12-09 22:16:51 -05:00
|
|
|
String messageBody = "";
|
2008-12-26 01:47:00 -05:00
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
2008-12-06 19:29:11 -05:00
|
|
|
HashMap<String, String> uidToUrl = getMessageUrls(uids);
|
|
|
|
String[] urls = new String[uids.length];
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = uids.length; i < count; i++) {
|
2008-12-06 19:29:11 -05:00
|
|
|
urls[i] = uidToUrl.get(uids[i]);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
messageBody = getMarkMessagesReadXml(urls, read);
|
2008-12-26 01:47:00 -05:00
|
|
|
headers.put("Brief", "t");
|
|
|
|
headers.put("If-Match", "*");
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2008-12-26 01:47:00 -05:00
|
|
|
processRequest(this.mFolderUrl, "BPROPPATCH", messageBody, headers, false);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private void deleteServerMessages(String[] uids) throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
HashMap<String, String> uidToUrl = getMessageUrls(uids);
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String uid : uids) {
|
2008-12-26 01:47:00 -05:00
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
|
|
|
String url = uidToUrl.get(uid);
|
|
|
|
String destinationUrl = generateDeleteUrl(url);
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2008-12-26 01:47:00 -05:00
|
|
|
/**
|
|
|
|
* If the destination is the same as the origin, assume delete forever
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
if (destinationUrl.equals(url)) {
|
2008-12-26 01:47:00 -05:00
|
|
|
headers.put("Brief", "t");
|
|
|
|
processRequest(url, "DELETE", null, headers, false);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-12-26 01:47:00 -05:00
|
|
|
headers.put("Destination", generateDeleteUrl(url));
|
|
|
|
headers.put("Brief", "t");
|
|
|
|
processRequest(url, "MOVE", null, headers, false);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-12-25 03:38:55 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String generateDeleteUrl(String startUrl) {
|
2008-12-25 03:38:55 -05:00
|
|
|
String[] urlParts = startUrl.split("/");
|
|
|
|
String filename = urlParts[urlParts.length - 1];
|
|
|
|
String finalUrl = WebDavStore.this.mUrl + "Deleted%20Items/" + filename;
|
|
|
|
|
|
|
|
return finalUrl;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
@Override
|
2011-06-28 07:20:48 -04:00
|
|
|
public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
|
2009-11-24 19:40:29 -05:00
|
|
|
appendWebDavMessages(messages);
|
2011-06-28 07:20:48 -04:00
|
|
|
return null;
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public Message[] appendWebDavMessages(Message[] messages) throws MessagingException {
|
2009-10-24 22:58:26 -04:00
|
|
|
Message[] retMessages = new Message[messages.length];
|
|
|
|
int ind = 0;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-11-15 10:19:39 -05:00
|
|
|
WebDavHttpClient httpclient = getHttpClient();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (Message message : messages) {
|
2009-10-24 22:58:26 -04:00
|
|
|
HttpGeneric httpmethod;
|
|
|
|
HttpResponse response;
|
|
|
|
StringEntity bodyEntity;
|
|
|
|
int statusCode;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-10-24 22:58:26 -04:00
|
|
|
ByteArrayOutputStream out;
|
2010-11-30 22:02:13 -05:00
|
|
|
|
|
|
|
out = new ByteArrayOutputStream(message.getSize());
|
|
|
|
|
2013-07-30 16:36:47 -04:00
|
|
|
open(Folder.OPEN_MODE_RW);
|
2010-03-17 23:23:45 -04:00
|
|
|
EOLConvertingOutputStream msgOut = new EOLConvertingOutputStream(
|
2010-12-24 13:55:05 -05:00
|
|
|
new BufferedOutputStream(out, 1024));
|
2010-03-17 23:23:45 -04:00
|
|
|
message.writeTo(msgOut);
|
|
|
|
msgOut.flush();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
bodyEntity = new StringEntity(out.toString(), "UTF-8");
|
|
|
|
bodyEntity.setContentType("message/rfc822");
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
String messageURL = mFolderUrl;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!messageURL.endsWith("/")) {
|
2009-10-24 22:58:26 -04:00
|
|
|
messageURL += "/";
|
|
|
|
}
|
2014-09-28 06:59:11 -04:00
|
|
|
messageURL += com.fsck.k9.helper.UrlEncodingHelper.encodeUtf8(message.getUid() + ":" + System.currentTimeMillis() + ".eml");
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-03-17 22:50:05 -04:00
|
|
|
Log.i(K9.LOG_TAG, "Uploading message as " + messageURL);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
httpmethod = new HttpGeneric(messageURL);
|
|
|
|
httpmethod.setMethod("PUT");
|
|
|
|
httpmethod.setEntity(bodyEntity);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
String mAuthString = getAuthString();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mAuthString != null) {
|
2009-10-24 22:58:26 -04:00
|
|
|
httpmethod.setHeader("Authorization", mAuthString);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-10-23 21:03:29 -04:00
|
|
|
response = httpclient.executeOverride(httpmethod, mContext);
|
2009-10-24 22:58:26 -04:00
|
|
|
statusCode = response.getStatusLine().getStatusCode();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
if (statusCode < 200 ||
|
2011-02-06 17:09:48 -05:00
|
|
|
statusCode > 300) {
|
2010-04-29 00:59:14 -04:00
|
|
|
throw new IOException("Error with status code " + statusCode
|
2010-12-24 13:55:05 -05:00
|
|
|
+ " while sending/appending message. Response = "
|
|
|
|
+ response.getStatusLine().toString() + " for message " + messageURL);
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
|
|
|
WebDavMessage retMessage = new WebDavMessage(message.getUid(), this);
|
|
|
|
|
|
|
|
retMessage.setUrl(messageURL);
|
|
|
|
retMessages[ind++] = retMessage;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2009-10-24 22:58:26 -04:00
|
|
|
throw new MessagingException("Unable to append", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retMessages;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean equals(Object o) {
|
2008-12-06 19:29:11 -05:00
|
|
|
return false;
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getUidFromMessageId(Message message) throws MessagingException {
|
2010-12-17 09:40:19 -05:00
|
|
|
Log.e(K9.LOG_TAG,
|
2010-12-24 13:55:05 -05:00
|
|
|
"Unimplemented method getUidFromMessageId in WebDavStore.WebDavFolder could lead to duplicate messages "
|
|
|
|
+ " being uploaded to the Sent folder");
|
2008-12-26 01:52:07 -05:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setFlags(Flag[] flags, boolean value) throws MessagingException {
|
2010-12-17 09:40:19 -05:00
|
|
|
Log.e(K9.LOG_TAG,
|
2010-12-24 13:55:05 -05:00
|
|
|
"Unimplemented method setFlags(Flag[], boolean) breaks markAllMessagesAsRead and EmptyTrash");
|
2008-12-26 01:52:07 -05:00
|
|
|
// Try to make this efficient by not retrieving all of the messages
|
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/**
|
|
|
|
* A WebDav Message
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
class WebDavMessage extends MimeMessage {
|
2009-12-09 22:16:51 -05:00
|
|
|
private String mUrl = "";
|
2009-10-24 22:58:26 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
WebDavMessage(String uid, Folder folder) {
|
2008-12-06 19:29:11 -05:00
|
|
|
this.mUid = uid;
|
|
|
|
this.mFolder = folder;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setUrl(String url) {
|
2010-12-17 09:40:19 -05:00
|
|
|
// TODO: This is a not as ugly hack (ie, it will actually work)
|
|
|
|
// XXX: prevent URLs from getting to us that are broken
|
2011-10-27 11:17:43 -04:00
|
|
|
if (!(url.toLowerCase(Locale.US).contains("http"))) {
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!(url.startsWith("/"))) {
|
2009-01-08 00:47:10 -05:00
|
|
|
url = "/" + url;
|
|
|
|
}
|
|
|
|
url = WebDavStore.this.mUrl + this.mFolder + url;
|
|
|
|
}
|
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
String[] urlParts = url.split("/");
|
|
|
|
int length = urlParts.length;
|
|
|
|
String end = urlParts[length - 1];
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-12-09 22:16:51 -05:00
|
|
|
this.mUrl = "";
|
|
|
|
url = "";
|
2008-12-06 19:29:11 -05:00
|
|
|
|
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* We have to decode, then encode the URL because Exchange likes to not properly encode all characters
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2014-09-28 06:59:11 -04:00
|
|
|
end = UrlEncodingHelper.decodeUtf8(end);
|
|
|
|
end = com.fsck.k9.helper.UrlEncodingHelper.encodeUtf8(end);
|
2008-12-06 19:29:11 -05:00
|
|
|
end = end.replaceAll("\\+", "%20");
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IllegalArgumentException iae) {
|
2010-12-17 09:40:19 -05:00
|
|
|
Log.e(K9.LOG_TAG, "IllegalArgumentException caught in setUrl: " + iae + "\nTrace: "
|
2010-12-24 13:55:05 -05:00
|
|
|
+ processException(iae));
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0; i < length - 1; i++) {
|
|
|
|
if (i != 0) {
|
2008-12-06 19:29:11 -05:00
|
|
|
url = url + "/" + urlParts[i];
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-12-06 19:29:11 -05:00
|
|
|
url = urlParts[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
url = url + "/" + end;
|
|
|
|
|
|
|
|
this.mUrl = url;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getUrl() {
|
2008-12-06 19:29:11 -05:00
|
|
|
return this.mUrl;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setSize(int size) {
|
2008-12-06 19:29:11 -05:00
|
|
|
this.mSize = size;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setFlagInternal(Flag flag, boolean set) throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
super.setFlag(flag, set);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setNewHeaders(ParsedMessageEnvelope envelope) throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
String[] headers = envelope.getHeaderList();
|
|
|
|
HashMap<String, String> messageHeaders = envelope.getMessageHeaders();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String header : headers) {
|
2010-07-18 21:52:18 -04:00
|
|
|
String headerValue = messageHeaders.get(header);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (header.equals("Content-Length")) {
|
2010-07-18 21:52:18 -04:00
|
|
|
int size = Integer.parseInt(messageHeaders.get(header));
|
2009-01-17 21:43:15 -05:00
|
|
|
this.setSize(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (headerValue != null &&
|
2011-02-06 17:09:48 -05:00
|
|
|
!headerValue.equals("")) {
|
2010-07-18 21:52:18 -04:00
|
|
|
this.addHeader(header, headerValue);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-24 22:58:26 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void delete(String trashFolderName) throws MessagingException {
|
2010-12-17 09:40:19 -05:00
|
|
|
WebDavFolder wdFolder = (WebDavFolder) getFolder();
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.i(K9.LOG_TAG, "Deleting message by moving to " + trashFolderName);
|
2009-10-24 22:58:26 -04:00
|
|
|
wdFolder.moveMessages(new Message[] { this }, wdFolder.getStore().getFolder(trashFolderName));
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setFlag(Flag flag, boolean set) throws MessagingException {
|
2008-12-06 19:29:11 -05:00
|
|
|
super.setFlag(flag, set);
|
|
|
|
mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set);
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* XML Parsing Handler Can handle all XML handling needs
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public class WebDavHandler extends DefaultHandler {
|
2009-01-17 21:43:15 -05:00
|
|
|
private DataSet mDataSet = new DataSet();
|
2011-11-02 23:47:48 -04:00
|
|
|
private final LinkedList<String> mOpenTags = new LinkedList<String>();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public DataSet getDataSet() {
|
2008-12-06 19:29:11 -05:00
|
|
|
return this.mDataSet;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void startDocument() throws SAXException {
|
2009-01-17 21:43:15 -05:00
|
|
|
this.mDataSet = new DataSet();
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void endDocument() throws SAXException {
|
2008-12-06 19:29:11 -05:00
|
|
|
/* Do nothing */
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void startElement(String namespaceURI, String localName,
|
2011-02-06 17:09:48 -05:00
|
|
|
String qName, Attributes atts) throws SAXException {
|
2011-09-30 02:22:44 -04:00
|
|
|
mOpenTags.addFirst(localName);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void endElement(String namespaceURI, String localName, String qName) {
|
2011-09-30 02:22:44 -04:00
|
|
|
mOpenTags.removeFirst();
|
2008-12-06 19:29:11 -05:00
|
|
|
|
|
|
|
/** Reset the hash temp variables */
|
2011-02-06 17:09:48 -05:00
|
|
|
if (localName.equals("response")) {
|
2009-01-17 21:43:15 -05:00
|
|
|
this.mDataSet.finish();
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void characters(char ch[], int start, int length) {
|
2008-12-06 19:29:11 -05:00
|
|
|
String value = new String(ch, start, length);
|
|
|
|
mDataSet.addValue(value, mOpenTags.peek());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* Data set for a single E-Mail message's required headers (the envelope) Only provides accessor methods to the
|
|
|
|
* stored data. All processing should be done elsewhere. This is done rather than having multiple hashmaps
|
2008-12-06 19:29:11 -05:00
|
|
|
* associating UIDs to values
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public static class ParsedMessageEnvelope {
|
2009-01-17 21:43:15 -05:00
|
|
|
/**
|
|
|
|
* Holds the mappings from the name returned from Exchange to the MIME format header name
|
|
|
|
*/
|
2011-11-03 00:57:38 -04:00
|
|
|
private static final Map<String, String> HEADER_MAPPINGS;
|
|
|
|
static {
|
|
|
|
Map<String, String> map = new HashMap<String, String>();
|
|
|
|
map.put("mime-version", "MIME-Version");
|
|
|
|
map.put("content-type", "Content-Type");
|
|
|
|
map.put("subject", "Subject");
|
|
|
|
map.put("date", "Date");
|
|
|
|
map.put("thread-topic", "Thread-Topic");
|
|
|
|
map.put("thread-index", "Thread-Index");
|
|
|
|
map.put("from", "From");
|
|
|
|
map.put("to", "To");
|
|
|
|
map.put("in-reply-to", "In-Reply-To");
|
|
|
|
map.put("cc", "Cc");
|
|
|
|
map.put("getcontentlength", "Content-Length");
|
|
|
|
HEADER_MAPPINGS = Collections.unmodifiableMap(map);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
private boolean mReadStatus = false;
|
2009-12-09 22:16:51 -05:00
|
|
|
private String mUid = "";
|
2008-12-06 19:29:11 -05:00
|
|
|
private HashMap<String, String> mMessageHeaders = new HashMap<String, String>();
|
|
|
|
private ArrayList<String> mHeaders = new ArrayList<String>();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void addHeader(String field, String value) {
|
2011-11-03 00:57:38 -04:00
|
|
|
String headerName = HEADER_MAPPINGS.get(field);
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (headerName != null) {
|
2011-11-03 00:57:38 -04:00
|
|
|
this.mMessageHeaders.put(HEADER_MAPPINGS.get(field), value);
|
|
|
|
this.mHeaders.add(HEADER_MAPPINGS.get(field));
|
2009-01-17 21:43:15 -05:00
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public HashMap<String, String> getMessageHeaders() {
|
2008-12-06 19:29:11 -05:00
|
|
|
return this.mMessageHeaders;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String[] getHeaderList() {
|
2010-08-07 11:10:07 -04:00
|
|
|
return this.mHeaders.toArray(EMPTY_STRING_ARRAY);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setReadStatus(boolean status) {
|
2008-12-06 19:29:11 -05:00
|
|
|
this.mReadStatus = status;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean getReadStatus() {
|
2008-12-06 19:29:11 -05:00
|
|
|
return this.mReadStatus;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setUid(String uid) {
|
|
|
|
if (uid != null) {
|
2008-12-06 19:29:11 -05:00
|
|
|
this.mUid = uid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getUid() {
|
2008-12-06 19:29:11 -05:00
|
|
|
return this.mUid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* Dataset for all XML parses. Data is stored in a single format inside the class and is formatted appropriately
|
|
|
|
* depending on the accessor calls made.
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public class DataSet {
|
2010-04-16 08:20:10 -04:00
|
|
|
private HashMap<String, HashMap<String, String>> mData = new HashMap<String, HashMap<String, String>>();
|
2011-07-28 15:08:00 -04:00
|
|
|
private StringBuilder mUid = new StringBuilder();
|
2009-01-17 21:43:15 -05:00
|
|
|
private HashMap<String, String> mTempData = new HashMap<String, String>();
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void addValue(String value, String tagName) {
|
|
|
|
if (tagName.equals("uid")) {
|
2011-07-28 15:08:00 -04:00
|
|
|
mUid.append(value);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mTempData.containsKey(tagName)) {
|
2009-01-17 21:43:15 -05:00
|
|
|
mTempData.put(tagName, mTempData.get(tagName) + value);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-01-17 21:43:15 -05:00
|
|
|
mTempData.put(tagName, value);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void finish() {
|
2011-07-28 15:08:00 -04:00
|
|
|
String uid = mUid.toString();
|
2011-12-28 14:03:44 -05:00
|
|
|
if (uid != null && mTempData != null) {
|
2011-07-28 15:08:00 -04:00
|
|
|
mData.put(uid, mTempData);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (mTempData != null) {
|
2010-12-17 09:40:19 -05:00
|
|
|
/*
|
|
|
|
* Lost Data are for requests that don't include a message UID. These requests should only have a depth
|
|
|
|
* of one for the response so it will never get stomped over.
|
2009-01-17 21:43:15 -05:00
|
|
|
*/
|
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-07-28 15:08:00 -04:00
|
|
|
mUid = new StringBuilder();
|
2009-01-17 21:43:15 -05:00
|
|
|
mTempData = new HashMap<String, String>();
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-04-12 08:17:22 -04:00
|
|
|
* Returns a hashmap of special folder name => special folder url
|
|
|
|
*/
|
|
|
|
public HashMap<String, String> getSpecialFolderToUrl() {
|
|
|
|
// We return the first (and only) map
|
|
|
|
for (HashMap<String, String> folderMap : mData.values()) {
|
|
|
|
return folderMap;
|
|
|
|
}
|
|
|
|
return new HashMap<String, String>();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-01-17 21:43:15 -05:00
|
|
|
* Returns a hashmap of Message UID => Message Url
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public HashMap<String, String> getUidToUrl() {
|
2009-01-17 21:43:15 -05:00
|
|
|
HashMap<String, String> uidToUrl = new HashMap<String, String>();
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String uid : mData.keySet()) {
|
2009-01-17 21:43:15 -05:00
|
|
|
HashMap<String, String> data = mData.get(uid);
|
|
|
|
String value = data.get("href");
|
|
|
|
if (value != null &&
|
2011-02-06 17:09:48 -05:00
|
|
|
!value.equals("")) {
|
2009-01-17 21:43:15 -05:00
|
|
|
uidToUrl.put(uid, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return uidToUrl;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-01-17 21:43:15 -05:00
|
|
|
* Returns a hashmap of Message UID => Read Status
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public HashMap<String, Boolean> getUidToRead() {
|
2009-01-17 21:43:15 -05:00
|
|
|
HashMap<String, Boolean> uidToRead = new HashMap<String, Boolean>();
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String uid : mData.keySet()) {
|
2009-01-17 21:43:15 -05:00
|
|
|
HashMap<String, String> data = mData.get(uid);
|
|
|
|
String readStatus = data.get("read");
|
2011-07-10 14:27:36 -04:00
|
|
|
if (readStatus != null && !readStatus.equals("")) {
|
2010-08-29 19:39:26 -04:00
|
|
|
Boolean value = !readStatus.equals("0");
|
2009-01-17 21:43:15 -05:00
|
|
|
uidToRead.put(uid, value);
|
2011-07-10 14:27:36 -04:00
|
|
|
} else {
|
|
|
|
// We don't actually want to have null values in our hashmap,
|
|
|
|
// as it causes the calling code to crash with an NPE as it
|
2011-09-27 00:03:13 -04:00
|
|
|
// does a lookup in the map.
|
2011-07-10 14:27:36 -04:00
|
|
|
uidToRead.put(uid, false);
|
2009-01-17 21:43:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return uidToRead;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/**
|
2009-01-17 21:43:15 -05:00
|
|
|
* Returns an array of all hrefs (urls) that were received
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public String[] getHrefs() {
|
2009-01-17 21:43:15 -05:00
|
|
|
ArrayList<String> hrefs = new ArrayList<String>();
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String uid : mData.keySet()) {
|
2009-01-17 21:43:15 -05:00
|
|
|
HashMap<String, String> data = mData.get(uid);
|
|
|
|
String href = data.get("href");
|
|
|
|
hrefs.add(href);
|
|
|
|
}
|
|
|
|
|
2010-08-07 11:10:07 -04:00
|
|
|
return hrefs.toArray(EMPTY_STRING_ARRAY);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/**
|
2009-01-17 21:43:15 -05:00
|
|
|
* Return an array of all Message UIDs that were received
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public String[] getUids() {
|
2009-01-17 21:43:15 -05:00
|
|
|
ArrayList<String> uids = new ArrayList<String>();
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String uid : mData.keySet()) {
|
2009-01-17 21:43:15 -05:00
|
|
|
uids.add(uid);
|
|
|
|
}
|
|
|
|
|
2010-08-07 11:10:07 -04:00
|
|
|
return uids.toArray(EMPTY_STRING_ARRAY);
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/**
|
2009-01-17 21:43:15 -05:00
|
|
|
* Returns the message count as it was retrieved
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getMessageCount() {
|
2011-07-09 17:00:35 -04:00
|
|
|
// It appears that Exchange is returning responses
|
|
|
|
// without a visiblecount element for empty folders
|
|
|
|
// Which resulted in this code returning -1 (as that was
|
|
|
|
// the previous default.)
|
|
|
|
// -1 is an error condition. Now the default is empty
|
|
|
|
int messageCount = 0;
|
2008-12-06 19:29:11 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String uid : mData.keySet()) {
|
2009-01-17 21:43:15 -05:00
|
|
|
HashMap<String, String> data = mData.get(uid);
|
|
|
|
String count = data.get("visiblecount");
|
|
|
|
|
|
|
|
if (count != null &&
|
2011-02-06 17:09:48 -05:00
|
|
|
!count.equals("")) {
|
2009-12-09 22:16:51 -05:00
|
|
|
messageCount = Integer.parseInt(count);
|
2009-01-17 21:43:15 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-01-17 21:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return messageCount;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/**
|
2009-01-17 21:43:15 -05:00
|
|
|
* Returns a HashMap of message UID => ParsedMessageEnvelope
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public HashMap<String, ParsedMessageEnvelope> getMessageEnvelopes() {
|
2009-01-17 21:43:15 -05:00
|
|
|
HashMap<String, ParsedMessageEnvelope> envelopes = new HashMap<String, ParsedMessageEnvelope>();
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String uid : mData.keySet()) {
|
2009-01-17 21:43:15 -05:00
|
|
|
ParsedMessageEnvelope envelope = new ParsedMessageEnvelope();
|
|
|
|
HashMap<String, String> data = mData.get(uid);
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (data != null) {
|
2011-11-01 00:42:12 -04:00
|
|
|
for (Map.Entry<String, String> entry : data.entrySet()) {
|
|
|
|
String header = entry.getKey();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (header.equals("read")) {
|
2011-11-01 00:42:12 -04:00
|
|
|
String read = entry.getValue();
|
|
|
|
boolean readStatus = !read.equals("0");
|
2009-01-17 21:43:15 -05:00
|
|
|
|
|
|
|
envelope.setReadStatus(readStatus);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (header.equals("date")) {
|
2009-01-17 21:43:15 -05:00
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* Exchange doesn't give us rfc822 dates like it claims. The date is in the format:
|
|
|
|
* yyyy-MM-dd'T'HH:mm:ss.SSS<Single digit representation of timezone, so far, all instances
|
|
|
|
* are Z>
|
2009-01-17 21:43:15 -05:00
|
|
|
*/
|
2011-11-01 00:42:12 -04:00
|
|
|
String date = entry.getValue();
|
2009-01-17 21:43:15 -05:00
|
|
|
date = date.substring(0, date.length() - 1);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-05-25 22:32:26 -04:00
|
|
|
DateFormat dfInput = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US);
|
|
|
|
DateFormat dfOutput = new SimpleDateFormat("EEE, d MMM yy HH:mm:ss Z", Locale.US);
|
2009-01-17 21:43:15 -05:00
|
|
|
String tempDate = "";
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-01-17 21:43:15 -05:00
|
|
|
Date parsedDate = dfInput.parse(date);
|
|
|
|
tempDate = dfOutput.format(parsedDate);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (java.text.ParseException pe) {
|
2010-12-17 09:40:19 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Error parsing date: " + pe + "\nTrace: " + processException(pe));
|
2009-01-17 21:43:15 -05:00
|
|
|
}
|
|
|
|
envelope.addHeader(header, tempDate);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2011-11-01 00:42:12 -04:00
|
|
|
envelope.addHeader(header, entry.getValue());
|
2009-01-17 21:43:15 -05:00
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
2009-01-17 21:43:15 -05:00
|
|
|
|
2011-01-18 20:30:13 -05:00
|
|
|
envelopes.put(uid, envelope);
|
2009-01-17 21:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return envelopes;
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* New HTTP Method that allows changing of the method and generic handling Needed for WebDAV custom methods such as
|
|
|
|
* SEARCH and PROPFIND
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public class HttpGeneric extends HttpEntityEnclosingRequestBase {
|
2008-12-06 19:29:11 -05:00
|
|
|
public String METHOD_NAME = "POST";
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public HttpGeneric() {
|
2008-12-06 19:29:11 -05:00
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public HttpGeneric(final URI uri) {
|
2008-12-06 19:29:11 -05:00
|
|
|
super();
|
|
|
|
setURI(uri);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* @throws IllegalArgumentException
|
|
|
|
* if the uri is invalid.
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public HttpGeneric(final String uri) {
|
2008-12-06 19:29:11 -05:00
|
|
|
super();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.v(K9.LOG_TAG, "Starting uri = '" + uri + "'");
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
|
|
|
|
String[] urlParts = uri.split("/");
|
|
|
|
int length = urlParts.length;
|
|
|
|
String end = urlParts[length - 1];
|
2009-12-09 22:16:51 -05:00
|
|
|
String url = "";
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-12-06 19:29:11 -05:00
|
|
|
/**
|
2010-12-17 09:40:19 -05:00
|
|
|
* We have to decode, then encode the URL because Exchange likes to not properly encode all characters
|
2008-12-06 19:29:11 -05:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
|
|
|
if (length > 3) {
|
2014-09-28 06:59:11 -04:00
|
|
|
end = UrlEncodingHelper.decodeUtf8(end);
|
|
|
|
end = com.fsck.k9.helper.UrlEncodingHelper.encodeUtf8(end);
|
2009-05-06 23:17:26 -04:00
|
|
|
end = end.replaceAll("\\+", "%20");
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IllegalArgumentException iae) {
|
2010-12-17 09:40:19 -05:00
|
|
|
Log.e(K9.LOG_TAG, "IllegalArgumentException caught in HttpGeneric(String uri): " + iae + "\nTrace: "
|
2010-12-24 13:55:05 -05:00
|
|
|
+ processException(iae));
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0; i < length - 1; i++) {
|
|
|
|
if (i != 0) {
|
2008-12-06 19:29:11 -05:00
|
|
|
url = url + "/" + urlParts[i];
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-12-06 19:29:11 -05:00
|
|
|
url = urlParts[i];
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG && K9.DEBUG_PROTOCOL_WEBDAV) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.v(K9.LOG_TAG, "url = '" + url + "' length = " + url.length()
|
2010-12-24 13:55:05 -05:00
|
|
|
+ ", end = '" + end + "' length = " + end.length());
|
2009-10-24 22:58:26 -04:00
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
url = url + "/" + end;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.i(K9.LOG_TAG, "url = " + url);
|
2008-12-06 19:29:11 -05:00
|
|
|
setURI(URI.create(url));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getMethod() {
|
2008-12-06 19:29:11 -05:00
|
|
|
return METHOD_NAME;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setMethod(String method) {
|
|
|
|
if (method != null) {
|
2008-12-06 19:29:11 -05:00
|
|
|
METHOD_NAME = method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-10-23 21:02:46 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public static class WebDavHttpClient extends DefaultHttpClient {
|
2009-11-15 10:19:39 -05:00
|
|
|
/*
|
2010-12-17 09:40:19 -05:00
|
|
|
* Copyright (C) 2007 The Android Open Source Project
|
2010-12-24 13:55:05 -05:00
|
|
|
*
|
2010-12-17 09:40:19 -05:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
|
|
|
|
* compliance with the License. You may obtain a copy of the License at
|
2010-12-24 13:55:05 -05:00
|
|
|
*
|
2010-12-17 09:40:19 -05:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2010-12-24 13:55:05 -05:00
|
|
|
*
|
2010-12-17 09:40:19 -05:00
|
|
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
|
|
|
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
|
|
|
* the License for the specific language governing permissions and limitations under the License.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public static void modifyRequestToAcceptGzipResponse(HttpRequest request) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.i(K9.LOG_TAG, "Requesting gzipped data");
|
2009-11-15 10:19:39 -05:00
|
|
|
request.addHeader("Accept-Encoding", "gzip");
|
|
|
|
}
|
2010-10-23 21:02:46 -04:00
|
|
|
|
2009-11-15 10:19:39 -05:00
|
|
|
public static InputStream getUngzippedContent(HttpEntity entity)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws IOException {
|
2009-11-15 10:19:39 -05:00
|
|
|
InputStream responseStream = entity.getContent();
|
2010-12-17 09:40:19 -05:00
|
|
|
if (responseStream == null)
|
|
|
|
return responseStream;
|
2009-11-15 10:19:39 -05:00
|
|
|
Header header = entity.getContentEncoding();
|
2010-12-17 09:40:19 -05:00
|
|
|
if (header == null)
|
|
|
|
return responseStream;
|
2009-11-15 10:19:39 -05:00
|
|
|
String contentEncoding = header.getValue();
|
2010-12-17 09:40:19 -05:00
|
|
|
if (contentEncoding == null)
|
|
|
|
return responseStream;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (contentEncoding.contains("gzip")) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.i(K9.LOG_TAG, "Response is gzipped");
|
2009-11-15 10:19:39 -05:00
|
|
|
responseStream = new GZIPInputStream(responseStream);
|
|
|
|
}
|
|
|
|
return responseStream;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-10-23 21:03:29 -04:00
|
|
|
public HttpResponse executeOverride(HttpUriRequest request, HttpContext context)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws IOException {
|
2009-11-15 10:19:39 -05:00
|
|
|
modifyRequestToAcceptGzipResponse(request);
|
2010-10-23 21:03:29 -04:00
|
|
|
return super.execute(request, context);
|
2009-11-15 10:19:39 -05:00
|
|
|
}
|
2010-10-23 21:03:29 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-10-23 21:03:29 -04:00
|
|
|
/**
|
|
|
|
* Simple data container for passing connection information.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
private static class ConnectionInfo {
|
2010-10-23 21:03:29 -04:00
|
|
|
public int statusCode;
|
|
|
|
public short requiredAuthType;
|
|
|
|
public String guessedAuthUrl;
|
|
|
|
public String redirectUrl;
|
2009-11-15 10:19:39 -05:00
|
|
|
}
|
2008-12-06 19:29:11 -05:00
|
|
|
}
|