/** * Parse a message from the server stream. * * @return the parsed Message * @throws IOException */ private EmailContent.Message addParser(final int endingTag) throws IOException, CommandStatusException { EmailContent.Message msg = new EmailContent.Message(); msg.mAccountKey = mAccount.mId; msg.mMailboxKey = mMailbox.mId; msg.mFlagLoaded = EmailContent.Message.FLAG_LOADED_COMPLETE; // Default to 1 (success) in case we don't get this tag int status = 1; while (nextTag(endingTag) != END) { switch (tag) { case Tags.SYNC_SERVER_ID: msg.mServerId = getValue(); LogUtils.d(TAG, "ServerId: %s", msg.mServerId); break; case Tags.SYNC_STATUS: status = getValueInt(); break; case Tags.SYNC_APPLICATION_DATA: addData(msg, tag); break; default: skipTag(); } } // For sync, status 1 = success if (status != 1) { throw new CommandStatusException(status, msg.mServerId); } return msg; }
private void bodyParser(EmailContent.Message msg) throws IOException { String bodyType = Eas.BODY_PREFERENCE_TEXT; String body = ""; while (nextTag(Tags.BASE_BODY) != END) { switch (tag) { case Tags.BASE_TYPE: bodyType = getValue(); break; case Tags.BASE_DATA: body = getValue(); break; case Tags.BASE_TRUNCATED: // M: Message is partial loaded if AirSyncBaseBody::Truncated is True; // Otherwise message is complete loaded if False String bodyTruncated = getValue(); if ("1".equals(bodyTruncated) || "true".equals(bodyTruncated)) { msg.mFlagLoaded = EmailContent.Message.FLAG_LOADED_PARTIAL; } else { msg.mFlagLoaded = EmailContent.Message.FLAG_LOADED_COMPLETE; } LogUtils.d(TAG, "_____________ EAS 12+ body truncated: " + bodyTruncated); break; default: skipTag(); } } // We always ask for TEXT or HTML; there's no third option if (bodyType.equals(Eas.BODY_PREFERENCE_HTML)) { msg.mHtml = body; } else { msg.mText = body; } /** * M: What if a mail with bodyTruncated tag, but both body html and text are empty? In this * case, client should set a suitable load flag: FLAG_LOADED_COMPLETE. @{ */ if (TextUtils.isEmpty(msg.mHtml) && TextUtils.isEmpty(msg.mText)) { LogUtils.d(TAG, " for empty mailbody, reset load complete tag."); msg.mFlagLoaded = EmailContent.Message.FLAG_LOADED_COMPLETE; } /** @} */ }
public void addData(EmailContent.Message msg, int endingTag) throws IOException { ArrayList<EmailContent.Attachment> atts = new ArrayList<EmailContent.Attachment>(); boolean truncated = false; while (nextTag(endingTag) != END) { switch (tag) { case Tags.EMAIL_ATTACHMENTS: case Tags.BASE_ATTACHMENTS: // BASE_ATTACHMENTS is used in EAS 12.0 and up attachmentsParser(atts, msg, tag); break; case Tags.EMAIL_TO: msg.mTo = Address.pack(Address.parse(getValue(), false)); break; case Tags.EMAIL_FROM: Address[] froms = Address.parse(getValue(), false); if (froms != null && froms.length > 0) { msg.mDisplayName = froms[0].toFriendly(); } msg.mFrom = Address.toString(froms); break; case Tags.EMAIL_CC: msg.mCc = Address.pack(Address.parse(getValue(), false)); break; case Tags.EMAIL_REPLY_TO: msg.mReplyTo = Address.pack(Address.parse(getValue(), false)); break; case Tags.EMAIL_DATE_RECEIVED: try { msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue()); } catch (ParseException e) { LogUtils.w(TAG, "Parse error for EMAIL_DATE_RECEIVED tag.", e); } break; case Tags.EMAIL_SUBJECT: msg.mSubject = getValue(); break; case Tags.EMAIL_READ: msg.mFlagRead = getValueInt() == 1; break; case Tags.BASE_BODY: bodyParser(msg); break; case Tags.EMAIL_FLAG: msg.mFlagFavorite = flagParser(); break; case Tags.EMAIL_MIME_TRUNCATED: truncated = getValueInt() == 1; break; case Tags.EMAIL_MIME_DATA: // We get MIME data for EAS 2.5. First we parse it, then we take the // html and/or plain text data and store it in the message if (truncated) { // If the MIME data is truncated, don't bother parsing it, because // it will take time and throw an exception anyway when EOF is reached // In this case, we will load the body separately by tagging the message // "partially loaded". // Get the data (and ignore it) getValue(); userLog("Partially loaded: ", msg.mServerId); msg.mFlagLoaded = EmailContent.Message.FLAG_LOADED_PARTIAL; mFetchNeeded = true; } else { mimeBodyParser(msg, getValue()); } break; case Tags.EMAIL_BODY: String text = getValue(); msg.mText = text; break; case Tags.EMAIL_MESSAGE_CLASS: String messageClass = getValue(); if (messageClass.equals("IPM.Schedule.Meeting.Request")) { msg.mFlags |= EmailContent.Message.FLAG_INCOMING_MEETING_INVITE; } else if (messageClass.equals("IPM.Schedule.Meeting.Canceled")) { msg.mFlags |= EmailContent.Message.FLAG_INCOMING_MEETING_CANCEL; } break; case Tags.EMAIL_MEETING_REQUEST: meetingRequestParser(msg); break; case Tags.EMAIL_THREAD_TOPIC: msg.mThreadTopic = getValue(); break; case Tags.RIGHTS_LICENSE: skipParser(tag); break; case Tags.EMAIL2_CONVERSATION_ID: msg.mServerConversationId = Base64.encodeToString(getValueBytes(), Base64.URL_SAFE); break; case Tags.EMAIL2_CONVERSATION_INDEX: // Ignore this byte array since we're not constructing a tree. getValueBytes(); break; case Tags.EMAIL2_LAST_VERB_EXECUTED: int val = getValueInt(); if (val == LAST_VERB_REPLY || val == LAST_VERB_REPLY_ALL) { // We aren't required to distinguish between reply and reply all here msg.mFlags |= EmailContent.Message.FLAG_REPLIED_TO; } else if (val == LAST_VERB_FORWARD) { msg.mFlags |= EmailContent.Message.FLAG_FORWARDED; } break; default: skipTag(); } } if (atts.size() > 0) { msg.mAttachments = atts; } if ((msg.mFlags & EmailContent.Message.FLAG_INCOMING_MEETING_MASK) != 0) { String text = TextUtilities.makeSnippetFromHtmlText(msg.mText != null ? msg.mText : msg.mHtml); if (TextUtils.isEmpty(text)) { // Create text for this invitation String meetingInfo = msg.mMeetingInfo; if (!TextUtils.isEmpty(meetingInfo)) { PackedString ps = new PackedString(meetingInfo); ContentValues values = new ContentValues(); putFromMeeting( ps, MeetingInfo.MEETING_LOCATION, values, CalendarContract.Events.EVENT_LOCATION); String dtstart = ps.get(MeetingInfo.MEETING_DTSTART); if (!TextUtils.isEmpty(dtstart)) { try { final long startTime = Utility.parseEmailDateTimeToMillis(dtstart); values.put(CalendarContract.Events.DTSTART, startTime); } catch (ParseException e) { LogUtils.w(TAG, "Parse error for MEETING_DTSTART tag.", e); } } putFromMeeting(ps, MeetingInfo.MEETING_ALL_DAY, values, CalendarContract.Events.ALL_DAY); msg.mText = CalendarUtilities.buildMessageTextFromEntityValues(mContext, values, null); msg.mHtml = Html.toHtml(new SpannedString(msg.mText)); } } } }
/** * Copy field-by-field from a "store" message to a "provider" message * * @param message The message we've just downloaded (must be a MimeMessage) * @param localMessage The message we'd like to write into the DB * @return true if dirty (changes were made) */ public static boolean updateMessageFields( final EmailContent.Message localMessage, final Message message, final long accountId, final long mailboxId) throws MessagingException { final Address[] from = message.getFrom(); final Address[] to = message.getRecipients(Message.RecipientType.TO); final Address[] cc = message.getRecipients(Message.RecipientType.CC); final Address[] bcc = message.getRecipients(Message.RecipientType.BCC); final Address[] replyTo = message.getReplyTo(); final String subject = message.getSubject(); final Date sentDate = message.getSentDate(); final Date internalDate = message.getInternalDate(); if (from != null && from.length > 0) { localMessage.mDisplayName = from[0].toFriendly(); } if (sentDate != null) { localMessage.mTimeStamp = sentDate.getTime(); } else if (internalDate != null) { LogUtils.w(Logging.LOG_TAG, "No sentDate, falling back to internalDate"); localMessage.mTimeStamp = internalDate.getTime(); } if (subject != null) { localMessage.mSubject = subject; } localMessage.mFlagRead = message.isSet(Flag.SEEN); if (message.isSet(Flag.ANSWERED)) { localMessage.mFlags |= EmailContent.Message.FLAG_REPLIED_TO; } // Keep the message in the "unloaded" state until it has (at least) a display name. // This prevents early flickering of empty messages in POP download. if (localMessage.mFlagLoaded != EmailContent.Message.FLAG_LOADED_COMPLETE) { if (localMessage.mDisplayName == null || "".equals(localMessage.mDisplayName)) { localMessage.mFlagLoaded = EmailContent.Message.FLAG_LOADED_UNLOADED; } else { localMessage.mFlagLoaded = EmailContent.Message.FLAG_LOADED_PARTIAL; } } localMessage.mFlagFavorite = message.isSet(Flag.FLAGGED); // public boolean mFlagAttachment = false; // public int mFlags = 0; localMessage.mServerId = message.getUid(); if (internalDate != null) { localMessage.mServerTimeStamp = internalDate.getTime(); } // public String mClientId; // Only replace the local message-id if a new one was found. This is seen in some ISP's // which may deliver messages w/o a message-id header. final String messageId = message.getMessageId(); if (messageId != null) { localMessage.mMessageId = messageId; } // public long mBodyKey; localMessage.mMailboxKey = mailboxId; localMessage.mAccountKey = accountId; if (from != null && from.length > 0) { localMessage.mFrom = Address.toString(from); } localMessage.mTo = Address.toString(to); localMessage.mCc = Address.toString(cc); localMessage.mBcc = Address.toString(bcc); localMessage.mReplyTo = Address.toString(replyTo); // public String mText; // public String mHtml; // public String mTextReply; // public String mHtmlReply; // // Can be used while building messages, but is NOT saved by the Provider // transient public ArrayList<Attachment> mAttachments = null; return true; }