mirror of
https://github.com/moparisthebest/davmail
synced 2025-01-07 11:48:02 -05:00
Major refactoring: use straight inpustream instead of reader everywhere
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1175 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
1a5e963c1b
commit
c7d6607f40
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package davmail;
|
package davmail;
|
||||||
|
|
||||||
|
import davmail.exception.DavMailException;
|
||||||
import davmail.exchange.ExchangeSession;
|
import davmail.exchange.ExchangeSession;
|
||||||
import davmail.ui.tray.DavGatewayTray;
|
import davmail.ui.tray.DavGatewayTray;
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
@ -35,9 +36,74 @@ public class AbstractConnection extends Thread {
|
|||||||
INITIAL, LOGIN, USER, PASSWORD, AUTHENTICATED, STARTMAIL, RECIPIENT, MAILDATA
|
INITIAL, LOGIN, USER, PASSWORD, AUTHENTICATED, STARTMAIL, RECIPIENT, MAILDATA
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static class LineReaderInputStream extends PushbackInputStream {
|
||||||
|
final String encoding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected LineReaderInputStream(InputStream in, String encoding) {
|
||||||
|
super(in);
|
||||||
|
if (encoding == null) {
|
||||||
|
this.encoding = "ASCII";
|
||||||
|
} else {
|
||||||
|
this.encoding = encoding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String readLine() throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
int b;
|
||||||
|
while ((b = read()) > -1) {
|
||||||
|
if (b == '\r') {
|
||||||
|
int next = read();
|
||||||
|
if (next != '\n') {
|
||||||
|
unread(next);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else if (b == '\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
baos.write(b);
|
||||||
|
}
|
||||||
|
return new String(baos.toByteArray(), encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read byteSize bytes from inputStream, return content as String.
|
||||||
|
* @param byteSize content size
|
||||||
|
* @return content
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
|
public String readContentAsString(int byteSize) throws IOException {
|
||||||
|
return new String(readContent(byteSize), encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read byteSize bytes from inputStream, return content as byte array.
|
||||||
|
* @param byteSize content size
|
||||||
|
* @return content
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
|
public byte[] readContent(int byteSize) throws IOException {
|
||||||
|
byte[] buffer = new byte[byteSize];
|
||||||
|
int startIndex = 0;
|
||||||
|
int count = 0;
|
||||||
|
while (count >= 0 && startIndex < byteSize) {
|
||||||
|
count = in.read(buffer, startIndex, byteSize - startIndex);
|
||||||
|
startIndex += count;
|
||||||
|
}
|
||||||
|
if (startIndex < byteSize) {
|
||||||
|
throw new DavMailException("EXCEPTION_END_OF_STREAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected final Socket client;
|
protected final Socket client;
|
||||||
|
|
||||||
protected BufferedReader in;
|
protected LineReaderInputStream in;
|
||||||
protected OutputStream os;
|
protected OutputStream os;
|
||||||
// user name and password initialized through connection
|
// user name and password initialized through connection
|
||||||
protected String userName;
|
protected String userName;
|
||||||
@ -70,12 +136,7 @@ public class AbstractConnection extends Thread {
|
|||||||
super(name + '-' + clientSocket.getPort());
|
super(name + '-' + clientSocket.getPort());
|
||||||
this.client = clientSocket;
|
this.client = clientSocket;
|
||||||
try {
|
try {
|
||||||
if (encoding == null) {
|
in = new LineReaderInputStream(client.getInputStream(), encoding);
|
||||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
|
||||||
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
|
|
||||||
} else {
|
|
||||||
in = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8"));
|
|
||||||
}
|
|
||||||
os = new BufferedOutputStream(client.getOutputStream());
|
os = new BufferedOutputStream(client.getOutputStream());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
close();
|
close();
|
||||||
|
@ -91,21 +91,7 @@ public class CaldavConnection extends AbstractConnection {
|
|||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw new DavMailException("EXCEPTION_INVALID_CONTENT_LENGTH", contentLength);
|
throw new DavMailException("EXCEPTION_INVALID_CONTENT_LENGTH", contentLength);
|
||||||
}
|
}
|
||||||
char[] buffer = new char[size];
|
String content = in.readContentAsString(size);
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
int actualSize = in.read(buffer);
|
|
||||||
builder.append(buffer, 0, actualSize);
|
|
||||||
if (actualSize < 0) {
|
|
||||||
throw new DavMailException("EXCEPTION_END_OF_STREAM");
|
|
||||||
}
|
|
||||||
// dirty hack to ensure full content read
|
|
||||||
// TODO : replace with a dedicated reader
|
|
||||||
while (builder.toString().getBytes("UTF-8").length < size) {
|
|
||||||
actualSize = in.read(buffer);
|
|
||||||
builder.append(buffer, 0, actualSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
String content = builder.toString();
|
|
||||||
if (wireLogger.isDebugEnabled()) {
|
if (wireLogger.isDebugEnabled()) {
|
||||||
wireLogger.debug("< " + content);
|
wireLogger.debug("< " + content);
|
||||||
}
|
}
|
||||||
|
89
src/java/davmail/exchange/DoubleDotInputStream.java
Normal file
89
src/java/davmail/exchange/DoubleDotInputStream.java
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
|
||||||
|
* Copyright (C) 2010 Mickael Guessant
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
package davmail.exchange;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PushbackInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace double dot lines with single dot in input stream.
|
||||||
|
* A line with a single dot means end of stream
|
||||||
|
*/
|
||||||
|
public class DoubleDotInputStream extends PushbackInputStream {
|
||||||
|
int[] buffer = new int[4];
|
||||||
|
int index = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public DoubleDotInputStream(InputStream in) {
|
||||||
|
super(in, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push current byte to buffer and read next byte.
|
||||||
|
*
|
||||||
|
* @param currentByte current byte
|
||||||
|
* @return next byte
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
|
protected int readNextByte() throws IOException {
|
||||||
|
int b = super.read();
|
||||||
|
buffer[++index] = b;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
int b = super.read();
|
||||||
|
if (b == '\r') {
|
||||||
|
// \r\n
|
||||||
|
if (readNextByte() == '\n') {
|
||||||
|
// \r\n.
|
||||||
|
if (readNextByte() == '.') {
|
||||||
|
// \r\n.\r
|
||||||
|
if (readNextByte() == '\r') {
|
||||||
|
// \r\n.\r\n
|
||||||
|
if (readNextByte() == '\n') {
|
||||||
|
// end of stream
|
||||||
|
index = -1;
|
||||||
|
b = -1;
|
||||||
|
}
|
||||||
|
// \r\n..
|
||||||
|
} else if (buffer[index] == '.') {
|
||||||
|
// \r\n..\r
|
||||||
|
if ((readNextByte()) == '\r') {
|
||||||
|
// replace double dot
|
||||||
|
buffer[--index] = '\r';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// push back characters
|
||||||
|
if (index >= 0) {
|
||||||
|
while(index >= 0) {
|
||||||
|
unread(buffer[index--]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
59
src/java/davmail/exchange/DoubleDotOutputStream.java
Normal file
59
src/java/davmail/exchange/DoubleDotOutputStream.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
|
||||||
|
* Copyright (C) 2010 Mickael Guessant
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
package davmail.exchange;
|
||||||
|
|
||||||
|
import java.io.FilterOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace single dot lines with double dot.
|
||||||
|
*/
|
||||||
|
public class DoubleDotOutputStream extends FilterOutputStream {
|
||||||
|
enum State {
|
||||||
|
CR, CRLF, CRLFDOT
|
||||||
|
}
|
||||||
|
|
||||||
|
State currentState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public DoubleDotOutputStream(OutputStream out) {
|
||||||
|
super(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
if (currentState == null && b == '\r') {
|
||||||
|
currentState = State.CR;
|
||||||
|
} else if (currentState == State.CR && b == '\n') {
|
||||||
|
currentState = State.CRLF;
|
||||||
|
} else if (currentState == State.CRLF && b == '.') {
|
||||||
|
currentState = State.CRLFDOT;
|
||||||
|
} else if (currentState == State.CRLFDOT && b == '\r') {
|
||||||
|
out.write('.');
|
||||||
|
currentState = null;
|
||||||
|
} else {
|
||||||
|
currentState = null;
|
||||||
|
}
|
||||||
|
out.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -35,7 +35,9 @@ import org.htmlcleaner.CommentToken;
|
|||||||
import org.htmlcleaner.HtmlCleaner;
|
import org.htmlcleaner.HtmlCleaner;
|
||||||
import org.htmlcleaner.TagNode;
|
import org.htmlcleaner.TagNode;
|
||||||
|
|
||||||
|
import javax.mail.Address;
|
||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
import javax.mail.internet.MimeMessage;
|
||||||
import javax.mail.internet.MimeMultipart;
|
import javax.mail.internet.MimeMultipart;
|
||||||
import javax.mail.internet.MimePart;
|
import javax.mail.internet.MimePart;
|
||||||
@ -473,7 +475,7 @@ public abstract class ExchangeSession {
|
|||||||
* @param messageBody mail body
|
* @param messageBody mail body
|
||||||
* @throws IOException when unable to create message
|
* @throws IOException when unable to create message
|
||||||
*/
|
*/
|
||||||
public abstract void createMessage(String folderPath, String messageName, HashMap<String, String> properties, String messageBody) throws IOException;
|
public abstract void createMessage(String folderPath, String messageName, HashMap<String, String> properties, byte[] messageBody) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update given properties on message.
|
* Update given properties on message.
|
||||||
@ -496,11 +498,10 @@ public abstract class ExchangeSession {
|
|||||||
/**
|
/**
|
||||||
* Send message to recipients, properties contains bcc recipients and other non MIME flags.
|
* Send message to recipients, properties contains bcc recipients and other non MIME flags.
|
||||||
*
|
*
|
||||||
* @param properties additional message properties
|
|
||||||
* @param messageBody MIME message body
|
* @param messageBody MIME message body
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
public abstract void sendMessage(HashMap<String, String> properties, String messageBody) throws IOException;
|
public abstract void sendMessage(byte[] messageBody) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create message MIME body reader.
|
* Create message MIME body reader.
|
||||||
@ -906,67 +907,25 @@ public abstract class ExchangeSession {
|
|||||||
* @param reader message stream
|
* @param reader message stream
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
public void sendMessage(List<String> recipients, BufferedReader reader) throws IOException {
|
public void sendMessage(List<String> rcptToRecipients, MimeMessage mimeMessage) throws IOException, MessagingException {
|
||||||
String line = reader.readLine();
|
// Exchange 2007 : skip From: header
|
||||||
StringBuilder mailBuffer = new StringBuilder();
|
mimeMessage.removeHeader("from");
|
||||||
StringBuilder recipientBuffer = new StringBuilder();
|
|
||||||
boolean inHeader = true;
|
|
||||||
boolean inRecipientHeader = false;
|
|
||||||
while (!".".equals(line)) {
|
|
||||||
// Exchange 2007 : skip From: header
|
|
||||||
if ((inHeader && line.length() >= 5)) {
|
|
||||||
String prefix = line.substring(0, 5).toLowerCase();
|
|
||||||
if ("from:".equals(prefix)) {
|
|
||||||
line = reader.readLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inHeader && line.length() == 0) {
|
|
||||||
inHeader = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inRecipientHeader = inRecipientHeader && line.startsWith(" ");
|
|
||||||
|
|
||||||
if ((inHeader && line.length() >= 3) || inRecipientHeader) {
|
|
||||||
String prefix = line.substring(0, 3).toLowerCase();
|
|
||||||
if ("to:".equalsIgnoreCase(prefix) || "cc:".equalsIgnoreCase(prefix) || inRecipientHeader) {
|
|
||||||
inRecipientHeader = true;
|
|
||||||
recipientBuffer.append(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String nextLine = reader.readLine();
|
|
||||||
mailBuffer.append(line);
|
|
||||||
if (!".".equals(nextLine)) {
|
|
||||||
mailBuffer.append((char) 13).append((char) 10);
|
|
||||||
}
|
|
||||||
line = nextLine;
|
|
||||||
}
|
|
||||||
// remove visible recipients from list
|
// remove visible recipients from list
|
||||||
List<String> visibleRecipients = new ArrayList<String>();
|
Set<String> visibleRecipients = new HashSet<String>();
|
||||||
for (String recipient : recipients) {
|
Address[] recipients = mimeMessage.getAllRecipients();
|
||||||
if (recipientBuffer.indexOf(recipient) >= 0) {
|
for (Address address:recipients) {
|
||||||
visibleRecipients.add(recipient);
|
visibleRecipients.add(address.toString());
|
||||||
|
}
|
||||||
|
for (String recipient : rcptToRecipients) {
|
||||||
|
if (!visibleRecipients.contains(recipient)) {
|
||||||
|
mimeMessage.addRecipient(javax.mail.Message.RecipientType.BCC, new InternetAddress(recipient));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recipients.removeAll(visibleRecipients);
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
mimeMessage.writeTo(baos);
|
||||||
|
|
||||||
StringBuilder bccBuffer = new StringBuilder();
|
sendMessage(baos.toByteArray());
|
||||||
for (String recipient : recipients) {
|
|
||||||
if (bccBuffer.length() > 0) {
|
|
||||||
bccBuffer.append(',');
|
|
||||||
}
|
|
||||||
bccBuffer.append('<');
|
|
||||||
bccBuffer.append(recipient);
|
|
||||||
bccBuffer.append('>');
|
|
||||||
}
|
|
||||||
|
|
||||||
String bcc = bccBuffer.toString();
|
|
||||||
HashMap<String, String> properties = new HashMap<String, String>();
|
|
||||||
if (bcc.length() > 0) {
|
|
||||||
properties.put("bcc", bcc);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendMessage(properties, mailBuffer.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1407,7 +1407,7 @@ public class DavExchangeSession extends ExchangeSession {
|
|||||||
* @throws IOException when unable to create message
|
* @throws IOException when unable to create message
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void createMessage(String folderPath, String messageName, HashMap<String, String> properties, String messageBody) throws IOException {
|
public void createMessage(String folderPath, String messageName, HashMap<String, String> properties, byte[] messageBody) throws IOException {
|
||||||
String messageUrl = URIUtil.encodePathQuery(getFolderPath(folderPath) + '/' + messageName + ".EML");
|
String messageUrl = URIUtil.encodePathQuery(getFolderPath(folderPath) + '/' + messageName + ".EML");
|
||||||
PropPatchMethod patchMethod;
|
PropPatchMethod patchMethod;
|
||||||
List<DavConstants> davProperties = buildProperties(properties);
|
List<DavConstants> davProperties = buildProperties(properties);
|
||||||
@ -1434,7 +1434,7 @@ public class DavExchangeSession extends ExchangeSession {
|
|||||||
putmethod.setRequestHeader("Translate", "f");
|
putmethod.setRequestHeader("Translate", "f");
|
||||||
try {
|
try {
|
||||||
// use same encoding as client socket reader
|
// use same encoding as client socket reader
|
||||||
putmethod.setRequestEntity(new ByteArrayRequestEntity(messageBody.getBytes()));
|
putmethod.setRequestEntity(new ByteArrayRequestEntity(messageBody));
|
||||||
int code = httpClient.executeMethod(putmethod);
|
int code = httpClient.executeMethod(putmethod);
|
||||||
|
|
||||||
if (code != HttpStatus.SC_OK && code != HttpStatus.SC_CREATED) {
|
if (code != HttpStatus.SC_OK && code != HttpStatus.SC_CREATED) {
|
||||||
@ -1495,10 +1495,10 @@ public class DavExchangeSession extends ExchangeSession {
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(HashMap<String, String> properties, String messageBody) throws IOException {
|
public void sendMessage(byte[] messageBody) throws IOException {
|
||||||
String messageName = UUID.randomUUID().toString();
|
String messageName = UUID.randomUUID().toString();
|
||||||
|
|
||||||
createMessage("Drafts", messageName, properties, messageBody);
|
createMessage("Drafts", messageName, null, messageBody);
|
||||||
|
|
||||||
String tempUrl = draftsUrl + '/' + messageName + ".EML";
|
String tempUrl = draftsUrl + '/' + messageName + ".EML";
|
||||||
MoveMethod method = new MoveMethod(URIUtil.encodePath(tempUrl), URIUtil.encodePath(sendmsgUrl), true);
|
MoveMethod method = new MoveMethod(URIUtil.encodePath(tempUrl), URIUtil.encodePath(sendmsgUrl), true);
|
||||||
|
@ -162,17 +162,10 @@ public class EwsExchangeSession extends ExchangeSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createMessage(String folderPath, String messageName, HashMap<String, String> properties, String messageBody) throws IOException {
|
public void createMessage(String folderPath, String messageName, HashMap<String, String> properties, byte[] messageBody) throws IOException {
|
||||||
EWSMethod.Item item = new EWSMethod.Item();
|
EWSMethod.Item item = new EWSMethod.Item();
|
||||||
item.type = "Message";
|
item.type = "Message";
|
||||||
String bcc = properties.get("bcc");
|
item.mimeContent = Base64.encodeBase64(messageBody);
|
||||||
if (bcc != null) {
|
|
||||||
properties.remove("bcc");
|
|
||||||
// put bcc header back into mime body, Exchange will handle it on send
|
|
||||||
item.mimeContent = Base64.encodeBase64(("bcc: "+bcc+ "\r\n" +messageBody).getBytes());
|
|
||||||
} else {
|
|
||||||
item.mimeContent = Base64.encodeBase64(messageBody.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<FieldUpdate> fieldUpdates = buildProperties(properties);
|
Set<FieldUpdate> fieldUpdates = buildProperties(properties);
|
||||||
if (!properties.containsKey("draft")) {
|
if (!properties.containsKey("draft")) {
|
||||||
@ -204,23 +197,13 @@ public class EwsExchangeSession extends ExchangeSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(HashMap<String, String> properties, String messageBody) throws IOException {
|
public void sendMessage(byte[] messageBody) throws IOException {
|
||||||
EWSMethod.Item item = new EWSMethod.Item();
|
EWSMethod.Item item = new EWSMethod.Item();
|
||||||
item.type = "Message";
|
item.type = "Message";
|
||||||
String bcc = properties.get("bcc");
|
item.mimeContent = Base64.encodeBase64(messageBody);
|
||||||
if (bcc != null) {
|
|
||||||
properties.remove("bcc");
|
|
||||||
// put bcc header back into mime body, Exchange will handle it on send
|
|
||||||
item.mimeContent = Base64.encodeBase64(("bcc: "+bcc+ "\r\n" +messageBody).getBytes());
|
|
||||||
} else {
|
|
||||||
item.mimeContent = Base64.encodeBase64(messageBody.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<FieldUpdate> fieldUpdates = buildProperties(properties);
|
|
||||||
item.setFieldUpdates(fieldUpdates);
|
|
||||||
CreateItemMethod createItemMethod = new CreateItemMethod(MessageDisposition.SendAndSaveCopy, getFolderId(SENT), item);
|
CreateItemMethod createItemMethod = new CreateItemMethod(MessageDisposition.SendAndSaveCopy, getFolderId(SENT), item);
|
||||||
executeMethod(createItemMethod);
|
executeMethod(createItemMethod);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -419,20 +419,12 @@ public class ImapConnection extends AbstractConnection {
|
|||||||
}
|
}
|
||||||
int size = Integer.parseInt(nextToken);
|
int size = Integer.parseInt(nextToken);
|
||||||
sendClient("+ send literal data");
|
sendClient("+ send literal data");
|
||||||
char[] buffer = new char[size];
|
byte[] buffer = in.readContent(size);
|
||||||
int index = 0;
|
|
||||||
int count = 0;
|
|
||||||
while (count >= 0 && index < size) {
|
|
||||||
count = in.read(buffer, index, size - index);
|
|
||||||
if (count >= 0) {
|
|
||||||
index += count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// empty line
|
// empty line
|
||||||
readClient();
|
readClient();
|
||||||
|
|
||||||
String messageName = UUID.randomUUID().toString();
|
String messageName = UUID.randomUUID().toString();
|
||||||
session.createMessage(folderName, messageName, properties, new String(buffer));
|
session.createMessage(folderName, messageName, properties, buffer);
|
||||||
sendClient(commandId + " OK APPEND completed");
|
sendClient(commandId + " OK APPEND completed");
|
||||||
} else if ("idle".equalsIgnoreCase(command) && imapIdleDelay > 0) {
|
} else if ("idle".equalsIgnoreCase(command) && imapIdleDelay > 0) {
|
||||||
sendClient("+ idling ");
|
sendClient("+ idling ");
|
||||||
@ -441,7 +433,7 @@ public class ImapConnection extends AbstractConnection {
|
|||||||
DavGatewayTray.resetIcon();
|
DavGatewayTray.resetIcon();
|
||||||
try {
|
try {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while (!in.ready()) {
|
while (in.available() == 0) {
|
||||||
if (++count >= imapIdleDelay) {
|
if (++count >= imapIdleDelay) {
|
||||||
count = 0;
|
count = 0;
|
||||||
List<Long> previousImapUidList = currentFolder.getImapUidList();
|
List<Long> previousImapUidList = currentFolder.getImapUidList();
|
||||||
|
@ -21,18 +21,22 @@ package davmail.smtp;
|
|||||||
import davmail.AbstractConnection;
|
import davmail.AbstractConnection;
|
||||||
import davmail.BundleMessage;
|
import davmail.BundleMessage;
|
||||||
import davmail.exception.DavMailException;
|
import davmail.exception.DavMailException;
|
||||||
|
import davmail.exchange.DoubleDotInputStream;
|
||||||
import davmail.exchange.ExchangeSessionFactory;
|
import davmail.exchange.ExchangeSessionFactory;
|
||||||
import davmail.ui.tray.DavGatewayTray;
|
import davmail.ui.tray.DavGatewayTray;
|
||||||
|
|
||||||
import javax.mail.internet.InternetAddress;
|
|
||||||
import javax.mail.internet.AddressException;
|
import javax.mail.internet.AddressException;
|
||||||
|
import javax.mail.internet.InternetAddress;
|
||||||
|
import javax.mail.internet.MimeMessage;
|
||||||
|
import javax.mail.util.SharedByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dav Gateway smtp connection implementation
|
* Dav Gateway smtp connection implementation
|
||||||
@ -48,6 +52,7 @@ public class SmtpConnection extends AbstractConnection {
|
|||||||
super(SmtpConnection.class.getSimpleName(), clientSocket, null);
|
super(SmtpConnection.class.getSimpleName(), clientSocket, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
String line;
|
String line;
|
||||||
@ -146,7 +151,15 @@ public class SmtpConnection extends AbstractConnection {
|
|||||||
sendClient("354 Start mail input; end with <CRLF>.<CRLF>");
|
sendClient("354 Start mail input; end with <CRLF>.<CRLF>");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
session.sendMessage(recipients, in);
|
// read message in buffer
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
DoubleDotInputStream doubleDotInputStream = new DoubleDotInputStream(in);
|
||||||
|
int b;
|
||||||
|
while ((b = doubleDotInputStream.read()) >= 0) {
|
||||||
|
baos.write(b);
|
||||||
|
}
|
||||||
|
MimeMessage mimeMessage = new MimeMessage(null, new SharedByteArrayInputStream(baos.toByteArray()));
|
||||||
|
session.sendMessage(recipients, mimeMessage);
|
||||||
state = State.AUTHENTICATED;
|
state = State.AUTHENTICATED;
|
||||||
sendClient("250 Queued mail for delivery");
|
sendClient("250 Queued mail for delivery");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -59,11 +59,10 @@ public class AbstractExchangeSessionTestCase extends AbstractDavMailTestCase {
|
|||||||
return mimeMessage;
|
return mimeMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getMimeBody(MimeMessage mimeMessage) throws IOException, MessagingException {
|
protected byte[] getMimeBody(MimeMessage mimeMessage) throws IOException, MessagingException {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
mimeMessage.writeTo(baos);
|
mimeMessage.writeTo(baos);
|
||||||
byte[] content = baos.toByteArray();
|
return baos.toByteArray();
|
||||||
return new String(content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
67
src/test/davmail/exchange/TestDoubleDotInputStream.java
Normal file
67
src/test/davmail/exchange/TestDoubleDotInputStream.java
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
|
||||||
|
* Copyright (C) 2010 Mickael Guessant
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
package davmail.exchange;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test double dot input stream.
|
||||||
|
*/
|
||||||
|
public class TestDoubleDotInputStream extends TestCase {
|
||||||
|
static final String END_OF_STREAM = "\r\n.\r\n";
|
||||||
|
|
||||||
|
protected String doubleDotRead(String value) throws IOException {
|
||||||
|
DoubleDotInputStream doubleDotInputStream = new DoubleDotInputStream(new ByteArrayInputStream(value.getBytes()));
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
int b;
|
||||||
|
while ((b = doubleDotInputStream.read()) != -1) {
|
||||||
|
baos.write(b);
|
||||||
|
}
|
||||||
|
return new String(baos.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSimple() throws IOException {
|
||||||
|
String value = "simple test";
|
||||||
|
assertEquals(value, doubleDotRead(value + END_OF_STREAM));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNoEof() throws IOException {
|
||||||
|
String value = "simple test";
|
||||||
|
assertEquals(value, doubleDotRead(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiLine() throws IOException {
|
||||||
|
String value = "simple test\r\nsecond line";
|
||||||
|
assertEquals(value, doubleDotRead(value+END_OF_STREAM));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDoubleDot() throws IOException {
|
||||||
|
String value = "simple test\r\n..\r\nsecond line";
|
||||||
|
assertEquals(value.replaceAll("\\.\\.", "."), doubleDotRead(value+END_OF_STREAM));
|
||||||
|
}
|
||||||
|
public void testDoubleDotEnd() throws IOException {
|
||||||
|
String value = "simple test\r\n..";
|
||||||
|
assertEquals(value.replaceAll("\\.\\.", "."), doubleDotRead(value+END_OF_STREAM));
|
||||||
|
assertEquals("..", doubleDotRead(".."+END_OF_STREAM));
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ package davmail.smtp;
|
|||||||
import davmail.AbstractDavMailTestCase;
|
import davmail.AbstractDavMailTestCase;
|
||||||
import davmail.DavGateway;
|
import davmail.DavGateway;
|
||||||
import davmail.Settings;
|
import davmail.Settings;
|
||||||
|
import davmail.exchange.DoubleDotOutputStream;
|
||||||
import davmail.exchange.ExchangeSession;
|
import davmail.exchange.ExchangeSession;
|
||||||
import davmail.exchange.ExchangeSessionFactory;
|
import davmail.exchange.ExchangeSessionFactory;
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
@ -78,14 +79,6 @@ public class TestSmtp extends AbstractDavMailTestCase {
|
|||||||
return new String(baos.toByteArray(), "ASCII");
|
return new String(baos.toByteArray(), "ASCII");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String readFullAnswer(String prefix) throws IOException {
|
|
||||||
String line = readLine();
|
|
||||||
while (!line.startsWith(prefix)) {
|
|
||||||
line = readLine();
|
|
||||||
}
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUp() throws IOException {
|
public void setUp() throws IOException {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
@ -95,43 +88,26 @@ public class TestSmtp extends AbstractDavMailTestCase {
|
|||||||
clientSocket = new Socket("localhost", Settings.getIntProperty("davmail.smtpPort"));
|
clientSocket = new Socket("localhost", Settings.getIntProperty("davmail.smtpPort"));
|
||||||
socketOutputStream = new BufferedOutputStream(clientSocket.getOutputStream());
|
socketOutputStream = new BufferedOutputStream(clientSocket.getOutputStream());
|
||||||
socketInputStream = new BufferedInputStream(clientSocket.getInputStream());
|
socketInputStream = new BufferedInputStream(clientSocket.getInputStream());
|
||||||
|
|
||||||
|
String banner = readLine();
|
||||||
|
assertNotNull(banner);
|
||||||
|
String credentials = (char) 0+Settings.getProperty("davmail.username")+ (char) 0 +Settings.getProperty("davmail.password");
|
||||||
|
writeLine("AUTH PLAIN " + new String(new Base64().encode(credentials.getBytes())));
|
||||||
|
assertEquals("235 OK Authenticated", readLine());
|
||||||
}
|
}
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
session = ExchangeSessionFactory.getInstance(Settings.getProperty("davmail.username"), Settings.getProperty("davmail.password"));
|
session = ExchangeSessionFactory.getInstance(Settings.getProperty("davmail.username"), Settings.getProperty("davmail.password"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testBanner() throws IOException {
|
public void sendAndCheckMessage(MimeMessage mimeMessage) throws IOException, MessagingException, InterruptedException {
|
||||||
String banner = readLine();
|
|
||||||
assertNotNull(banner);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testLogin() throws IOException {
|
|
||||||
String credentials = (char) 0+Settings.getProperty("davmail.username")+ (char) 0 +Settings.getProperty("davmail.password");
|
|
||||||
writeLine("AUTH PLAIN " + new String(new Base64().encode(credentials.getBytes())));
|
|
||||||
assertEquals("235 OK Authenticated", readLine());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void testSendMessage() throws IOException, MessagingException, InterruptedException {
|
|
||||||
String body = "Test message\r\n" +
|
|
||||||
"Special characters: éèçà\r\n" +
|
|
||||||
"Chinese: "+((char)0x604F)+((char)0x7D59);
|
|
||||||
MimeMessage mimeMessage = new MimeMessage((Session) null);
|
|
||||||
mimeMessage.addHeader("To", Settings.getProperty("davmail.to"));
|
|
||||||
mimeMessage.setText(body, "UTF-8");
|
|
||||||
//mimeMessage.setHeader("Content-Transfer-Encoding", "8bit");
|
|
||||||
mimeMessage.setSubject("Test subject");
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
mimeMessage.writeTo(baos);
|
|
||||||
byte[] content = baos.toByteArray();
|
|
||||||
writeLine("MAIL FROM:"+session.getEmail());
|
writeLine("MAIL FROM:"+session.getEmail());
|
||||||
readLine();
|
readLine();
|
||||||
writeLine("RCPT TO:"+Settings.getProperty("davmail.to"));
|
writeLine("RCPT TO:"+Settings.getProperty("davmail.to"));
|
||||||
readLine();
|
readLine();
|
||||||
writeLine("DATA");
|
writeLine("DATA");
|
||||||
assertEquals("354 Start mail input; end with <CRLF>.<CRLF>", readLine());
|
assertEquals("354 Start mail input; end with <CRLF>.<CRLF>", readLine());
|
||||||
mimeMessage.writeTo(socketOutputStream);
|
mimeMessage.writeTo(new DoubleDotOutputStream(socketOutputStream));
|
||||||
writeLine("");
|
writeLine("");
|
||||||
writeLine(".");
|
writeLine(".");
|
||||||
assertEquals("250 Queued mail for delivery", readLine());
|
assertEquals("250 Queued mail for delivery", readLine());
|
||||||
@ -141,29 +117,35 @@ public class TestSmtp extends AbstractDavMailTestCase {
|
|||||||
assertEquals(1, messages.size());
|
assertEquals(1, messages.size());
|
||||||
ExchangeSession.Message message = messages.get(0);
|
ExchangeSession.Message message = messages.get(0);
|
||||||
message.getMimeMessage().writeTo(System.out);
|
message.getMimeMessage().writeTo(System.out);
|
||||||
assertEquals(body, (String) message.getMimeMessage().getDataHandler().getContent());
|
assertEquals(mimeMessage.getDataHandler().getContent(), (String) message.getMimeMessage().getDataHandler().getContent());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testBccMessage() throws IOException, MessagingException {
|
public void testSendMessage() throws IOException, MessagingException, InterruptedException {
|
||||||
|
String body = "Test message\r\n" +
|
||||||
|
"Special characters: éèçà\r\n" +
|
||||||
|
"Chinese: "+((char)0x604F)+((char)0x7D59);
|
||||||
|
MimeMessage mimeMessage = new MimeMessage((Session) null);
|
||||||
|
mimeMessage.addHeader("To", Settings.getProperty("davmail.to"));
|
||||||
|
mimeMessage.setSubject("Test subject");
|
||||||
|
mimeMessage.setText(body, "UTF-8");
|
||||||
|
sendAndCheckMessage(mimeMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBccMessage() throws IOException, MessagingException, InterruptedException {
|
||||||
MimeMessage mimeMessage = new MimeMessage((Session) null);
|
MimeMessage mimeMessage = new MimeMessage((Session) null);
|
||||||
mimeMessage.addHeader("to", Settings.getProperty("davmail.to"));
|
mimeMessage.addHeader("to", Settings.getProperty("davmail.to"));
|
||||||
mimeMessage.setText("Test message");
|
|
||||||
mimeMessage.setSubject("Test subject");
|
mimeMessage.setSubject("Test subject");
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
mimeMessage.setText("Test message");
|
||||||
mimeMessage.writeTo(baos);
|
sendAndCheckMessage(mimeMessage);
|
||||||
byte[] content = baos.toByteArray();
|
}
|
||||||
writeLine("MAIL FROM:"+session.getEmail());
|
|
||||||
readLine();
|
public void testDotMessage() throws IOException, MessagingException, InterruptedException {
|
||||||
writeLine("RCPT TO:"+Settings.getProperty("davmail.to"));
|
String body = "First line\r\n.\r\nSecond line";
|
||||||
readLine();
|
MimeMessage mimeMessage = new MimeMessage((Session) null);
|
||||||
writeLine("RCPT TO:"+Settings.getProperty("davmail.bcc"));
|
mimeMessage.addHeader("to", Settings.getProperty("davmail.to"));
|
||||||
readLine();
|
mimeMessage.setSubject("Test subject");
|
||||||
writeLine("DATA");
|
mimeMessage.setText(body);
|
||||||
assertEquals("354 Start mail input; end with <CRLF>.<CRLF>", readLine());
|
sendAndCheckMessage(mimeMessage);
|
||||||
writeLine(new String(content));
|
|
||||||
writeLine(".");
|
|
||||||
assertEquals("250 Queued mail for delivery", readLine());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testQuit() throws IOException {
|
public void testQuit() throws IOException {
|
||||||
|
Loading…
Reference in New Issue
Block a user