Package com.funambol.syncclient.blackberry.email.impl

Source Code of com.funambol.syncclient.blackberry.email.impl.MIMEParser

/*
* Funambol is a mobile platform developed by Funambol, Inc.
* Copyright (C) 2003 - 2007 Funambol, Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
*
* 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 Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*
* You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
* 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by Funambol" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by Funambol".
*/
package com.funambol.syncclient.blackberry.email.impl;

import java.util.Hashtable;
import java.util.Vector;
import java.util.Date;
import java.util.Calendar;
import java.util.TimeZone;

import net.rim.blackberry.api.mail.Header;
import net.rim.blackberry.api.mail.Message;
import net.rim.blackberry.api.mail.Folder;
import net.rim.blackberry.api.mail.Store;
import net.rim.blackberry.api.mail.Session;

import net.rim.device.api.xml.parsers.*;
import net.rim.device.api.io.http.HttpDateParser;

import org.w3c.dom.*;
import org.xml.sax.*;

import com.funambol.syncclient.common.Base64;
import com.funambol.syncclient.util.StaticDataHelper;

import net.rim.blackberry.api.mail.Multipart;
import net.rim.blackberry.api.mail.SupportedAttachmentPart;
import net.rim.blackberry.api.mail.TextBodyPart;
import net.rim.blackberry.api.mail.FolderNotFoundException;
import net.rim.blackberry.api.mail.Address;


/**
* To parse MIME content in SyncML
*
*/
public class MIMEParser
{
    /**
     *
     * This method processes the MIME data and returns the message object
     *
     */      
    public Message parseMessage(String messageString,String locURI) {     
        Message msg = new Message();
        Hashtable ht = new Hashtable();

        try {           
            messageString = extractMIMEContent(messageString);
            ht = CreateHashTable(messageString);

            MailContent mailContent = new MailContent((String)ht.get("CONTENT-TYPE"));
            mailContent.parseContent();

            MailBody body = mailContent.getMailBody();

            Vector attachment = mailContent.getAttachmentList();

            msg.addHeader("FROM", (String)ht.get("FROM"));
            msg.addHeader("TO", (String)ht.get("TO"));
            msg.setHeader("DATE", (String)ht.get("DATE"));
            msg.setSentDate(parseRfc822Date((String)ht.get("DATE")));
            // Set sent date, shown when message is opened
            msg.addHeader("MIME-Version", (String)ht.get("MIME-VERSION"));
            // FIXME: should be set the received date too, if possible.
            MessageIDStore msgIdStore = new MessageIDStore();

            msgIdStore.setMessageMappingKey();

            msgIdStore.add(locURI, (String)ht.get("MESSAGE-ID"));

            msg.setSubject((String)ht.get("SUBJECT"));

            if ((String)ht.get("FROM") != null) {               
                String strFrom = (String)ht.get("FROM");

                if(strFrom.indexOf("<") >= 0){
                    strFrom = strFrom.substring(strFrom.indexOf("<") + 1, strFrom.indexOf(">"));
                }
                Address from = new Address(strFrom.trim(), " ");
                msg.setFrom(from);
            }

            // FIXME: What if there is more than one To: ??
            if ((String)ht.get("TO") != null) {

                String strTo = (String)ht.get("TO");

                if (strTo.indexOf("<") >= 0) {
                    strTo = strTo.substring(strTo.indexOf("<") + 1, strTo.indexOf(">"));
                }
                Address to[] = new Address[1];
                to[0] = new Address(strTo.trim(), " ");
                msg.addRecipients(Message.RecipientType.TO, to);
            }

            /*
                while (strTo.indexOf("<") >= 0) {
                    addr = strTo.substring(strTo.indexOf("<") + 1, strTo.indexOf(">"));
                   
                    Arrays.add(to, new Address(addr.trim(), " "));
                }
                msg.addRecipients(Message.RecipientType.TO, to);
            */
           
            if (attachment.size() == 0) {
                if (body.getActualContent() != null) {                  
                    String encoding = (String)ht.get("CONTENT-TRANSFER-ENCODING");

                    if (encoding != null && encoding.trim().equalsIgnoreCase("base64")) {
                        msg.setContent(new String(Base64.decode(body.getActualContent().getBytes())));
                    } else {    
                        msg.setContent(body.getActualContent());
                    }
                }
            } else {

                Multipart mp = new Multipart();
                TextBodyPart bodypart = new TextBodyPart(mp);
                int bodyPartIndex = 0;

                if (body.getActualContent() != null) {

                    String encoding = body.getContentTransferEncoding();

                    bodypart.setContentType(body.getContentType());

                    String content = null;
                    if (encoding != null && encoding.trim().equalsIgnoreCase("base64")){
                        content = new String(Base64.decode(body.getActualContent().getBytes()));
                    } else {
                        content = body.getActualContent();
                    }

                    bodypart.setContent(content);
                    mp.addBodyPart(bodypart,bodyPartIndex);
                    ++bodyPartIndex;
                }

                for (int i = 0; i < attachment.size(); i++) {

                    SupportedAttachmentPart sp =  new SupportedAttachmentPart(mp);

                    MailAttachment mailAttachment = (MailAttachment)attachment.elementAt(i);
                    sp.setFilename(mailAttachment.getFileName());

                    if (mailAttachment.getContentTransferEncoding().equals("base64")){
                        sp.setContent(new String(Base64.decode(mailAttachment.getactualContent().getBytes())));
                    }
                    else {
                        sp.setContent(mailAttachment.getactualContent());
                    }

                    sp.setContentType(mailAttachment.getContentType());
                    mp.addBodyPart(sp,bodyPartIndex);
                    ++bodyPartIndex;
                }

                msg.setContent(mp);
            }

        } catch(FolderNotFoundException f) {
            StaticDataHelper.log("FolderNotFoundException in MIMEParser.parseMessage(): "
                                 + f.getMessage());
        } catch(Exception e) {
            StaticDataHelper.log("Exception in MIMEParser.parseMessage(): " + e.getMessage());
        }

        return msg;
    }

