k-9/src/com/fsck/k9/mail/store/WebDavStore.java

1682 lines
63 KiB
Java
Raw Normal View History

Merge into 'trunk' r51837@31b (orig r127): ismarc31 | 2008-11-10 19:10:50 -0500 Experimental branch for Exchange WebDAV support r51838@31b (orig r128): ismarc31 | 2008-11-10 19:24:52 -0500 Initial proof-of-concept code for WebDav support r51839@31b (orig r129): ismarc31 | 2008-11-10 22:02:37 -0500 Fixed a couple of migration issues and enabled WebDav as a mail type r53269@31b (orig r132): ismarc31 | 2008-11-21 21:55:55 -0500 Mostly rewritten class and organization. Better implementation of message fetching. Consolidated response parsing. Removed a large number of redundant calls. There is still some unused functions needing cleaning up, and some unimplemented actions r53338@31b (orig r133): ismarc31 | 2008-11-22 16:50:02 -0500 Removed more redundant and unused calls. Implemented checking read status r53453@31b (orig r134): ismarc31 | 2008-11-24 20:13:24 -0500 Added support for marking messages as read. r53454@31b (orig r135): ismarc31 | 2008-11-24 22:04:04 -0500 Added support for deleting messages server side r53455@31b (orig r136): ismarc31 | 2008-11-25 01:32:19 -0500 Improved flag setting functionality, do bulk HTTP request instead of lots of little ones r53589@31b (orig r138): young.bradley | 2008-11-29 16:18:25 -0500 Missing some ports (webDavPorts); this causes an array index out of bounds exception when anything other than "None" or "SSL (Optional)" are selected. Adding the three additional ports solves this issue. r53590@31b (orig r139): young.bradley | 2008-11-30 00:47:42 -0500 Initial support for sending via WebDav r53591@31b (orig r140): ismarc31 | 2008-11-30 20:12:41 -0500 Fix for display names being URL Encoded for folders. Initial support of Uid Hashmaps instead of plain arrays. r53592@31b (orig r141): ismarc31 | 2008-11-30 21:46:06 -0500 Fix to constructor of HttpGeneric(final String uri). URLs returned from Exchange aren't always fully encoded, this fixes the encoding before creating the method. r53593@31b (orig r142): ismarc31 | 2008-12-01 02:22:16 -0500 Completed support for using hashmaps instead of arrays for indexing urls to emails and read status. Delete is safe again and read status is correct the first time through. r53594@31b (orig r143): ismarc31 | 2008-12-01 22:20:50 -0500 Fix for double-Inbox display issue. Removed volumous amounts of Log.d messages. r53644@31b (orig r157): young.bradley | 2008-12-04 15:14:28 -0500 Fix for wildcard certificates (e.g. issued to *.example.com). Only checking the trust of the certificate itself, since apparently the full chain causes it to not work. r53765@31b (orig r161): ismarc31 | 2008-12-06 18:55:08 -0500 Implemented new functionality for pulling message envelope. Uses a WebDAV call for all messages rather than parsing the stream. Message size is properly set now as well. r54055@31b (orig r163): jessev | 2008-12-06 19:28:24 -0500 * merge fixes
2008-12-06 19:29:11 -05:00
package com.fsck.k9.mail.store;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Stack;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
import com.fsck.k9.k9;
import com.fsck.k9.mail.FetchProfile;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessageRetrievalListener;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.internet.MimeBodyPart;
import com.fsck.k9.mail.internet.MimeMessage;
import org.apache.http.HttpResponse;
import org.apache.http.HttpEntity;
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.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
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;
/**
* <pre>
* Uses WebDAV formatted HTTP calls to an MS Exchange server to fetch emails
* and email information. This has only been tested on an MS Exchange
* Server 2003. It uses Form-Based authentication and requires that
* Outlook Web Access be enabled on the server.
* </pre>
*/
public class WebDavStore extends Store {
public static final int CONNECTION_SECURITY_NONE = 0;
public static final int CONNECTION_SECURITY_TLS_OPTIONAL = 1;
public static final int CONNECTION_SECURITY_TLS_REQUIRED = 2;
public static final int CONNECTION_SECURITY_SSL_REQUIRED = 3;
public static final int CONNECTION_SECURITY_SSL_OPTIONAL = 4;
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.SEEN, Flag.ANSWERED };
private int mConnectionSecurity;
private String mUsername; /* Stores the username for authentications */
private String alias;
private String mPassword; /* Stores the password for authentications */
private String mUrl; /* Stores the base URL for the server */
private CookieStore mAuthCookies; /* Stores cookies from authentication */
private boolean mAuthenticated = false; /* Stores authentication state */
private long mLastAuth = -1; /* Stores the timestamp of last auth */
private long mAuthTimeout = 5 * 60;
/**
* webdav://user:password@server:port CONNECTION_SECURITY_NONE
* webdav+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
* webdav+tls+://user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
* webdav+ssl+://user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
* webdav+ssl://user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
*
* @param _uri
*/
public WebDavStore(String _uri) throws MessagingException {
URI uri;
try {
uri = new URI(_uri);
} catch (URISyntaxException use) {
throw new MessagingException("Invalid WebDavStore URI", use);
}
String scheme = uri.getScheme();
if (scheme.equals("webdav")) {
mConnectionSecurity = CONNECTION_SECURITY_NONE;
} else if (scheme.equals("webdav+ssl")) {
mConnectionSecurity = CONNECTION_SECURITY_SSL_OPTIONAL;
} else if (scheme.equals("webdav+ssl+")) {
mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
} else if (scheme.equals("webdav+tls")) {
mConnectionSecurity = CONNECTION_SECURITY_TLS_OPTIONAL;
} else if (scheme.equals("webdav+tls+")) {
mConnectionSecurity = CONNECTION_SECURITY_TLS_REQUIRED;
} else {
throw new MessagingException("Unsupported protocol");
}
String host = uri.getHost();
if (host.startsWith("http")) {
String[] hostParts = host.split("://", 2);
if (hostParts.length > 1) {
host = hostParts[1];
}
}
if (mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED ||
mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED ||
mConnectionSecurity == CONNECTION_SECURITY_TLS_OPTIONAL ||
mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
this.mUrl = "https://" + host;
} else {
this.mUrl = "http://" + host;
}
if (uri.getUserInfo() != null) {
String[] userInfoParts = uri.getUserInfo().split(":", 2);
mUsername = userInfoParts[0];
String userParts[] = mUsername.split("/", 2);
if (userParts.length > 1) {
alias = userParts[1];
} else {
alias = mUsername;
}
if (userInfoParts.length > 1) {
mPassword = userInfoParts[1];
}
}
}
@Override
public void checkSettings() throws MessagingException {
Log.e(k9.LOG_TAG, "WebDavStore.checkSettings() not implemented");
}
@Override
public Folder[] getPersonalNamespaces() throws MessagingException {
ArrayList<Folder> folderList = new ArrayList<Folder>();
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpEntity responseEntity;
HttpGeneric httpmethod;
HttpResponse response;
StringEntity messageEntity;
String messageBody;
int status_code;
if (needAuth()) {
authenticate();
}
if (this.mAuthenticated == false ||
this.mAuthCookies == null) {
return folderList.toArray(new Folder[] {});
}
try {
/** Set up and execute the request */
httpclient.setCookieStore(this.mAuthCookies);
messageBody = getFolderListXml();
messageEntity = new StringEntity(messageBody);
messageEntity.setContentType("text/xml");
httpmethod = new HttpGeneric(this.mUrl + "/Exchange/" + this.mUsername);
httpmethod.setMethod("SEARCH");
httpmethod.setEntity(messageEntity);
httpmethod.setHeader("Brief", "t");
response = httpclient.execute(httpmethod);
status_code = response.getStatusLine().getStatusCode();
if (status_code < 200 ||
status_code > 300) {
throw new IOException("Error getting folder listing");
}
responseEntity = response.getEntity();
if (responseEntity != null) {
/** Parse the returned data */
try {
InputStream istream = responseEntity.getContent();
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
WebDavHandler myHandler = new WebDavHandler();
xr.setContentHandler(myHandler);
xr.parse(new InputSource(istream));
ParsedDataSet dataset = myHandler.getDataSet();
String[] folderUrls = dataset.getHrefs();
int urlLength = folderUrls.length;
for (int i = 0; i < urlLength; i++) {
String[] urlParts = folderUrls[i].split("/");
String folderName = urlParts[urlParts.length - 1];
if (folderName.equalsIgnoreCase(k9.INBOX)) {
folderName = "INBOX";
}
folderList.add(getFolder(java.net.URLDecoder.decode(folderName, "UTF-8")));
}
} catch (SAXException se) {
Log.e(k9.LOG_TAG, "Error with SAXParser " + se);
} catch (ParserConfigurationException pce) {
Log.e(k9.LOG_TAG, "Error with SAXParser " + pce);
}
}
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "Error with encoding " + uee);
} catch (IOException ioe) {
Log.e(k9.LOG_TAG, "IOException " + ioe);
}
return folderList.toArray(new WebDavFolder[] {});
}
@Override
public Folder getFolder(String name) throws MessagingException {
WebDavFolder folder;
folder = new WebDavFolder(name);
return folder;
}
/***************************************************************
* WebDAV XML Request body retrieval functions
*/
private String getFolderListXml() {
StringBuffer buffer = new StringBuffer(200);
buffer.append("<?xml version='1.0' ?>");
buffer.append("<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n");
buffer.append("SELECT \"DAV:ishidden\"\r\n");
buffer.append(" FROM \"\"\r\n");
buffer.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=True\r\n");
buffer.append("</a:sql></a:searchrequest>\r\n");
return buffer.toString();
}
private String getMessageCountXml(String messageState) {
StringBuffer buffer = new StringBuffer(200);
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");
buffer.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=False AND \"urn:schemas:httpmail:read\"="+messageState+"\r\n");
buffer.append(" GROUP BY \"DAV:ishidden\"\r\n");
buffer.append("</a:sql></a:searchrequest>\r\n");
return buffer.toString();
}
private String getMessageEnvelopeXml(String[] uids) {
StringBuffer buffer = new StringBuffer(200);
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:received\",");
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:return-path\",");
buffer.append(" \"urn:schemas:mailheader:cc\",");
buffer.append(" \"urn:schemas:mailheader:references\",");
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 ");
for (int i = 0, count = uids.length; i < count; i++) {
if (i != 0) {
buffer.append(" OR ");
}
buffer.append(" \"DAV:uid\"='"+uids[i]+"' ");
}
buffer.append("\r\n");
buffer.append("</a:sql></a:searchrequest>\r\n");
return buffer.toString();
}
private String getMessagesXml() {
StringBuffer buffer = new StringBuffer(200);
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();
}
private String getMessageUrlsXml(String[] uids) {
StringBuffer buffer = new StringBuffer(600);
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 ");
for (int i = 0, count = uids.length; i < count; i++) {
if (i != 0) {
buffer.append(" OR ");
}
buffer.append(" \"DAV:uid\"='"+uids[i]+"' ");
}
buffer.append("\r\n");
buffer.append("</a:sql></a:searchrequest>\r\n");
return buffer.toString();
}
private String getMessageFlagsXml(String[] uids) throws MessagingException {
if (uids.length == 0) {
throw new MessagingException("Attempt to get flags on 0 length array for uids");
}
StringBuffer buffer = new StringBuffer(200);
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 ");
for (int i = 0, count = uids.length; i < count; i++) {
if (i != 0) {
buffer.append(" OR ");
}
buffer.append(" \"DAV:uid\"='"+uids[i]+"' ");
}
buffer.append("\r\n");
buffer.append("</a:sql></a:searchrequest>\r\n");
return buffer.toString();
}
private String getMarkMessagesReadXml(String[] urls) {
StringBuffer buffer = new StringBuffer(600);
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");
for (int i = 0, count = urls.length; i < count; i++) {
buffer.append(" <a:href>"+urls[i].substring(urls[i].lastIndexOf('/') + 1)+"</a:href>\r\n");
}
buffer.append("</a:target>\r\n");
buffer.append("<a:set>\r\n");
buffer.append(" <a:prop>\r\n");
buffer.append(" <b:read>1</b:read>\r\n");
buffer.append(" </a:prop>\r\n");
buffer.append("</a:set>\r\n");
buffer.append("</a:propertyupdate>\r\n");
return buffer.toString();
}
/***************************************************************
* Authentication related methods
*/
/**
* Performs Form Based authentication regardless of the current
* authentication state
*/
public void authenticate() {
try {
this.mAuthCookies = doAuthentication(this.mUsername, this.mPassword, this.mUrl);
} catch (IOException ioe) {
Log.e(k9.LOG_TAG, "Error during authentication: " + ioe);
this.mAuthCookies = null;
}
if (this.mAuthCookies == null) {
this.mAuthenticated = false;
} else {
this.mAuthenticated = true;
this.mLastAuth = System.currentTimeMillis()/1000;
}
}
/**
* Determines if a new authentication is needed.
* Returns true if new authentication is needed.
*/
public boolean needAuth() {
boolean status = false;
long currentTime = -1;
if (this.mAuthenticated == false) {
status = true;
}
currentTime = System.currentTimeMillis()/1000;
if ((currentTime - this.mLastAuth) > (this.mAuthTimeout)) {
status = true;
}
return status;
}
/**
* Performs the Form Based Authentication
* Returns the CookieStore object for later use or null
*/
public CookieStore doAuthentication(String username, String password,
String url) throws IOException {
String authPath = "/exchweb/bin/auth/owaauth.dll";
CookieStore cookies = null;
/* Browser Client */
DefaultHttpClient httpclient = new DefaultHttpClient();
/* Post Method */
HttpPost httppost = new HttpPost(url + authPath);
/** Build the POST data to use */
ArrayList<BasicNameValuePair> pairs = new ArrayList();
pairs.add(new BasicNameValuePair("username", username));
pairs.add(new BasicNameValuePair("password", password));
pairs.add(new BasicNameValuePair("destination", url + "/Exchange/"));
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"));
try {
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(pairs);
httppost.setEntity(formEntity);
/** Perform the actual POST */
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
int status_code = response.getStatusLine().getStatusCode();
/** Verify success */
if (status_code > 300 ||
status_code < 200) {
throw new IOException("Error during authentication: "+status_code);
}
cookies = httpclient.getCookieStore();
if (cookies == null) {
throw new IOException("Error during authentication: No Cookies");
}
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "Error encoding POST data for authencation");
}
return cookies;
}
public CookieStore getAuthCookies() {
return mAuthCookies;
}
public String getAlias() {
return alias;
}
public String getUrl() {
return mUrl;
}
/*************************************************************************
* Helper and Inner classes
*/
/**
* A WebDav Folder
*/
class WebDavFolder extends Folder {
private String mName;
private String mLocalUsername;
private String mFolderUrl;
private boolean mIsOpen = false;
private int mMessageCount = 0;
private int mUnreadMessageCount = 0;
public WebDavFolder(String name) {
String[] userParts;
String encodedName = new String();
try {
encodedName = java.net.URLEncoder.encode(name, "UTF-8");
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "UnsupportedEncodingException URLEncoding folder name, skipping encoded");
encodedName = name;
}
encodedName = encodedName.replaceAll("\\+", "%20");
this.mName = name;
userParts = WebDavStore.this.mUsername.split("/", 2);
if (userParts.length > 1) {
this.mLocalUsername = userParts[1];
} else {
this.mLocalUsername = WebDavStore.this.mUsername;
}
this.mFolderUrl = WebDavStore.this.mUrl + "/Exchange/" + this.mLocalUsername + "/" + encodedName;
}
@Override
public void open(OpenMode mode) throws MessagingException {
if (needAuth()) {
authenticate();
}
if (WebDavStore.this.mAuthCookies == null) {
return;
}
this.mIsOpen = true;
}
private int getMessageCount(boolean read, CookieStore authCookies) {
String isRead;
int messageCount = 0;
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpGeneric httpmethod;
HttpResponse response;
HttpEntity responseEntity;
StringEntity bodyEntity;
String messageBody;
int statusCode;
if (read) {
isRead = new String("True");
} else {
isRead = new String("False");
}
httpclient.setCookieStore(authCookies);
messageBody = getMessageCountXml(isRead);
try {
bodyEntity = new StringEntity(messageBody);
bodyEntity.setContentType("text/xml");
httpmethod = new HttpGeneric(this.mFolderUrl);
httpmethod.setMethod("SEARCH");
httpmethod.setEntity(bodyEntity);
httpmethod.setHeader("Brief", "t");
response = httpclient.execute(httpmethod);
statusCode = response.getStatusLine().getStatusCode();
if (statusCode < 200 ||
statusCode > 300) {
throw new IOException("Error getting message count, status code was " + statusCode);
}
responseEntity = response.getEntity();
if (responseEntity != null) {
try {
ParsedDataSet dataset = new ParsedDataSet();
InputStream istream = responseEntity.getContent();
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
WebDavHandler myHandler = new WebDavHandler();
xr.setContentHandler(myHandler);
xr.parse(new InputSource(istream));
dataset = myHandler.getDataSet();
messageCount = dataset.getMessageCount();
istream.close();
} catch (SAXException se) {
Log.e(k9.LOG_TAG, "SAXException in getMessageCount " + se);
} catch (ParserConfigurationException pce) {
Log.e(k9.LOG_TAG, "ParserConfigurationException in getMessageCount " + pce);
}
}
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "UnsupportedEncodingException in getMessageCount() " + uee);
} catch (IOException ioe) {
Log.e(k9.LOG_TAG, "IOException in getMessageCount() " + ioe);
}
return messageCount;
}
@Override
public int getMessageCount() throws MessagingException {
open(OpenMode.READ_WRITE);
this.mMessageCount = getMessageCount(true, WebDavStore.this.mAuthCookies);
return this.mMessageCount;
}
@Override
public int getUnreadMessageCount() throws MessagingException {
open(OpenMode.READ_WRITE);
this.mUnreadMessageCount = getMessageCount(false, WebDavStore.this.mAuthCookies);
return this.mUnreadMessageCount;
}
@Override
public boolean isOpen() {
return this.mIsOpen;
}
@Override
public OpenMode getMode() throws MessagingException {
return OpenMode.READ_WRITE;
}
@Override
public String getName() {
return this.mName;
}
@Override
public boolean exists() {
return true;
}
@Override
public void close(boolean expunge) throws MessagingException {
this.mMessageCount = 0;
this.mUnreadMessageCount = 0;
this.mIsOpen = false;
}
@Override
public boolean create(FolderType type) throws MessagingException {
return true;
}
@Override
public void delete(boolean recursive) throws MessagingException {
throw new Error("WebDavFolder.delete() not implemeneted");
}
@Override
public Message getMessage(String uid) throws MessagingException {
return new WebDavMessage(uid, this);
}
@Override
public Message[] getMessages(int start, int end, MessageRetrievalListener listener)
throws MessagingException {
DefaultHttpClient httpclient = new DefaultHttpClient();
ArrayList<Message> messages = new ArrayList<Message>();
String[] uids;
String messageBody;
int prevStart = start;
/** Reverse the message range since 0 index is newest */
start = this.mMessageCount - end;
end = this.mMessageCount - prevStart;
if (start < 0 || end < 0 || end < start) {
throw new MessagingException(String.format("Invalid message set %d %d", start, end));
}
/** Verify authentication */
if (needAuth()) {
authenticate();
}
if (WebDavStore.this.mAuthenticated == false ||
WebDavStore.this.mAuthCookies == null) {
return messages.toArray(new Message[] {});
}
/** Retrieve and parse the XML entity for our messages */
httpclient.setCookieStore(WebDavStore.this.mAuthCookies);
messageBody = getMessagesXml();
try {
int status_code = -1;
StringEntity messageEntity = new StringEntity(messageBody);
HttpGeneric httpmethod = new HttpGeneric(this.mFolderUrl);
HttpResponse response;
HttpEntity entity;
messageEntity.setContentType("text/xml");
httpmethod.setMethod("SEARCH");
httpmethod.setEntity(messageEntity);
httpmethod.setHeader("Brief", "t");
httpmethod.setHeader("Range", "rows=" + start + "-" + end);
response = httpclient.execute(httpmethod);
status_code = response.getStatusLine().getStatusCode();
if (status_code < 200 ||
status_code > 300) {
throw new IOException("Error getting messages, returned HTTP Response code " + status_code);
}
entity = response.getEntity();
if (entity != null) {
try {
InputStream istream = entity.getContent();
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
WebDavHandler myHandler = new WebDavHandler();
ParsedDataSet dataset;
int uidsLength = 0;
int urlsLength = 0;
xr.setContentHandler(myHandler);
xr.parse(new InputSource(istream));
dataset = myHandler.getDataSet();
uids = dataset.getUids();
HashMap<String, String> uidToUrl = dataset.getUidToUrl();
uidsLength = uids.length;
for (int i = 0; i < uidsLength; i++) {
if (listener != null) {
listener.messageStarted(uids[i], i, uidsLength);
}
WebDavMessage message = new WebDavMessage(uids[i], this);
message.setUrl(uidToUrl.get(uids[i]));
messages.add(message);
if (listener != null) {
listener.messageFinished(message, i, uidsLength);
}
}
} catch (SAXException se) {
Log.e(k9.LOG_TAG, "SAXException in getMessages() " + se);
} catch (ParserConfigurationException pce) {
Log.e(k9.LOG_TAG, "ParserConfigurationException in getMessages() " + pce);
}
}
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "UnsupportedEncodingException: " + uee);
} catch (IOException ioe) {
Log.e(k9.LOG_TAG, "IOException: " + ioe);
}
return messages.toArray(new Message[] {});
}
@Override
public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException {
return getMessages(null, listener);
}
@Override
public Message[] getMessages(String[] uids, MessageRetrievalListener listener) throws MessagingException {
ArrayList<Message> messageList = new ArrayList<Message>();
Message[] messages;
if (uids == null) {
messages = getMessages(0, k9.DEFAULT_VISIBLE_LIMIT, listener);
} else {
for (int i = 0, count = uids.length; i < count; i++) {
if (listener != null) {
listener.messageStarted(uids[i], i, count);
}
WebDavMessage message = new WebDavMessage(uids[i], this);
messageList.add(message);
if (listener != null) {
listener.messageFinished(message, i, count);
}
}
messages = messageList.toArray(new Message[] {});
}
return messages;
}
private HashMap<String, String> getMessageUrls(String[] uids) {
HashMap<String, String> uidToUrl = new HashMap<String, String>();
DefaultHttpClient httpclient = new DefaultHttpClient();
String messageBody;
/** Verify authentication */
if (needAuth()) {
authenticate();
}
if (WebDavStore.this.mAuthenticated == false ||
WebDavStore.this.mAuthCookies == null) {
return uidToUrl;
}
/** Retrieve and parse the XML entity for our messages */
httpclient.setCookieStore(WebDavStore.this.mAuthCookies);
messageBody = getMessageUrlsXml(uids);
try {
int status_code = -1;
StringEntity messageEntity = new StringEntity(messageBody);
HttpGeneric httpmethod = new HttpGeneric(this.mFolderUrl);
HttpResponse response;
HttpEntity entity;
messageEntity.setContentType("text/xml");
httpmethod.setMethod("SEARCH");
httpmethod.setEntity(messageEntity);
httpmethod.setHeader("Brief", "t");
response = httpclient.execute(httpmethod);
status_code = response.getStatusLine().getStatusCode();
if (status_code < 200 ||
status_code > 300) {
throw new IOException("Error getting messages, returned HTTP Response code " + status_code);
}
entity = response.getEntity();
if (entity != null) {
try {
InputStream istream = entity.getContent();
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
WebDavHandler myHandler = new WebDavHandler();
ParsedDataSet dataset;
int uidsLength = 0;
int urlsLength = 0;
xr.setContentHandler(myHandler);
xr.parse(new InputSource(istream));
dataset = myHandler.getDataSet();
uidToUrl = dataset.getUidToUrl();
} catch (SAXException se) {
Log.e(k9.LOG_TAG, "SAXException in getMessages() " + se);
} catch (ParserConfigurationException pce) {
Log.e(k9.LOG_TAG, "ParserConfigurationException in getMessages() " + pce);
}
}
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "UnsupportedEncodingException: " + uee);
} catch (IOException ioe) {
Log.e(k9.LOG_TAG, "IOException: " + ioe);
}
return uidToUrl;
}
@Override
public void fetch(Message[] messages, FetchProfile fp, MessageRetrievalListener listener)
throws MessagingException {
HashMap<String, Boolean> uidToReadStatus = new HashMap<String, Boolean>();
HashMap<String, ParsedMessageEnvelope> envelopes = new HashMap<String, ParsedMessageEnvelope>();
if (messages == null ||
messages.length == 0) {
return;
}
/**
* Get message info for all messages here since it can be pulled with
* a single request. Header data will be set in the for loop.
* Listener isn't started yet since it isn't a per-message lookup.
*/
if (fp.contains(FetchProfile.Item.ENVELOPE)) {
DefaultHttpClient httpclient = new DefaultHttpClient();
String messageBody = new String();
String[] uids = new String[messages.length];
for (int i = 0, count = messages.length; i < count; i++) {
uids[i] = messages[i].getUid();
}
httpclient.setCookieStore(WebDavStore.this.mAuthCookies);
messageBody = getMessageEnvelopeXml(uids);
try {
int status_code = -1;
StringEntity messageEntity = new StringEntity(messageBody);
HttpGeneric httpmethod = new HttpGeneric(this.mFolderUrl);
HttpResponse response;
HttpEntity entity;
messageEntity.setContentType("text/xml");
httpmethod.setMethod("SEARCH");
httpmethod.setEntity(messageEntity);
httpmethod.setHeader("Brief", "t");
response = httpclient.execute(httpmethod);
status_code = response.getStatusLine().getStatusCode();
if (status_code < 200 ||
status_code > 300) {
throw new IOException("Error getting message envelopes, returned HTTP Response code " + status_code);
}
entity = response.getEntity();
if (entity != null) {
try {
InputStream istream = entity.getContent();
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
WebDavHandler myHandler = new WebDavHandler();
ParsedDataSet dataset;
xr.setContentHandler(myHandler);
xr.parse(new InputSource(istream));
dataset = myHandler.getDataSet();
envelopes = dataset.getMessageEnvelopes();
} catch (SAXException se) {
Log.e(k9.LOG_TAG, "SAXException in fetch() " + se);
} catch (ParserConfigurationException pce) {
Log.e(k9.LOG_TAG, "ParserConfigurationException in fetch() " + pce);
}
}
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "UnsupportedEncodingException: " + uee);
} catch (IOException ioe) {
Log.e(k9.LOG_TAG, "IOException: " + ioe);
}
}
/**
* Check for flags and get the status here since it can be pulled with
* just one request. Flags will be set inside the for loop.
* Listener isn't started yet since it isn't a per-message lookup.
*/
if (fp.contains(FetchProfile.Item.FLAGS)) {
DefaultHttpClient httpclient = new DefaultHttpClient();
String messageBody = new String();
String[] uids = new String[messages.length];
for (int i = 0, count = messages.length; i < count; i++) {
uids[i] = messages[i].getUid();
}
httpclient.setCookieStore(WebDavStore.this.mAuthCookies);
messageBody = getMessageFlagsXml(uids);
try {
int status_code = -1;
StringEntity messageEntity = new StringEntity(messageBody);
HttpGeneric httpmethod = new HttpGeneric(this.mFolderUrl);
HttpResponse response;
HttpEntity entity;
messageEntity.setContentType("text/xml");
httpmethod.setMethod("SEARCH");
httpmethod.setEntity(messageEntity);
httpmethod.setHeader("Brief", "t");
response = httpclient.execute(httpmethod);
status_code = response.getStatusLine().getStatusCode();
if (status_code < 200 ||
status_code > 300) {
throw new IOException("Error getting message flags, returned HTTP Response code " + status_code);
}
entity = response.getEntity();
if (entity != null) {
try {
InputStream istream = entity.getContent();
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
WebDavHandler myHandler = new WebDavHandler();
ParsedDataSet dataset;
xr.setContentHandler(myHandler);
xr.parse(new InputSource(istream));
dataset = myHandler.getDataSet();
uidToReadStatus = dataset.getUidToRead();
} catch (SAXException se) {
Log.e(k9.LOG_TAG, "SAXException in fetch() " + se);
} catch (ParserConfigurationException pce) {
Log.e(k9.LOG_TAG, "ParserConfigurationException in fetch() " + pce);
}
}
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "UnsupportedEncodingException: " + uee);
} catch (IOException ioe) {
Log.e(k9.LOG_TAG, "IOException: " + ioe);
}
}
for (int i = 0, count = messages.length; i < count; i++) {
if (!(messages[i] instanceof WebDavMessage)) {
throw new MessagingException("WebDavStore fetch called with non-WebDavMessage");
}
WebDavMessage wdMessage = (WebDavMessage) messages[i];
if (listener != null) {
listener.messageStarted(wdMessage.getUid(), i, count);
}
if (fp.contains(FetchProfile.Item.FLAGS)) {
wdMessage.setFlagInternal(Flag.SEEN, uidToReadStatus.get(wdMessage.getUid()));
}
if (fp.contains(FetchProfile.Item.ENVELOPE)) {
wdMessage.setNewHeaders(envelopes.get(wdMessage.getUid()));
}
/**
* Set the body to null if it's asking for the structure because
* we don't support it yet.
*/
if (fp.contains(FetchProfile.Item.STRUCTURE)) {
wdMessage.setBody(null);
}
/**
* Message fetching that we can pull as a stream
*/
if (fp.contains(FetchProfile.Item.BODY) ||
fp.contains(FetchProfile.Item.BODY_SANE)) {
DefaultHttpClient httpclient = new DefaultHttpClient();
InputStream istream = null;
InputStream resultStream = null;
HttpGet httpget;
HttpEntity entity;
HttpResponse response;
int statusCode = 0;
try {
httpclient.setCookieStore(WebDavStore.this.mAuthCookies);
httpget = new HttpGet(new URI(wdMessage.getUrl()));
httpget.setHeader("translate", "f");
response = httpclient.execute(httpget);
statusCode = response.getStatusLine().getStatusCode();
if (statusCode < 200 ||
statusCode > 300) {
throw new IOException("Status Code in invalid range");
}
entity = response.getEntity();
if (entity != null) {
String resultText = new String();
String tempText = new String();
BufferedReader reader;
resultText = "";
istream = entity.getContent();
/**
* Keep this commented out for now, messages won't display properly if
* we do it like this.
*/
/**
if (fp.contains(FetchProfile.Item.BODY_SANE)) {
int lines = FETCH_BODY_SANE_SUGGESTED_SIZE / 76;
int line = 0;
reader = new BufferedReader(new InputStreamReader(istream),4096);
while ((tempText = reader.readLine()) != null &&
(line < lines)) {
if (resultText.equals("")) {
resultText = tempText;
} else {
resultText = resultText + "\r\n" + tempText;
}
line++;
}
istream.close();
istream = new ByteArrayInputStream(resultText.getBytes("UTF-8"));
}*/
wdMessage.parse(istream);
}
} catch (IllegalArgumentException iae) {
Log.e(k9.LOG_TAG, "IllegalArgumentException caught " + iae);
} catch (URISyntaxException use) {
Log.e(k9.LOG_TAG, "URISyntaxException caught " + use);
} catch (IOException ioe) {
Log.e(k9.LOG_TAG, "Non-success response code loading message, response code was " + statusCode);
}
}
if (listener != null) {
listener.messageFinished(wdMessage, i, count);
}
}
}
@Override
public Flag[] getPermanentFlags() throws MessagingException {
return PERMANENT_FLAGS;
}
@Override
public void setFlags(Message[] messages, Flag[] flags, boolean value)
throws MessagingException {
String[] uids = new String[messages.length];
if (needAuth()) {
authenticate();
}
if (WebDavStore.this.mAuthenticated == false ||
WebDavStore.this.mAuthCookies == null) {
return;
}
for (int i = 0, count = messages.length; i < count; i++) {
uids[i] = messages[i].getUid();
}
for (int i = 0, count = flags.length; i < count; i++) {
Flag flag = flags[i];
if (flag == Flag.SEEN) {
markServerMessagesRead(uids);
} else if (flag == Flag.DELETED) {
deleteServerMessages(uids);
}
}
}
private void markServerMessagesRead(String[] uids) throws MessagingException {
DefaultHttpClient httpclient = new DefaultHttpClient();
String messageBody = new String();
HashMap<String, String> uidToUrl = getMessageUrls(uids);
String[] urls = new String[uids.length];
for (int i = 0, count = uids.length; i < count; i++) {
urls[i] = uidToUrl.get(uids[i]);
}
httpclient.setCookieStore(WebDavStore.this.mAuthCookies);
messageBody = getMarkMessagesReadXml(urls);
try {
int status_code = -1;
StringEntity messageEntity = new StringEntity(messageBody);
HttpGeneric httpmethod = new HttpGeneric(this.mFolderUrl + "/");
HttpResponse response;
HttpEntity entity;
messageEntity.setContentType("text/xml");
httpmethod.setMethod("BPROPPATCH");
httpmethod.setEntity(messageEntity);
httpmethod.setHeader("Brief", "t");
httpmethod.setHeader("If-Match", "*");
response = httpclient.execute(httpmethod);
status_code = response.getStatusLine().getStatusCode();
if (status_code < 200 ||
status_code > 300) {
throw new IOException("Error marking messages as read, returned HTTP Response code " + status_code);
}
entity = response.getEntity();
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "UnsupportedEncodingException: " + uee);
} catch (IOException ioe) {
Log.e(k9.LOG_TAG, "IOException: " + ioe);
}
}
private void deleteServerMessages(String[] uids) throws MessagingException {
DefaultHttpClient httpclient = new DefaultHttpClient();
HashMap<String, String> uidToUrl = getMessageUrls(uids);
String[] urls = new String[uids.length];
httpclient.setCookieStore(WebDavStore.this.mAuthCookies);
for (int i = 0, count = uids.length; i < count; i++) {
try {
int status_code = -1;
HttpGeneric httpmethod = new HttpGeneric(uidToUrl.get(uids[i]));
HttpResponse response;
HttpEntity entity;
httpmethod.setMethod("DELETE");
httpmethod.setHeader("Brief", "t");
response = httpclient.execute(httpmethod);
status_code = response.getStatusLine().getStatusCode();
if (status_code < 200 ||
status_code > 300) {
throw new IOException("Error deleting message url: "+urls[i]+" \nResponse Code: "+status_code);
}
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "UnsupportedEncodingException: " + uee);
} catch (IOException ioe) {
Log.e(k9.LOG_TAG, "IOException: " + ioe);
}
}
}
@Override
public void appendMessages(Message[] messages) throws MessagingException {
appendMessages(messages, false);
}
public void appendMessages(Message[] messages, boolean copy) throws MessagingException {
}
@Override
public void copyMessages(Message[] msgs, Folder folder) throws MessagingException {
}
@Override
public Message[] expunge() throws MessagingException {
return null;
}
@Override
public boolean equals(Object o) {
return false;
}
}
/**
* A WebDav Message
*/
class WebDavMessage extends MimeMessage {
private String mUrl = null;
WebDavMessage(String uid, Folder folder) throws MessagingException {
this.mUid = uid;
this.mFolder = folder;
}
public void setUrl(String url) {
String[] urlParts = url.split("/");
int length = urlParts.length;
String end = urlParts[length - 1];
this.mUrl = new String();
url = new String();
/**
* We have to decode, then encode the URL because Exchange likes to
* not properly encode all characters
*/
try {
end = java.net.URLDecoder.decode(end, "UTF-8");
end = java.net.URLEncoder.encode(end, "UTF-8");
end = end.replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "UnsupportedEncodingException caught in setUrl");
} catch (IllegalArgumentException iae) {
Log.e(k9.LOG_TAG, "IllegalArgumentException caught in setUrl");
}
for (int i = 0; i < length - 1; i++) {
if (i != 0) {
url = url + "/" + urlParts[i];
} else {
url = urlParts[i];
}
}
url = url + "/" + end;
this.mUrl = url;
}
public String getUrl() {
return this.mUrl;
}
public void setSize(int size) {
this.mSize = size;
}
public void parse(InputStream in) throws IOException, MessagingException {
super.parse(in);
}
public void setFlagInternal(Flag flag, boolean set) throws MessagingException {
super.setFlag(flag, set);
}
public void setNewHeaders(ParsedMessageEnvelope envelope) throws MessagingException {
String[] headers = envelope.getHeaderList();
HashMap<String, String> messageHeaders = envelope.getMessageHeaders();
for (int i = 0, count = headers.length; i < count; i++) {
if (headers[i].equals("Content-Length")) {
this.setSize(new Integer(messageHeaders.get(headers[i])).intValue());
}
this.addHeader(headers[i], messageHeaders.get(headers[i]));
}
}
@Override
public void setFlag(Flag flag, boolean set) throws MessagingException {
super.setFlag(flag, set);
mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set);
}
}
/**
* XML Parsing Handler
* Can handle all XML handling needs
*/
public class WebDavHandler extends DefaultHandler {
private ParsedDataSet mDataSet = new ParsedDataSet();
private Stack<String> mOpenTags = new Stack<String>();
public ParsedDataSet getDataSet() {
return this.mDataSet;
}
@Override
public void startDocument() throws SAXException {
this.mDataSet = new ParsedDataSet();
}
@Override
public void endDocument() throws SAXException {
/* Do nothing */
}
@Override
public void startElement(String namespaceURI, String localName,
String qName, Attributes atts) throws SAXException {
mOpenTags.push(localName);
}
@Override
public void endElement(String namespaceURI, String localName, String qName) {
mOpenTags.pop();
/** Reset the hash temp variables */
if (localName.equals("response")) {
this.mDataSet.addEnvelope();
this.mDataSet.clearTempData();
}
}
@Override
public void characters(char ch[], int start, int length) {
String value = new String(ch, start, length);
mDataSet.addValue(value, mOpenTags.peek());
}
}
/**
* 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
* associating UIDs to values
*/
public class ParsedMessageEnvelope {
private boolean mReadStatus = false;
private String mUid = new String();
private HashMap<String, String> mMessageHeaders = new HashMap<String, String>();
private ArrayList<String> mHeaders = new ArrayList<String>();
public void addHeader(String field, String value) {
this.mMessageHeaders.put(field, value);
this.mHeaders.add(field);
}
public HashMap<String, String> getMessageHeaders() {
return this.mMessageHeaders;
}
public String[] getHeaderList() {
return this.mHeaders.toArray(new String[] {});
}
public void setReadStatus(boolean status) {
this.mReadStatus = status;
}
public boolean getReadStatus() {
return this.mReadStatus;
}
public void setUid(String uid) {
if (uid != null) {
this.mUid = uid;
}
}
public String getUid() {
return this.mUid;
}
}
/**
* Data set for handling all XML Parses
*/
public class ParsedDataSet {
private ArrayList<String> mHrefs = new ArrayList<String>();
private ArrayList<String> mUids = new ArrayList<String>();
private ArrayList<Boolean> mReads = new ArrayList<Boolean>();
private HashMap<String, String> mUidUrls = new HashMap<String, String>();
private HashMap<String, Boolean> mUidRead = new HashMap<String, Boolean>();
private HashMap<String, ParsedMessageEnvelope> mEnvelopes = new HashMap<String, ParsedMessageEnvelope>();
private int mMessageCount = 0;
private String mTempUid = "";
private String mTempUrl = "";
private String mFrom = "";
private String mTo = "";
private String mCc = "";
private String mReceived = "";
private Boolean mTempRead;
private ParsedMessageEnvelope mEnvelope = new ParsedMessageEnvelope();
private boolean mRead;
public void addValue(String value, String tagName) {
if (tagName.equals("href")) {
this.mHrefs.add(value);
this.mTempUrl = value;
} else if (tagName.equals("visiblecount")) {
this.mMessageCount = new Integer(value).intValue();
} else if (tagName.equals("uid")) {
this.mUids.add(value);
this.mEnvelope.setUid(value);
this.mTempUid = value;
} else if (tagName.equals("read")) {
if (value.equals("0")) {
this.mReads.add(false);
this.mEnvelope.setReadStatus(false);
this.mTempRead = false;
} else {
this.mReads.add(true);
this.mEnvelope.setReadStatus(true);
this.mTempRead = true;
}
} else if (tagName.equals("received")) {
this.mReceived = this.mReceived + value;
} else if (tagName.equals("mime-version")) {
this.mEnvelope.addHeader("MIME-Version", value);
} else if (tagName.equals("content-type")) {
this.mEnvelope.addHeader("Content-Type", value);
} else if (tagName.equals("subject")) {
this.mEnvelope.addHeader("Subject", value);
} else if (tagName.equals("date")) {
value = value.replaceAll("T", " ");
String[] valueBreak = value.split("\\.");
value = valueBreak[0];
DateFormat dfInput = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
DateFormat dfOutput = new SimpleDateFormat("EEE, d MMM yy HH:mm:ss Z");
String tempDate = "";
try {
Date parsedDate = dfInput.parse(value);
tempDate = dfOutput.format(parsedDate);
} catch (java.text.ParseException pe) {
Log.e(k9.LOG_TAG, "Error parsing date: "+ pe);
}
this.mEnvelope.addHeader("Date", tempDate);
} else if (tagName.equals("thread-topic")) {
this.mEnvelope.addHeader("Thread-Topic", value);
} else if (tagName.equals("thread-index")) {
this.mEnvelope.addHeader("Thread-Index", value);
} else if (tagName.equals("from")) {
this.mFrom = this.mFrom + value;
} else if (tagName.equals("to")) {
this.mTo = this.mTo + value;
} else if (tagName.equals("in-reply-to")) {
this.mEnvelope.addHeader("In-Reply-To", value);
} else if (tagName.equals("return-path")) {
this.mEnvelope.addHeader("Return-Path", value);
} else if (tagName.equals("cc")) {
this.mCc = this.mCc + value;
} else if (tagName.equals("references")) {
this.mEnvelope.addHeader("References", value);
} else if (tagName.equals("getcontentlength")) {
this.mEnvelope.addHeader("Content-Length", value);
}
if (!this.mTempUid.equals("") &&
this.mTempRead != null) {
if (this.mTempRead) {
this.mUidRead.put(this.mTempUid, true);
} else {
this.mUidRead.put(this.mTempUid, false);
}
}
if (!this.mTempUid.equals("") &&
!this.mTempUrl.equals("")) {
this.mUidUrls.put(this.mTempUid, this.mTempUrl);
}
}
/**
* Clears the temp variables
*/
public void clearTempData() {
this.mTempUid = "";
this.mTempUrl = "";
this.mFrom = "";
this.mEnvelope = new ParsedMessageEnvelope();
}
public void addEnvelope() {
this.mEnvelope.addHeader("From", this.mFrom);
this.mEnvelope.addHeader("To", this.mTo);
this.mEnvelope.addHeader("Cc", this.mCc);
this.mEnvelope.addHeader("Received", this.mReceived);
this.mEnvelopes.put(this.mEnvelope.getUid(), this.mEnvelope);
}
/**
* Returns an array of the set of message envelope objects
*/
public HashMap<String, ParsedMessageEnvelope> getMessageEnvelopes() {
return this.mEnvelopes;
}
/**
* Returns the Uid to Url hashmap
*/
public HashMap getUidToUrl() {
return this.mUidUrls;
}
/**
* Returns the Uid to Read hashmap
*/
public HashMap getUidToRead() {
return this.mUidRead;
}
/**
* Get all stored Hrefs
*/
public String[] getHrefs() {
return this.mHrefs.toArray(new String[] {});
}
/**
* Get the first stored Href
*/
public String getHref() {
String[] hrefs = this.mHrefs.toArray(new String[] {});
return hrefs[0];
}
/**
* Get all stored Uids
*/
public String[] getUids() {
return this.mUids.toArray(new String[] {});
}
/**
* Get the first stored Uid
*/
public String getUid() {
String[] uids = this.mUids.toArray(new String[] {});
return uids[0];
}
/**
* Get message count
*/
public int getMessageCount() {
return this.mMessageCount;
}
/**
* Get all stored read statuses
*/
public Boolean[] getReadArray() {
Boolean[] readStatus = this.mReads.toArray(new Boolean[] {});
return readStatus;
}
/**
* Get the first stored read status
*/
public boolean getRead() {
return this.mRead;
}
}
/**
* New HTTP Method that allows changing of the method and generic handling
* Needed for WebDAV custom methods such as SEARCH and PROPFIND
*/
public class HttpGeneric extends HttpEntityEnclosingRequestBase {
public String METHOD_NAME = "POST";
public HttpGeneric() {
super();
}
public HttpGeneric(final URI uri) {
super();
setURI(uri);
}
/**
* @throws IllegalArgumentException if the uri is invalid.
*/
public HttpGeneric(final String uri) {
super();
String[] urlParts = uri.split("/");
int length = urlParts.length;
String end = urlParts[length - 1];
String url = new String();
/**
* We have to decode, then encode the URL because Exchange likes to
* not properly encode all characters
*/
try {
end = java.net.URLDecoder.decode(end, "UTF-8");
end = java.net.URLEncoder.encode(end, "UTF-8");
end = end.replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException uee) {
Log.e(k9.LOG_TAG, "UnsupportedEncodingException caught in HttpGeneric(String uri)");
} catch (IllegalArgumentException iae) {
Log.e(k9.LOG_TAG, "IllegalArgumentException caught in HttpGeneric(String uri)");
}
for (int i = 0; i < length - 1; i++) {
if (i != 0) {
url = url + "/" + urlParts[i];
} else {
url = urlParts[i];
}
}
url = url + "/" + end;
setURI(URI.create(url));
}
@Override
public String getMethod() {
return METHOD_NAME;
}
public void setMethod(String method) {
if (method != null) {
METHOD_NAME = method;
}
}
}
}