diff --git a/build.xml b/build.xml
index 545d59e7f..6b9328d54 100644
--- a/build.xml
+++ b/build.xml
@@ -1255,7 +1255,7 @@ javax.swing.event, javax.swing.border"/>
manifest="${src}/net/java/sip/communicator/impl/protocol/msn/msn.provider.manifest.mf">
-
+
@@ -1270,7 +1270,7 @@ javax.swing.event, javax.swing.border"/>
manifest="${testsrc}/net/java/sip/communicator/slick/protocol/msn/msn.provider.slick.manifest.mf">
-
+
diff --git a/lib/bundle/org.apache.felix.servicebinder-0.8.0-SNAPSHOT.jar b/lib/bundle/org.apache.felix.servicebinder-0.8.0-SNAPSHOT.jar
deleted file mode 100644
index f071d23f1..000000000
Binary files a/lib/bundle/org.apache.felix.servicebinder-0.8.0-SNAPSHOT.jar and /dev/null differ
diff --git a/lib/installer-exclude/jml-1.0b1.jar b/lib/installer-exclude/jml-1.0b1.jar
deleted file mode 100644
index 4c64491e9..000000000
Binary files a/lib/installer-exclude/jml-1.0b1.jar and /dev/null differ
diff --git a/lib/installer-exclude/jml-1.0b2.jar b/lib/installer-exclude/jml-1.0b2.jar
new file mode 100644
index 000000000..068b1b9d4
Binary files /dev/null and b/lib/installer-exclude/jml-1.0b2.jar differ
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/ContactMsnImpl.java b/src/net/java/sip/communicator/impl/protocol/msn/ContactMsnImpl.java
index 7bf917739..99b946fd7 100644
--- a/src/net/java/sip/communicator/impl/protocol/msn/ContactMsnImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/msn/ContactMsnImpl.java
@@ -72,8 +72,19 @@ public boolean isLocal()
public byte[] getImage()
{
+ if(image == null)
+ ssclCallback.addContactForImageUpdate(this);
+
return image;
}
+
+ /**
+ * Set the image of the contact
+ */
+ void setImage(byte[] imgBytes)
+ {
+ this.image = imgBytes;
+ }
/**
* Returns a hashCode for this contact. The returned hashcode is actually
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/EventManager.java b/src/net/java/sip/communicator/impl/protocol/msn/EventManager.java
index e3a98657d..d977b618c 100644
--- a/src/net/java/sip/communicator/impl/protocol/msn/EventManager.java
+++ b/src/net/java/sip/communicator/impl/protocol/msn/EventManager.java
@@ -81,7 +81,7 @@ public void removeModificationListener(MsnContactListEventListener listener)
* @param message Message
* @throws Exception
*/
- public void messageSent(SocketSession session, Message message) throws Exception
+ public void messageSent(Session session, Message message) throws Exception
{
logger.trace(msnMessenger.getOwner().getEmail().getEmailAddress() +
" outgoing " + message);
@@ -93,7 +93,7 @@ public void messageSent(SocketSession session, Message message) throws Exception
* @param message Message
* @throws Exception
*/
- public void messageReceived(SocketSession session, Message message)
+ public void messageReceived(Session session, Message message)
throws Exception
{
MsnIncomingMessage incoming = (MsnIncomingMessage)((WrapperMessage)message)
@@ -220,7 +220,7 @@ else if(incoming instanceof IncomingQNG)
private boolean connected = false;
private Timer connectionTimer = new Timer();
- public void sessionTimeout(SocketSession socketSession) throws Exception
+ public void sessionTimeout(Session socketSession) throws Exception
{
connectionTimer.schedule(new TimerTask()
{
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java b/src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java
index a78244041..7037893f8 100644
--- a/src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java
@@ -7,6 +7,7 @@
package net.java.sip.communicator.impl.protocol.msn;
import java.util.*;
+import java.text.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
@@ -15,6 +16,7 @@
import net.sf.jml.*;
import net.sf.jml.event.*;
import net.sf.jml.message.*;
+import net.java.sip.communicator.impl.protocol.msn.mail.utils.*;
/**
* A straightforward implementation of the basic instant messaging operation
@@ -244,6 +246,8 @@ public void registrationStateChanged(RegistrationStateChangeEvent evt)
msnProvider.getMessenger().
addMessageListener(new MsnMessageListener());
+ msnProvider.getMessenger().
+ addEmailListener(new MsnMessageListener());
}
}
}
@@ -284,6 +288,7 @@ else if (evt instanceof MessageDeliveryFailedEvent)
private class MsnMessageListener
extends MsnMessageAdapter
+ implements MsnEmailListener
{
public void instantMessageReceived(MsnSwitchboard switchboard,
MsnInstantMessage message,
@@ -308,5 +313,74 @@ public void instantMessageReceived(MsnSwitchboard switchboard,
fireMessageEvent(msgReceivedEvt);
}
- }
+
+ public void initialEmailNotificationReceived(MsnSwitchboard switchboard,
+ MsnEmailInitMessage message,
+ MsnContact contact)
+ {
+ }
+
+ public void initialEmailDataReceived(MsnSwitchboard switchboard,
+ MsnEmailInitEmailData message,
+ MsnContact contact)
+ {
+ }
+
+ public void newEmailNotificationReceived(MsnSwitchboard switchboard,
+ MsnEmailNotifyMessage message,
+ MsnContact contact)
+ {
+ // we don't process incoming event without email.
+ if ((message.getFromAddr() == null)
+ || (message.getFromAddr().indexOf('@') < 0))
+ {
+ return;
+ }
+
+ String subject = message.getSubject();
+
+ try
+ {
+ subject = MimeUtility.decodeText(subject);
+ }
+ catch (Exception exception)
+ {
+ exception.printStackTrace();
+ }
+
+ Message newMailMessage = new MessageMsnImpl(
+ MessageFormat.format(Resources.getString("newMail"),
+ new Object[]{message.getFrom(),
+ message.getFromAddr(),
+ subject}),
+ DEFAULT_MIME_TYPE,
+ DEFAULT_MIME_ENCODING,
+ subject);
+
+ Contact sourceContact = opSetPersPresence.
+ findContactByID(message.getFromAddr());
+
+ if (sourceContact == null)
+ {
+ logger.debug("received a new mail from an unknown contact: "
+ + message.getFrom()
+ + " <" + message.getFromAddr() + ">");
+ //create the volatile contact
+ sourceContact = opSetPersPresence
+ .createVolatileContact(message.getFromAddr());
+ }
+ MessageReceivedEvent msgReceivedEvt
+ = new MessageReceivedEvent(
+ newMailMessage, sourceContact, new Date(),
+ MessageReceivedEvent.SYSTEM_MESSAGE_RECEIVED);
+
+ fireMessageEvent(msgReceivedEvt);
+ }
+
+ public void activityEmailNotificationReceived(MsnSwitchboard switchboard,
+ MsnEmailActivityMessage message,
+ MsnContact contact)
+ {
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/OperationSetPersistentPresenceMsnImpl.java b/src/net/java/sip/communicator/impl/protocol/msn/OperationSetPersistentPresenceMsnImpl.java
index 8cfbfd3aa..5afd80e7e 100644
--- a/src/net/java/sip/communicator/impl/protocol/msn/OperationSetPersistentPresenceMsnImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/msn/OperationSetPersistentPresenceMsnImpl.java
@@ -951,6 +951,46 @@ private void fireContactPresenceStatusChangeEvent(
listener.contactPresenceStatusChanged(evt);
}
}
+
+ /**
+ * Notify all subscription listeners of the corresponding contact property
+ * change event.
+ *
+ * @param eventID the String ID of the event to dispatch
+ * @param sourceContact the ContactJabberImpl instance that this event is
+ * pertaining to.
+ * @param oldValue the value that the changed property had before the change
+ * occurred.
+ * @param newValue the value that the changed property currently has (after
+ * the change has occurred).
+ */
+ void fireContactPropertyChangeEvent( String eventID,
+ ContactMsnImpl sourceContact,
+ Object oldValue,
+ Object newValue)
+ {
+ ContactPropertyChangeEvent evt =
+ new ContactPropertyChangeEvent(sourceContact, eventID
+ , oldValue, newValue);
+
+ logger.debug("Dispatching a Contact Property Change Event to"
+ +subscriptionListeners.size() + " listeners. Evt="+evt);
+
+ Iterator listeners = null;
+
+ synchronized (subscriptionListeners)
+ {
+ listeners = new ArrayList(subscriptionListeners).iterator();
+ }
+
+ while (listeners.hasNext())
+ {
+ SubscriptionListener listener
+ = (SubscriptionListener) listeners.next();
+
+ listener.contactModified(evt);
+ }
+ }
/**
* Sets the messenger instance impl of the lib
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/Resources.java b/src/net/java/sip/communicator/impl/protocol/msn/Resources.java
new file mode 100644
index 000000000..7077827b4
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/msn/Resources.java
@@ -0,0 +1,60 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.sip.communicator.impl.protocol.msn;
+
+import java.io.*;
+import java.util.*;
+
+import net.java.sip.communicator.util.*;
+
+/**
+ * The Resources class manages the access to the internationalization
+ * properties files.
+ *
+ * @author Yana Stamcheva
+ */
+public class Resources
+{
+
+ /**
+ * Logger for this class.
+ */
+ private static Logger log = Logger.getLogger(Resources.class);
+
+ /**
+ * Name of the bundle were we will search for localized string.
+ */
+ private static final String BUNDLE_NAME
+ = "net.java.sip.communicator.impl.protocol.msn.resources";
+
+ /**
+ * Bundle which handle access to localized resources.
+ */
+ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
+ .getBundle(BUNDLE_NAME);
+
+ /**
+ * Returns an internationalized string corresponding to the given key.
+ *
+ * @param key The key of the string.
+ *
+ * @return An internationalized string corresponding to the given key.
+ */
+ public static String getString(String key)
+ {
+ try
+ {
+ return RESOURCE_BUNDLE.getString(key);
+
+ }
+ catch (MissingResourceException e) {
+
+ return '!' + key + '!';
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/ServerStoredContactListMsnImpl.java b/src/net/java/sip/communicator/impl/protocol/msn/ServerStoredContactListMsnImpl.java
index 13f2b7efa..8b55e7913 100644
--- a/src/net/java/sip/communicator/impl/protocol/msn/ServerStoredContactListMsnImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/msn/ServerStoredContactListMsnImpl.java
@@ -14,6 +14,7 @@
import net.sf.jml.*;
import net.sf.jml.event.*;
import net.sf.jml.impl.*;
+import net.sf.jml.message.p2p.*;
/**
* This class encapsulates the Roster class. Once created, it will
@@ -1202,6 +1203,25 @@ void setMessenger(MsnMessenger messenger)
messenger.addContactListListener(new ContactListListener());
}
+
+ /**
+ * when there is no image for contact we must retreive it
+ * add contacts for image update
+ *
+ * @param c ContactJabberImpl
+ */
+ protected void addContactForImageUpdate(ContactMsnImpl c)
+ {
+ // Get the MSnObject
+ MsnObject avatar = c.getSourceContact().getAvatar();
+
+ if (avatar != null)
+ {
+ messenger.retrieveDisplayPicture(
+ avatar,
+ new ImageUpdater(c));
+ }
+ }
/**
* used for debuging. Printing the serverside lists that msn supports
@@ -1263,4 +1283,32 @@ public void printList()
}
logger.info("---=End Printing contact list=---");
}
+
+ private class ImageUpdater
+ implements DisplayPictureListener
+ {
+ private ContactMsnImpl contact;
+ ImageUpdater(ContactMsnImpl contact)
+ {
+ this.contact = contact;
+ }
+
+ public void notifyMsnObjectRetrieval(MsnMessenger arg0messenger,
+ DisplayPictureRetrieveWorker worker,
+ MsnObject msnObject,
+ ResultStatus result,
+ byte[] resultBytes,
+ Object context)
+ {
+ if (result == ResultStatus.GOOD)
+ {
+ contact.setImage(resultBytes);
+
+ parentOperationSet.fireContactPropertyChangeEvent(
+ ContactPropertyChangeEvent.PROPERTY_IMAGE,
+ contact, null, resultBytes);
+ }
+ }
+
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/VolatileContact.java b/src/net/java/sip/communicator/impl/protocol/msn/VolatileContact.java
index 31219eec7..19f7596e2 100644
--- a/src/net/java/sip/communicator/impl/protocol/msn/VolatileContact.java
+++ b/src/net/java/sip/communicator/impl/protocol/msn/VolatileContact.java
@@ -56,4 +56,6 @@ public String getFriendlyName()
public MsnUserStatus getOldStatus(){return null;}
public String getPersonalMessage(){return "";}
+
+ public MsnObject getAvatar(){return null;}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/Base64InputStream.java b/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/Base64InputStream.java
new file mode 100644
index 000000000..785ff057c
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/Base64InputStream.java
@@ -0,0 +1,184 @@
+/*
+ * Base64InputStream.java
+ * Copyright(C) 2002 The Free Software Foundation
+ *
+ * This file is part of GNU JavaMail, a library.
+ *
+ * GNU JavaMail 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.
+ *
+ * GNU JavaMail 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * As a special exception, if you link this library with other files to
+ * produce an executable, this library does not by itself cause the
+ * resulting executable to be covered by the GNU General Public License.
+ * This exception does not however invalidate any other reasons why the
+ * executable file might be covered by the GNU General Public License.
+ */
+
+package net.java.sip.communicator.impl.protocol.msn.mail.utils;
+
+import java.io.*;
+
+/**
+ * A Base64 content transfer encoding filter stream.
+ *
+ * From RFC 2045, section 6.8:
+ *
+ * The Base64 Content-Transfer-Encoding is designed to represent
+ * arbitrary sequences of octets in a form that need not be humanly
+ * readable. The encoding and decoding algorithms are simple, but the
+ * encoded data are consistently only about 33 percent larger than the
+ * unencoded data.
+ *
+ * @author Chris Burdess
+ */
+public class Base64InputStream
+ extends FilterInputStream
+{
+
+ private byte[] buffer;
+ private int buflen;
+ private int index;
+ private byte[] decodeBuf;
+
+ private static final char[] src =
+ {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '+', '/'
+ };
+ private static final byte[] dst;
+
+ private static final int LF = 10, CR = 13, EQ = 61;
+
+ static
+ {
+ dst = new byte[256];
+ for (int i = 0; i<255; i++)
+ dst[i] = -1;
+ for (int i = 0; i=buflen)
+ {
+ decode();
+ if (buflen==0)
+ return -1;
+ index = 0;
+ }
+ return buffer[index++] & 0xff;
+ }
+
+ /**
+ * Reads up to len bytes of data from the input stream into an array of
+ * bytes.
+ */
+ public int read(byte[] b, int off, int len)
+ throws IOException
+ {
+ try
+ {
+ int l = 0;
+ for (; l>>4 & 0x3);
+ if (decodeBuf[2]!=EQ)
+ {
+ b0 = b2;
+ b2 = dst[decodeBuf[2] & 0xff];
+ buffer[buflen++] = (byte)(b0<<4 & 0xf0 | b2>>>2 & 0xf);
+ if (decodeBuf[3]!=EQ)
+ {
+ b0 = b2;
+ b2 = dst[decodeBuf[3] & 0xff];
+ buffer[buflen++] = (byte)(b0<<6 & 0xc0 | b2 & 0x3f);
+ }
+ }
+ }
+
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/MimeUtility.java b/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/MimeUtility.java
new file mode 100644
index 000000000..71066dc46
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/MimeUtility.java
@@ -0,0 +1,193 @@
+/*
+ * MimeUtility.java
+ * Copyright (C) 2002, 2004, 2005 The Free Software Foundation
+ *
+ * This file is part of GNU JavaMail, a library.
+ *
+ * GNU JavaMail 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.
+ *
+ * GNU JavaMail 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * As a special exception, if you link this library with other files to
+ * produce an executable, this library does not by itself cause the
+ * resulting executable to be covered by the GNU General Public License.
+ * This exception does not however invalidate any other reasons why the
+ * executable file might be covered by the GNU General Public License.
+ */
+
+package net.java.sip.communicator.impl.protocol.msn.mail.utils;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.StringTokenizer;
+
+/**
+ * This is a utility class providing micellaneous MIME-related functionality.
+ *
+ * @author Chris Burdess
+ * @version 1.4
+ */
+public class MimeUtility
+{
+
+ /*
+ * Uninstantiable.
+ */
+ private MimeUtility()
+ {
+ }
+
+ /**
+ * Decodes headers that are defined as '*text' in RFC 822.
+ * @param etext the possibly encoded value
+ * @exception UnsupportedEncodingException if the charset conversion failed
+ */
+ public static String decodeText(String etext)
+ throws UnsupportedEncodingException
+ {
+ String delimiters = "\t\n\r ";
+ if (etext.indexOf("=?") == -1)
+ {
+ return etext;
+ }
+ StringTokenizer st = new StringTokenizer(etext, delimiters, true);
+ StringBuffer buffer = new StringBuffer();
+ StringBuffer extra = new StringBuffer();
+ boolean decoded = false;
+ while (st.hasMoreTokens())
+ {
+ String token = st.nextToken();
+ char c = token.charAt(0);
+ if (delimiters.indexOf(c) > -1)
+ {
+ extra.append(c);
+ }
+ else
+ {
+ try
+ {
+ token = decodeWord(token);
+ if (!decoded && extra.length() > 0)
+ {
+ buffer.append(extra);
+ }
+ decoded = true;
+ }
+ catch (Exception e)
+ {
+ if (extra.length() > 0)
+ {
+ buffer.append(extra);
+ }
+ decoded = false;
+ }
+ buffer.append(token);
+ extra.setLength(0);
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Decodes the specified string using the RFC 2047 rules for parsing an
+ * "encoded-word".
+ * @param eword the possibly encoded value
+ * @exception Exception if the string is not an encoded-word
+ * @exception UnsupportedEncodingException if the decoding failed
+ */
+ public static String decodeWord(String text)
+ throws Exception, UnsupportedEncodingException
+ {
+ if (!text.startsWith("=?"))
+ {
+ throw new Exception();
+ }
+ int start = 2;
+ int end = text.indexOf('?', start);
+ if (end < 0)
+ {
+ throw new Exception();
+ }
+ String charset = text.substring(start, end);
+ // Allow for RFC2231 language
+ int si = charset.indexOf('*');
+ if (si != -1)
+ {
+ charset = charset.substring(0, si);
+ }
+
+ start = end + 1;
+ end = text.indexOf('?', start);
+ if (end < 0)
+ {
+ throw new Exception();
+ }
+ String encoding = text.substring(start, end);
+ start = end + 1;
+ end = text.indexOf("?=", start);
+ if (end < 0)
+ {
+ throw new Exception();
+ }
+ text = text.substring(start, end);
+ try
+ {
+ // The characters in the remaining string must all be 7-bit clean.
+ // Therefore it is safe just to copy them verbatim into a byte array.
+ char[] chars = text.toCharArray();
+ int len = chars.length;
+ byte[] bytes = new byte[len];
+ for (int i = 0; i < len; i++)
+ {
+ bytes[i] = (byte) chars[i];
+ }
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+ InputStream is;
+ if (encoding.equalsIgnoreCase("B"))
+ {
+ is = new Base64InputStream(bis);
+ }
+ else if (encoding.equalsIgnoreCase("Q"))
+ {
+ is = new QInputStream(bis);
+ }
+ else
+ {
+ throw new UnsupportedEncodingException("Unknown encoding: " +
+ encoding);
+ }
+ len = bis.available();
+ bytes = new byte[len];
+ len = is.read(bytes, 0, len);
+ String ret = new String(bytes, 0, len, charset);
+ if (text.length() > end + 2)
+ {
+ String extra = text.substring(end + 2);
+
+ ret = ret + extra;
+ }
+ return ret;
+ }
+ catch (IOException e)
+ {
+ throw new Exception();
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new UnsupportedEncodingException();
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/QInputStream.java b/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/QInputStream.java
new file mode 100644
index 000000000..c8568020e
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/QInputStream.java
@@ -0,0 +1,104 @@
+/*
+ * QInputStream.java
+ * Copyright(C) 2002 The Free Software Foundation
+ *
+ * This file is part of GNU JavaMail, a library.
+ *
+ * GNU JavaMail 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.
+ *
+ * GNU JavaMail 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * As a special exception, if you link this library with other files to
+ * produce an executable, this library does not by itself cause the
+ * resulting executable to be covered by the GNU General Public License.
+ * This exception does not however invalidate any other reasons why the
+ * executable file might be covered by the GNU General Public License.
+ */
+
+package net.java.sip.communicator.impl.protocol.msn.mail.utils;
+
+import java.io.*;
+
+/**
+ * Provides RFC 2047 "B" transfer encoding.
+ * See section 4.2:
+ *
+ * The "Q" encoding is similar to the "Quoted-Printable" content-
+ * transfer-encoding defined in RFC 2045. It is designed to allow text
+ * containing mostly ASCII characters to be decipherable on an ASCII
+ * terminal without decoding.
+ *
+ * - Any 8-bit value may be represented by a "=" followed by two
+ * hexadecimal digits. For example, if the character set in use
+ * were ISO-8859-1, the "=" character would thus be encoded as
+ * "=3D", and a SPACE by "=20". (Upper case should be used for
+ * hexadecimal digits "A" through "F".)
+ *
- The 8-bit hexadecimal value 20(e.g., ISO-8859-1 SPACE) may be
+ * represented as "_"(underscore, ASCII 95.). (This character may
+ * not pass through some internetwork mail gateways, but its use
+ * will greatly enhance readability of "Q" encoded data with mail
+ * readers that do not support this encoding.) Note that the "_"
+ * always represents hexadecimal 20, even if the SPACE character
+ * occupies a different code position in the character set in use.
+ *
- 8-bit values which correspond to printable ASCII characters other
+ * than "=", "?", and "_"(underscore), MAY be represented as those
+ * characters. (But see section 5 for restrictions.) In
+ * particular, SPACE and TAB MUST NOT be represented as themselves
+ * within encoded words.
+ *
+ * @author Chris Burdess
+ */
+public class QInputStream
+ extends QPInputStream
+{
+
+ private static final int SPACE = 32;
+ private static final int EQ = 61;
+ private static final int UNDERSCORE = 95;
+
+ /**
+ * Constructor.
+ * @param in the underlying input stream.
+ */
+ public QInputStream(InputStream in)
+ {
+ super(in);
+ }
+
+ /**
+ * Read a character.
+ */
+ public int read()
+ throws IOException
+ {
+ int c = in.read();
+ if (c==UNDERSCORE)
+ return SPACE;
+ if (c==EQ)
+ {
+ buf[0] = (byte)in.read();
+ buf[1] = (byte)in.read();
+ try
+ {
+ return Integer.parseInt(new String(buf, 0, 2), 16);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IOException("Quoted-Printable encoding error: "+
+ e.getMessage());
+ }
+ }
+ return c;
+ }
+
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/QPInputStream.java b/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/QPInputStream.java
new file mode 100644
index 000000000..3abe72c9f
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/msn/mail/utils/QPInputStream.java
@@ -0,0 +1,167 @@
+/*
+ * QPInputStream.java
+ * Copyright(C) 2002 The Free Software Foundation
+ *
+ * This file is part of GNU JavaMail, a library.
+ *
+ * GNU JavaMail 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.
+ *
+ * GNU JavaMail 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * As a special exception, if you link this library with other files to
+ * produce an executable, this library does not by itself cause the
+ * resulting executable to be covered by the GNU General Public License.
+ * This exception does not however invalidate any other reasons why the
+ * executable file might be covered by the GNU General Public License.
+ */
+
+package net.java.sip.communicator.impl.protocol.msn.mail.utils;
+
+import java.io.*;
+
+/**
+ * A Quoted-Printable decoder stream.
+ *
+ * @author Chris Burdess
+ */
+public class QPInputStream
+ extends FilterInputStream
+{
+
+ protected byte[] buf;
+
+ /**
+ * The number of times read() will return a space.
+ */
+ protected int spaceCount;
+
+ private static final int LF = 10;
+ private static final int CR = 13;
+ private static final int SPACE = 32;
+ private static final int EQ = 61;
+
+ /**
+ * Constructor.
+ * @param in the underlying input stream.
+ */
+ public QPInputStream(InputStream in)
+ {
+ super(new PushbackInputStream(in, 2));
+ buf = new byte[2];
+ }
+
+ /**
+ * Read a character from the stream.
+ */
+ public int read()
+ throws IOException
+ {
+ if (spaceCount>0)
+ {
+ spaceCount--;
+ return SPACE;
+ }
+
+ int c = in.read();
+ if (c==SPACE)
+ {
+ while ((c = in.read())==SPACE)
+ spaceCount++;
+ if (c==LF || c==CR || c==-1)
+ spaceCount = 0;
+ else
+ {
+ ((PushbackInputStream)in).unread(c);
+ c = SPACE;
+ }
+ return c;
+ }
+ if (c==EQ)
+ {
+ int c2 = super.in.read();
+ if (c2==LF)
+ return read();
+ if (c2==CR)
+ {
+ int peek = in.read();
+ if (peek!=LF)
+ ((PushbackInputStream)in).unread(peek);
+ return read();
+ }
+ if (c2==-1)
+ return c2;
+
+ buf[0] = (byte)c2;
+ buf[1] = (byte)in.read();
+ try
+ {
+ return Integer.parseInt(new String(buf, 0, 2), 16);
+ }
+ catch (NumberFormatException e)
+ {
+ ((PushbackInputStream)in).unread(buf);
+ }
+ return c;
+ }
+ else
+ return c;
+ }
+
+ /**
+ * Reads from the underlying stream into the specified byte array.
+ */
+ public int read(byte[] bytes, int off, int len)
+ throws IOException
+ {
+ int pos = 0;
+ try
+ {
+ while (pos