    /**
     * Helper method to parse the data, this method populates the
     * Hashtable with the headers of the message and returns it.
     */ 
    public Hashtable CreateHashTable(String msg) throws Exception {

        String boundary = "''~~";//Intialise boundary to some junk value

        Hashtable htable = new Hashtable();

        // Split the content in lines and iterate through them
        String[] splitVals = new StringUtil().split(msg,"\n");

        for (int i = 0; i < splitVals.length; i++) {

            String temp = splitVals[i];

            // This is a boundary in case of multipart messages
            if (temp.trim().equals(boundary)) {

                StringBuffer section = new StringBuffer();

                for (int j = i; j < splitVals.length; j++) {
                    section.append(splitVals[j]).append("\n");
                }

                String contentType = (String) htable.get("CONTENT-TYPE");

                // The part content is stored in the hashtable along with the
                // content-type... No comment.
                if (contentType != null) {
                    htable.put("CONTENT-TYPE", contentType + section.toString());
                }

                break;
            }

            // This is an empty line: it is the separation between the header
            // and the body of the message.
            if ( temp.trim().length()==0 ) {

                if (StringUtil.removeWideSpaces(temp).equals("\r")
                   || StringUtil.removeWideSpaces(temp).equals("\r\r")) {

                    if (htable.containsKey("CONTENT-TYPE")) {
                        String contentType=(String)htable.get("CONTENT-TYPE");

                        String helper=contentType.toLowerCase();

                        if ((contentType.trim().length() != 0)
                                && (helper.indexOf("multipart/related")== -1)
                                && (helper.indexOf("multipart/alternative")== -1)
                                && (helper.indexOf("multipart/mixed")== -1)) {

                            StringBuffer section=new StringBuffer();

                            for (int j=i;j<splitVals.length;j++) {   
                                section.append(splitVals[j]).append("\n");
                            }

                            htable.put("CONTENT-TYPE",contentType+section.toString());
                            break;
                        }
                    }
                }
            }

            // Find the header separator in the line
            String[] keyValue = new StringUtil().split(temp, ":");

            // This is an header, parse it
            if (keyValue.length == 2) {

                String key = keyValue[0].toUpperCase().trim();
                String value = keyValue[1];

                if ((key.equalsIgnoreCase("Content-Disposition"))
                      // FIXME: what if the filename is not in the next line?
                      && (splitVals[i+1].toLowerCase().indexOf("filename") >= 0)) {
                   
                    htable.put("CONTENT-DISPOSITION", value + "\n");
                   
                    String[] file = new StringUtil().split(splitVals[i+1], "=");
                    htable.put("FILENAME", file[1]);
                    ++i;
                } else if (key.equalsIgnoreCase("Content-Type")) {

                    if (value.equals("*/*\r")) {
                        htable.put("CONTENT-TYPE", value + "\n");
                    }
                    else if (!(value.indexOf("multipart/related") < 0)) {
                        i++;
                        htable.put("CONTENT-TYPE", value + "\n" + splitVals[i]+ "\n");
                        boundary = "--" + findBoundary(splitVals[i]) ;

                    }
                    else if (!(value.indexOf("multipart/mixed") < 0)) {
                        i++;
                        htable.put("CONTENT-TYPE", value + "\n" + splitVals[i]+ "\n");
                        boundary = "--" + findBoundary(splitVals[i]) ;
                    }
                    else if (!(value.indexOf("multipart/alternative") < 0)) {
                        i++;
                        htable.put("CONTENT-TYPE", value + "\n" + splitVals[i]+ "\n");
                        boundary = "--" + findBoundary(splitVals[i]) ;
                    }
                    else{
                        if(splitVals[i+1].indexOf("charset=") >= 0) {
                            value = value+splitVals[i+1].trim();
                            ++i;
                        }
                        htable.put("CONTENT-TYPE", value + "\r\n");
                    }
                }
                else {
                    // Store the other headers
                    String storedValue = (String) htable.get(key);

                    if (storedValue != null) {
                        value = storedValue + "[``-<==>-``]" + value;
                    }
                    htable.put(key.trim(), value);
                }
            }
            else if (keyValue.length > 2) {
                // Store the headers with a colon inside the value
                // FIXME: could this happen for multipart header too?
                String value = new String();
                String key = keyValue[0].toUpperCase();

                for (int tempi = 1; tempi < keyValue.length; tempi++){
                    value = value + ":" + keyValue[tempi];
                }

                value = value.substring(1, value.length());
                htable.put(key.trim(), value);
            }
        }

        return htable;
    }

    /**
     *
     * This method find the boundry in the MIME data
     *
     */   
    public static String findBoundary(String content) {

        String copy = content.toLowerCase();
        int startIndex = copy.indexOf("boundary=\"");
        int endIndex = copy.indexOf("\"",startIndex+10);

        if (startIndex >= 0 && endIndex >= 0) {
            return content.substring(startIndex+10, endIndex);
        }

        startIndex = copy.indexOf("boundary=");
        endIndex = copy.indexOf(";");

        if (endIndex==-1) {
            endIndex = copy.indexOf("\r");
        }

        if (startIndex > 0 && endIndex > 0) {
            return content.substring(startIndex + 9, endIndex).trim();
        }
        return null;
    }

    /**
     *
     * This method extracts the required MIME data from the message
     *
     */ 

    private String extractMIMEContent(String content) {

        String DATA_START_MARKER    = "<![CDATA[";
        String DATA_END_MARKER      = "]]>";

        String mimeData = null;
        int startMarkerIndex = content.indexOf(DATA_START_MARKER);
        int endMarkerIndex = content.indexOf(DATA_END_MARKER);

        if (startMarkerIndex == -1 || endMarkerIndex == -1) {
            return null;
        }

        return mimeData = content.substring((startMarkerIndex
                    + DATA_START_MARKER.length()),endMarkerIndex);
    }
   
    // TODO: create a class derived from Date to handle the rfc822 date
    // parsing and formatting. Also the getTimeZoneOffset can be a method
    // of this class, now it's duplicated!

    /**
     * @param date date to read offset
     * @return timezone offset
     */
    private long getTimeZoneOffSet(Date date) {

        Calendar now = null;
        TimeZone zn = null;
        long offset = 0;
       
        /*
         * Gets a calendar using the
         * default time zone and locale
         */
        now = Calendar.getInstance();

        now.setTime(date);

        /*
         * creates a TimeZone based
         * on the time zone where the
         * program is running
         */
        zn = TimeZone.getDefault();

        /*
         * the offset to add to GMT to
         * get local time, modified in
         * case of daylight savings
         */
        offset = zn.getOffset(1                                               ,//era AD
                              now.get(Calendar.YEAR)                          ,
                              now.get(Calendar.MONTH)                         ,
                              now.get(Calendar.DAY_OF_MONTH)                  ,
                              now.get(Calendar.DAY_OF_WEEK)                   ,
                              now.get(Calendar.HOUR_OF_DAY) * 60 * 60 * 1000  +//the milliseconds
                              now.get(Calendar.MINUTE) * 60 * 1000            +//in day in standard
                              now.get(Calendar.SECOND) * 1000                );//local time


        return offset;
    }
   
    /*
     * Parse the string in RFC822 format and return a Date object.
     * On error, log and return the current date.
     */
    private Date parseRfc822Date(String d)
    {
        Hashtable monthNames = new Hashtable();
        monthNames.put("Jan", new Integer(0));
        monthNames.put("Feb", new Integer(1));
        monthNames.put("Mar", new Integer(2));   
        monthNames.put("Apr", new Integer(3));
        monthNames.put("May", new Integer(4));   
        monthNames.put("Jun", new Integer(5));
        monthNames.put("Jul", new Integer(6));   
        monthNames.put("Aug", new Integer(7));
        monthNames.put("Sep", new Integer(8));   
        monthNames.put("Oct", new Integer(9));
        monthNames.put("Nov", new Integer(10));  
        monthNames.put("Dec", new Integer(11));
       
        try {
            String [] ids = TimeZone.getAvailableIDs();
            Calendar cal = Calendar.getInstance();
           
            StaticDataHelper.log("Date original: " + d );

            int start = d.indexOf(',');
            // Just skip the weekday if present
            start = (start == -1) ? 0 : start + 2;
            // Get day of month
            int end = d.indexOf(' ', start);
            cal.set(cal.DAY_OF_MONTH, Integer.parseInt(d.substring(start,end)));
            // Get month
            start = end + 1;
            end = d.indexOf(' ', start);
            cal.set(cal.MONTH, ((Integer)monthNames.get(d.substring(start,end))).intValue());
            // Get year
            start = end + 1;
            end = d.indexOf(' ', start);
            cal.set(cal.YEAR, Integer.parseInt(d.substring(start,end)));
            // Get hour
            start = end + 1;
            end = d.indexOf(':', start);
            cal.set(cal.HOUR_OF_DAY, Integer.parseInt(d.substring(start,end)));
            // Get min
            start = end + 1;
            end = d.indexOf(':', start);
            cal.set(cal.MINUTE, Integer.parseInt(d.substring(start,end)));
            // Get sec
            start = end + 1;
            end = d.indexOf(' ', start);
            cal.set(cal.SECOND, Integer.parseInt(d.substring(start,end)));   
           
            Date ret = cal.getTime();
           
            // Get timezone
            start = end + 1;
            end = d.indexOf('\r', start);
            String tzString = null;
            if (end != -1){
                tzString = d.substring(start, end);
            }
            else {
                tzString = d.substring(start);
            }
            // Process timezone (TimeZone class does not work with this format)
            if(tzString.charAt(0) == '+' || tzString.charAt(0) == '-'){
                long tzoffset = getTimeZoneOffSet(ret);

                try {
                    // Skip the '+' or parseInt fails...
                    int st = (tzString.charAt(0) == '-') ? 0 : 1;
                    String hs = tzString.substring(st,3);
                    String ms = tzString.substring(3,5);
                    long hh = Integer.parseInt(hs);
                    long mm = Integer.parseInt(ms);
                   
                    // Adjust time
                    StaticDataHelper.log("Date Local: " + ret );
                    long tt = ret.getTime() + tzoffset;
                    StaticDataHelper.log("Date adjusted: " + new Date(tt) );
                    tt -= (hh * 3600 + mm * 60) * 1000;
                    ret = new Date(tt);
                }
                catch (NumberFormatException nfe){
                    StaticDataHelper.log("Error decoding tz.:" + tzString);
                }
            }
            else {
                TimeZone tz = TimeZone.getTimeZone(tzString);
                cal.setTimeZone(tz);
                ret = cal.getTime();
            }
           
            StaticDataHelper.log("Date parsed and converted: " + ret );
            return ret;
        }
        catch (Exception e){
            StaticDataHelper.log("Exception in parseRfc822Date: " + e.toString());
            // Return the current date
            return Calendar.getInstance().getTime();
        }
    }

    // TODO (see above) --------------------------------------------------------
}
TOP

Related Classes of com.funambol.syncclient.blackberry.email.impl.MIMEParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.