@Override
  public void loadMore(long messageId) throws RemoteException {
    /// M: We Can't load more in low storage state @{
    if (StorageLowState.checkIfStorageLow(mContext)) {
      LogUtils.e(Logging.LOG_TAG, "Can't load more due to low storage");
      return;
    }
    /// @}
    // Load a message for view...
    try {
      // 1. Resample the message, in case it disappeared or synced while
      // this command was in queue
      final EmailContent.Message message =
          EmailContent.Message.restoreMessageWithId(mContext, messageId);
      if (message == null) {
        return;
      }
      if (message.mFlagLoaded == EmailContent.Message.FLAG_LOADED_COMPLETE) {
        // We should NEVER get here
        return;
      }

      // 2. Open the remote folder.
      // TODO combine with common code in loadAttachment
      final Account account = Account.restoreAccountWithId(mContext, message.mAccountKey);
      final Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, message.mMailboxKey);
      if (account == null || mailbox == null) {
        // mListeners.loadMessageForViewFailed(messageId, "null account or mailbox");
        return;
      }
      TrafficStats.setThreadStatsTag(TrafficFlags.getSyncFlags(mContext, account));

      final Store remoteStore = Store.getInstance(account, mContext);
      final String remoteServerId;
      // If this is a search result, use the protocolSearchInfo field to get the
      // correct remote location
      if (!TextUtils.isEmpty(message.mProtocolSearchInfo)) {
        remoteServerId = message.mProtocolSearchInfo;
      } else {
        remoteServerId = mailbox.mServerId;
      }
      final Folder remoteFolder = remoteStore.getFolder(remoteServerId);
      remoteFolder.open(OpenMode.READ_WRITE);

      // 3. Set up to download the entire message
      final Message remoteMessage = remoteFolder.getMessage(message.mServerId);
      final FetchProfile fp = new FetchProfile();
      fp.add(FetchProfile.Item.BODY);
      remoteFolder.fetch(new Message[] {remoteMessage}, fp, null);

      // 4. Write to provider
      Utilities.copyOneMessageToProvider(
          mContext, remoteMessage, account, mailbox, EmailContent.Message.FLAG_LOADED_COMPLETE);
    } catch (MessagingException me) {
      if (Logging.LOGD) LogUtils.v(Logging.LOG_TAG, "", me);

    } catch (RuntimeException rte) {
      LogUtils.d(Logging.LOG_TAG, "RTE During loadMore");
    }
  }
 /**
  * For all accounts that require password expiration, put them in security hold and wipe their
  * data.
  *
  * @param context context
  * @return true if one or more accounts were wiped
  */
 @VisibleForTesting
 /*package*/ static boolean wipeExpiredAccounts(Context context) {
   boolean result = false;
   Cursor c =
       context
           .getContentResolver()
           .query(Policy.CONTENT_URI, Policy.ID_PROJECTION, HAS_PASSWORD_EXPIRATION, null, null);
   if (c == null) {
     return false;
   }
   try {
     while (c.moveToNext()) {
       long policyId = c.getLong(Policy.ID_PROJECTION_COLUMN);
       long accountId = Policy.getAccountIdWithPolicyKey(context, policyId);
       if (accountId < 0) continue;
       Account account = Account.restoreAccountWithId(context, accountId);
       if (account != null) {
         // Mark the account as "on hold".
         setAccountHoldFlag(context, account, true);
         // Erase data
         Uri uri = EmailProvider.uiUri("uiaccountdata", accountId);
         context.getContentResolver().delete(uri, null, null);
         // Report one or more were found
         result = true;
       }
     }
   } finally {
     c.close();
   }
   return result;
 }
 @Deprecated
 @Override
 public void startSync(long mailboxId, boolean userRequest, int deltaMessageCount)
     throws RemoteException {
   final Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId);
   if (mailbox == null) return;
   final Account account = Account.restoreAccountWithId(mContext, mailbox.mAccountKey);
   if (account == null) return;
   final EmailServiceInfo info = EmailServiceUtils.getServiceInfoForAccount(mContext, account.mId);
   final android.accounts.Account acct =
       new android.accounts.Account(account.mEmailAddress, info.accountType);
   final Bundle extras = Mailbox.createSyncBundle(mailboxId);
   if (userRequest) {
     extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
     extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
     extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
   }
   if (deltaMessageCount != 0) {
     extras.putInt(Mailbox.SYNC_EXTRA_DELTA_MESSAGE_COUNT, deltaMessageCount);
   }
   ContentResolver.requestSync(acct, EmailContent.AUTHORITY, extras);
   LogUtils.i(
       Logging.LOG_TAG,
       "requestSync EmailServiceStub startSync %s, %s",
       account.toString(),
       extras.toString());
 }
 private void handleError(final MessagingException result, final long accountId, int progress) {
   if (accountId == -1) {
     return;
   }
   if (result == null) {
     if (progress > 0) {
       // Connection now working; clear the error message banner
       if (mLastErrorAccountId == accountId) {
         dismissErrorMessage();
       }
     }
   } else {
     Account account = Account.restoreAccountWithId(EmailActivity.this, accountId);
     if (account == null) return;
     String message = MessagingExceptionStrings.getErrorString(EmailActivity.this, result);
     if (!TextUtils.isEmpty(account.mDisplayName)) {
       // TODO Use properly designed layout. Don't just concatenate strings;
       // which is generally poor for I18N.
       message = message + "   (" + account.mDisplayName + ")";
     }
     if (mErrorBanner.show(message)) {
       mLastErrorAccountId = accountId;
     }
   }
 }
 /** Convenience method; see javadoc below */
 public static void setAccountHoldFlag(Context context, long accountId, boolean newState) {
   Account account = Account.restoreAccountWithId(context, accountId);
   if (account != null) {
     setAccountHoldFlag(context, account, newState);
     if (newState) {
       // Make sure there's a notification up
       final NotificationController nc = NotificationControllerCreatorHolder.getInstance(context);
       nc.showSecurityNeededNotification(account);
     }
   }
 }
 /**
  * This is the remote call from the Email app, currently unused. TODO: remove this when it's
  * been deleted from IEmailService.aidl.
  */
 @Deprecated
 @Override
 public void startSync(long mailboxId, boolean userRequest, int deltaMessageCount)
     throws RemoteException {
   SyncManager exchangeService = INSTANCE;
   if (exchangeService == null) return;
   checkExchangeServiceServiceRunning();
   Mailbox m = Mailbox.restoreMailboxWithId(exchangeService, mailboxId);
   if (m == null) return;
   Account acct = Account.restoreAccountWithId(exchangeService, m.mAccountKey);
   if (acct == null) return;
   // If this is a user request and we're being held, release the hold; this allows us to
   // try again (the hold might have been specific to this account and released already)
   if (userRequest) {
     if (onSyncDisabledHold(acct)) {
       releaseSyncHolds(exchangeService, AbstractSyncService.EXIT_ACCESS_DENIED, acct);
       log("User requested sync of account in sync disabled hold; releasing");
     } else if (onSecurityHold(acct)) {
       releaseSyncHolds(exchangeService, AbstractSyncService.EXIT_SECURITY_FAILURE, acct);
       log("User requested sync of account in security hold; releasing");
     }
     if (sConnectivityHold) {
       return;
     }
   }
   if (m.mType == Mailbox.TYPE_OUTBOX) {
     // We're using SERVER_ID to indicate an error condition (it has no other use for
     // sent mail)  Upon request to sync the Outbox, we clear this so that all messages
     // are candidates for sending.
     ContentValues cv = new ContentValues();
     cv.put(SyncColumns.SERVER_ID, 0);
     exchangeService
         .getContentResolver()
         .update(
             Message.CONTENT_URI,
             cv,
             WHERE_MAILBOX_KEY,
             new String[] {Long.toString(mailboxId)});
     // Clear the error state; the Outbox sync will be started from checkMailboxes
     exchangeService.mSyncErrorMap.remove(mailboxId);
     kick("start outbox");
     // Outbox can't be synced in EAS
     return;
   } else if (!isSyncable(m)) {
     return;
   }
   startManualSync(
       mailboxId,
       userRequest
           ? ExchangeService.SYNC_UI_REQUEST
           : ExchangeService.SYNC_SERVICE_START_SYNC,
       null);
 }
  private void saveEntry(Context context, CacheEntry entry) {
    LogUtils.d(Logging.LOG_TAG, "saveEntry");

    final Account account = Account.restoreAccountWithId(context, entry.mAccountId);
    final HostAuth hostAuth = account.getOrCreateHostAuthRecv(context);
    final Credential cred = hostAuth.getOrCreateCredential(context);
    cred.mProviderId = entry.mProviderId;
    cred.mAccessToken = entry.mAccessToken;
    cred.mRefreshToken = entry.mRefreshToken;
    cred.mExpiration = entry.mExpirationTime;
    cred.update(context, cred.toContentValues());
  }
  @Override
  protected void onMessageShown(long messageId, Mailbox mailbox) {
    super.onMessageShown(messageId, mailbox);

    Account account = Account.restoreAccountWithId(mContext, getAccountId());
    boolean supportsMove = account.supportsMoveMessages(mContext) && mailbox.canHaveMessagesMoved();
    if (mSupportsMove != supportsMove) {
      mSupportsMove = supportsMove;
      Activity host = getActivity();
      if (host != null) {
        host.invalidateOptionsMenu();
      }
    }

    // Disable forward/reply buttons as necessary.
    enableReplyForwardButtons(Mailbox.isMailboxTypeReplyAndForwardable(mailbox.mType));
  }
  /**
   * API: Sync service should call this any time a sync fails due to isActive() returning false.
   * This will kick off the notify-acquire-admin-state process and/or increase the security level.
   * The caller needs to write the required policies into this account before making this call.
   * Should not be called from UI thread - uses DB lookups to prepare new notifications
   *
   * @param accountId the account for which sync cannot proceed
   */
  public void policiesRequired(long accountId) {
    Account account = Account.restoreAccountWithId(mContext, accountId);
    // In case the account has been deleted, just return
    if (account == null) return;
    if (account.mPolicyKey == 0) return;
    Policy policy = Policy.restorePolicyWithId(mContext, account.mPolicyKey);
    if (policy == null) return;
    if (DebugUtils.DEBUG) {
      LogUtils.d(TAG, "policiesRequired for " + account.mDisplayName + ": " + policy);
    }

    // Mark the account as "on hold".
    setAccountHoldFlag(mContext, account, true);

    // Put up an appropriate notification
    final NotificationController nc = NotificationControllerCreatorHolder.getInstance(mContext);
    if (policy.mProtocolPoliciesUnsupported == null) {
      nc.showSecurityNeededNotification(account);
    } else {
      nc.showSecurityUnsupportedNotification(account);
    }
  }
示例#10
0
  public static void sendMailImpl(Context context, long accountId) {
    /// M: We Can't send mail in low storage state @{
    if (StorageLowState.checkIfStorageLow(context)) {
      LogUtils.e(Logging.LOG_TAG, "Can't send mail due to low storage");
      return;
    }
    /// @}

    /** M: Get the sendable mails count of the account and notify this sending @{ */
    final int count = getSendableMessageCount(context, accountId);
    LogUtils.logFeature(LogTag.SENDMAIL_TAG, "sendable message count [%d]", count);
    if (count <= 0) {
      return;
    }
    SendNotificationProxy.getInstance(context)
        .showSendingNotification(accountId, NotificationController.SEND_MAIL, count);
    /** @} */
    final Account account = Account.restoreAccountWithId(context, accountId);
    TrafficStats.setThreadStatsTag(TrafficFlags.getSmtpFlags(context, account));
    final NotificationController nc = NotificationController.getInstance(context);
    // 1.  Loop through all messages in the account's outbox
    final long outboxId = Mailbox.findMailboxOfType(context, account.mId, Mailbox.TYPE_OUTBOX);
    if (outboxId == Mailbox.NO_MAILBOX) {
      return;
    }
    final ContentResolver resolver = context.getContentResolver();
    final Cursor c =
        resolver.query(
            EmailContent.Message.CONTENT_URI,
            EmailContent.Message.ID_COLUMN_PROJECTION,
            EmailContent.Message.MAILBOX_KEY + "=?",
            new String[] {Long.toString(outboxId)},
            null);
    try {
      // 2.  exit early
      if (c.getCount() <= 0) {
        return;
      }
      final Sender sender = Sender.getInstance(context, account);
      final Store remoteStore = Store.getInstance(account, context);
      final ContentValues moveToSentValues;
      if (remoteStore.requireCopyMessageToSentFolder()) {
        Mailbox sentFolder = Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_SENT);
        moveToSentValues = new ContentValues();
        moveToSentValues.put(MessageColumns.MAILBOX_KEY, sentFolder.mId);
      } else {
        moveToSentValues = null;
      }

      // 3.  loop through the available messages and send them
      /** M: mark should we cancel the Login Failed Notification. */
      boolean shouldCancelNf = false;
      while (c.moveToNext()) {
        long messageId = -1;
        if (moveToSentValues != null) {
          moveToSentValues.remove(EmailContent.MessageColumns.FLAGS);
        }
        try {
          messageId = c.getLong(0);
          // Don't send messages with unloaded attachments
          if (Utility.hasUnloadedAttachments(context, messageId)) {
            LogUtils.logFeature(
                LogTag.SENDMAIL_TAG, "Can't send #" + messageId + "; unloaded attachments");
            continue;
          }
          sender.sendMessage(messageId);
        } catch (MessagingException me) {
          LogUtils.logFeature(
              LogTag.SENDMAIL_TAG,
              "<<< Smtp send message failed id [%s], exception: %s",
              messageId,
              me);
          // report error for this message, but keep trying others
          if (me instanceof AuthenticationFailedException) {
            shouldCancelNf = false;
            nc.showLoginFailedNotification(account.mId);
          }
          /// M: One mail sent failed
          SendNotificationProxy.getInstance(context)
              .showSendingNotification(account.mId, NotificationController.SEND_FAILED, 1);
          continue;
        }

        /// M: One mail sent complete
        SendNotificationProxy.getInstance(context)
            .showSendingNotification(account.mId, NotificationController.SEND_COMPLETE, 1);

        // 4. move to sent, or delete
        final Uri syncedUri =
            ContentUris.withAppendedId(EmailContent.Message.SYNCED_CONTENT_URI, messageId);
        // Delete all cached files
        AttachmentUtilities.deleteAllCachedAttachmentFiles(context, account.mId, messageId);
        if (moveToSentValues != null) {
          // If this is a forwarded message and it has attachments, delete them, as they
          // duplicate information found elsewhere (on the server).  This saves storage.
          final EmailContent.Message msg =
              EmailContent.Message.restoreMessageWithId(context, messageId);
          if ((msg.mFlags & EmailContent.Message.FLAG_TYPE_FORWARD) != 0) {
            AttachmentUtilities.deleteAllAttachmentFiles(context, account.mId, messageId);
          }
          /// M: un-mark sending status after sending
          final int flags =
              msg.mFlags
                  & ~(EmailContent.Message.FLAG_TYPE_REPLY
                      | EmailContent.Message.FLAG_TYPE_FORWARD
                      | EmailContent.Message.FLAG_TYPE_REPLY_ALL
                      | EmailContent.Message.FLAG_TYPE_ORIGINAL
                      | EmailContent.Message.FLAG_STATUS_SENDING);

          moveToSentValues.put(EmailContent.MessageColumns.FLAGS, flags);
          resolver.update(syncedUri, moveToSentValues, null, null);
        } else {
          AttachmentUtilities.deleteAllAttachmentFiles(context, account.mId, messageId);
          final Uri uri = ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, messageId);
          resolver.delete(uri, null, null);
          resolver.delete(syncedUri, null, null);
        }
        shouldCancelNf = true;
      }
      if (shouldCancelNf) {
        nc.cancelLoginFailedNotification(account.mId);
      }
    } catch (MessagingException me) {
      if (me instanceof AuthenticationFailedException) {
        nc.showLoginFailedNotification(account.mId);
      }
      /// M: All mails failed to be sent, caused by fail to get instance of store
      SendNotificationProxy.getInstance(context)
          .showSendingNotification(account.mId, NotificationController.SEND_FAILED, c.getCount());
    } finally {
      c.close();
    }
  }
示例#11
0
  /// M: The folder sync must be synchronized, otherwise the folders may be duplicate
  @Override
  public synchronized void updateFolderList(long accountId) throws RemoteException {
    /// M: We Can't updateFolderList in low storage state @{
    if (StorageLowState.checkIfStorageLow(mContext)) {
      LogUtils.e(Logging.LOG_TAG, "Can't updateFolderList due to low storage");
      return;
    }
    /// @}
    final Account account = Account.restoreAccountWithId(mContext, accountId);
    if (account == null) {
      return;
    }
    long inboxId = -1;
    TrafficStats.setThreadStatsTag(TrafficFlags.getSyncFlags(mContext, account));
    Cursor localFolderCursor = null;
    try {
      // Step 0: Make sure the default system mailboxes exist.
      for (final int type : Mailbox.REQUIRED_FOLDER_TYPES) {
        if (Mailbox.findMailboxOfType(mContext, accountId, type) == Mailbox.NO_MAILBOX) {
          final Mailbox mailbox = Mailbox.newSystemMailbox(mContext, accountId, type);
          mailbox.save(mContext);
          if (type == Mailbox.TYPE_INBOX) {
            inboxId = mailbox.mId;
          }
        }
      }

      // Step 1: Get remote mailboxes
      final Store store = Store.getInstance(account, mContext);
      final Folder[] remoteFolders = store.updateFolders();
      final HashSet<String> remoteFolderNames = new HashSet<String>();
      for (final Folder remoteFolder : remoteFolders) {
        remoteFolderNames.add(remoteFolder.getName());
      }

      // Step 2: Get local mailboxes
      localFolderCursor =
          mContext
              .getContentResolver()
              .query(
                  Mailbox.CONTENT_URI,
                  MAILBOX_PROJECTION,
                  EmailContent.MailboxColumns.ACCOUNT_KEY + "=?",
                  new String[] {String.valueOf(account.mId)},
                  null);

      // Step 3: Remove any local mailbox not on the remote list
      while (localFolderCursor.moveToNext()) {
        final String mailboxPath = localFolderCursor.getString(MAILBOX_COLUMN_SERVER_ID);
        // Short circuit if we have a remote mailbox with the same name
        if (remoteFolderNames.contains(mailboxPath)) {
          continue;
        }

        final int mailboxType = localFolderCursor.getInt(MAILBOX_COLUMN_TYPE);
        final long mailboxId = localFolderCursor.getLong(MAILBOX_COLUMN_ID);
        switch (mailboxType) {
          case Mailbox.TYPE_INBOX:
          case Mailbox.TYPE_DRAFTS:
          case Mailbox.TYPE_OUTBOX:
          case Mailbox.TYPE_SENT:
          case Mailbox.TYPE_TRASH:
          case Mailbox.TYPE_SEARCH:
            // Never, ever delete special mailboxes
            break;
          default:
            // Drop all attachment files related to this mailbox
            AttachmentUtilities.deleteAllMailboxAttachmentFiles(mContext, accountId, mailboxId);
            // Delete the mailbox; database triggers take care of related
            // Message, Body and Attachment records
            Uri uri = ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId);
            mContext.getContentResolver().delete(uri, null, null);
            break;
        }
      }
    } catch (MessagingException me) {
      LogUtils.i(Logging.LOG_TAG, me, "Error in updateFolderList");
      // We'll hope this is temporary
    } finally {
      if (localFolderCursor != null) {
        localFolderCursor.close();
      }
      // If we just created the inbox, sync it
      if (inboxId != -1) {
        startSync(inboxId, true, 0);
      }
    }
  }
示例#12
0
  @Override
  public void loadAttachment(
      final IEmailServiceCallback cb, final long attachmentId, final boolean background)
      throws RemoteException {
    /// M: We Can't load attachment in low storage state @{
    if (StorageLowState.checkIfStorageLow(mContext)) {
      LogUtils.e(Logging.LOG_TAG, "Can't load attachment due to low storage");
      cb.loadAttachmentStatus(0, attachmentId, EmailServiceStatus.SUCCESS, 0);
      return;
    }
    /// @}
    Folder remoteFolder = null;
    try {
      // 1. Check if the attachment is already here and return early in that case
      Attachment attachment = Attachment.restoreAttachmentWithId(mContext, attachmentId);
      if (attachment == null) {
        cb.loadAttachmentStatus(0, attachmentId, EmailServiceStatus.ATTACHMENT_NOT_FOUND, 0);
        return;
      }
      final long messageId = attachment.mMessageKey;

      final EmailContent.Message message =
          EmailContent.Message.restoreMessageWithId(mContext, attachment.mMessageKey);
      if (message == null) {
        cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.MESSAGE_NOT_FOUND, 0);
        return;
      }

      // If the message is loaded, just report that we're finished
      if (Utility.attachmentExists(mContext, attachment)
          && attachment.mUiState == UIProvider.AttachmentState.SAVED) {
        cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.SUCCESS, 0);
        return;
      }

      // Say we're starting...
      cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.IN_PROGRESS, 0);

      // 2. Open the remote folder.
      final Account account = Account.restoreAccountWithId(mContext, message.mAccountKey);
      Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, message.mMailboxKey);

      if (mailbox.mType == Mailbox.TYPE_OUTBOX
          /// M: View an attachment which comes from refMessage need sourceKey to identify
          || mailbox.mType == Mailbox.TYPE_DRAFTS) {
        long sourceId =
            Utility.getFirstRowLong(
                mContext,
                Body.CONTENT_URI,
                new String[] {BodyColumns.SOURCE_MESSAGE_KEY},
                BodyColumns.MESSAGE_KEY + "=?",
                new String[] {Long.toString(messageId)},
                null,
                0,
                -1L);
        if (sourceId != -1) {
          EmailContent.Message sourceMsg =
              EmailContent.Message.restoreMessageWithId(mContext, sourceId);
          if (sourceMsg != null) {
            mailbox = Mailbox.restoreMailboxWithId(mContext, sourceMsg.mMailboxKey);
            message.mServerId = sourceMsg.mServerId;
          }
        }
      } else if (mailbox.mType == Mailbox.TYPE_SEARCH && message.mMainMailboxKey != 0) {
        mailbox = Mailbox.restoreMailboxWithId(mContext, message.mMainMailboxKey);
      }

      if (account == null || mailbox == null) {
        // If the account/mailbox are gone, just report success; the UI handles this
        cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.SUCCESS, 0);
        return;
      }
      TrafficStats.setThreadStatsTag(TrafficFlags.getAttachmentFlags(mContext, account));

      final Store remoteStore = Store.getInstance(account, mContext);
      remoteFolder = remoteStore.getFolder(mailbox.mServerId);
      remoteFolder.open(OpenMode.READ_WRITE);

      // 3. Generate a shell message in which to retrieve the attachment,
      // and a shell BodyPart for the attachment.  Then glue them together.
      final Message storeMessage = remoteFolder.createMessage(message.mServerId);
      final MimeBodyPart storePart = new MimeBodyPart();
      storePart.setSize((int) attachment.mSize);
      storePart.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, attachment.mLocation);
      storePart.setHeader(
          MimeHeader.HEADER_CONTENT_TYPE,
          String.format("%s;\n name=\"%s\"", attachment.mMimeType, attachment.mFileName));

      // TODO is this always true for attachments?  I think we dropped the
      // true encoding along the way
      /// M: set encoding type according to data base record.
      String encoding = attachment.mEncoding;
      if (TextUtils.isEmpty(encoding)) {
        encoding = "base64";
      }
      storePart.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);

      final MimeMultipart multipart = new MimeMultipart();
      multipart.setSubType("mixed");
      multipart.addBodyPart(storePart);

      storeMessage.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "multipart/mixed");
      storeMessage.setBody(multipart);

      // 4. Now ask for the attachment to be fetched
      final FetchProfile fp = new FetchProfile();
      fp.add(storePart);
      remoteFolder.fetch(
          new Message[] {storeMessage},
          fp,
          new MessageRetrievalListenerBridge(messageId, attachmentId, cb));

      // If we failed to load the attachment, throw an Exception here, so that
      // AttachmentDownloadService knows that we failed
      if (storePart.getBody() == null) {
        throw new MessagingException("Attachment not loaded.");
      }

      // Save the attachment to wherever it's going
      AttachmentUtilities.saveAttachment(
          mContext, storePart.getBody().getInputStream(), attachment);

      // 6. Report success
      cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.SUCCESS, 0);

    } catch (MessagingException me) {
      LogUtils.i(Logging.LOG_TAG, me, "Error loading attachment");

      final ContentValues cv = new ContentValues(1);
      cv.put(AttachmentColumns.UI_STATE, UIProvider.AttachmentState.FAILED);
      final Uri uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, attachmentId);
      mContext.getContentResolver().update(uri, cv, null, null);

      cb.loadAttachmentStatus(0, attachmentId, EmailServiceStatus.CONNECTION_ERROR, 0);
    } finally {
      if (remoteFolder != null) {
        remoteFolder.close(false);
      }
    }
  }
  public void setAccountPolicy(long accountId, Policy policy, String securityKey, boolean notify) {
    Account account = Account.restoreAccountWithId(mContext, accountId);
    // In case the account has been deleted, just return
    if (account == null) {
      return;
    }
    Policy oldPolicy = null;
    if (account.mPolicyKey > 0) {
      oldPolicy = Policy.restorePolicyWithId(mContext, account.mPolicyKey);
    }

    // If attachment policies have changed, fix up any affected attachment records
    if (oldPolicy != null && securityKey != null) {
      if ((oldPolicy.mDontAllowAttachments != policy.mDontAllowAttachments)
          || (oldPolicy.mMaxAttachmentSize != policy.mMaxAttachmentSize)) {
        Policy.setAttachmentFlagsForNewPolicy(mContext, account, policy);
      }
    }

    boolean policyChanged = (oldPolicy == null) || !oldPolicy.equals(policy);
    if (!policyChanged
        && (TextUtilities.stringOrNullEquals(securityKey, account.mSecuritySyncKey))) {
      LogUtils.d(Logging.LOG_TAG, "setAccountPolicy; policy unchanged");
    } else {
      setAccountPolicy(mContext, account, policy, securityKey);
      policiesUpdated();
    }

    boolean setHold = false;
    final NotificationController nc = NotificationControllerCreatorHolder.getInstance(mContext);
    if (policy.mProtocolPoliciesUnsupported != null) {
      // We can't support this, reasons in unsupportedRemotePolicies
      LogUtils.d(
          Logging.LOG_TAG, "Notify policies for " + account.mDisplayName + " not supported.");
      setHold = true;
      if (notify) {
        nc.showSecurityUnsupportedNotification(account);
      }
      // Erase data
      Uri uri = EmailProvider.uiUri("uiaccountdata", accountId);
      mContext.getContentResolver().delete(uri, null, null);
    } else if (isActive(policy)) {
      if (policyChanged) {
        LogUtils.d(Logging.LOG_TAG, "Notify policies for " + account.mDisplayName + " changed.");
        if (notify) {
          // Notify that policies changed
          nc.showSecurityChangedNotification(account);
        }
      } else {
        LogUtils.d(Logging.LOG_TAG, "Policy is active and unchanged; do not notify.");
      }
    } else {
      setHold = true;
      LogUtils.d(
          Logging.LOG_TAG,
          "Notify policies for " + account.mDisplayName + " are not being enforced.");
      if (notify) {
        // Put up a notification
        nc.showSecurityNeededNotification(account);
      }
    }
    // Set/clear the account hold.
    setAccountHoldFlag(mContext, account, setHold);
  }
  public static void reloadFolderList(Context context, long accountId, boolean force) {
    SyncManager exchangeService = INSTANCE;
    if (exchangeService == null) return;
    Cursor c =
        context
            .getContentResolver()
            .query(
                Mailbox.CONTENT_URI,
                Mailbox.CONTENT_PROJECTION,
                MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + "=?",
                new String[] {
                  Long.toString(accountId), Long.toString(Mailbox.TYPE_EAS_ACCOUNT_MAILBOX)
                },
                null);
    try {
      if (c.moveToFirst()) {
        synchronized (sSyncLock) {
          Mailbox mailbox = new Mailbox();
          mailbox.restore(c);
          Account acct = Account.restoreAccountWithId(context, accountId);
          if (acct == null) {
            reloadFolderListFailed(accountId);
            return;
          }
          String syncKey = acct.mSyncKey;
          // No need to reload the list if we don't have one
          if (!force && (syncKey == null || syncKey.equals("0"))) {
            reloadFolderListFailed(accountId);
            return;
          }

          // Change all ping/push boxes to push/hold
          ContentValues cv = new ContentValues();
          cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH_HOLD);
          context
              .getContentResolver()
              .update(
                  Mailbox.CONTENT_URI,
                  cv,
                  WHERE_PUSH_OR_PING_NOT_ACCOUNT_MAILBOX,
                  new String[] {Long.toString(accountId)});
          log("Set push/ping boxes to push/hold");

          long id = mailbox.mId;
          AbstractSyncService svc = exchangeService.mServiceMap.get(id);
          // Tell the service we're done
          if (svc != null) {
            synchronized (svc.getSynchronizer()) {
              svc.stop();
              // Interrupt the thread so that it can stop
              Thread thread = svc.mThread;
              if (thread != null) {
                thread.setName(thread.getName() + " (Stopped)");
                thread.interrupt();
              }
            }
            // Abandon the service
            exchangeService.releaseMailbox(id);
            // And have it start naturally
            kick("reload folder list");
          }
        }
      }
    } finally {
      c.close();
    }
  }
示例#15
0
  public static int searchMessages(
      Context context, long accountId, SearchParams searchParams, long destMailboxId) {
    // Sanity check for arguments
    final int offset = searchParams.mOffset;
    final int limit = searchParams.mLimit;
    final String filter = searchParams.mFilter;
    if (limit < 0 || limit > MAX_SEARCH_RESULTS || offset < 0) return 0;
    // TODO Should this be checked in UI?  Are there guidelines for minimums?
    if (filter == null || filter.length() < MIN_QUERY_LENGTH) return 0;

    int res = 0;
    final Account account = Account.restoreAccountWithId(context, accountId);
    if (account == null) return res;
    final EasSyncService svc = EasSyncService.setupServiceForAccount(context, account);
    if (svc == null) return res;
    final Mailbox searchMailbox = Mailbox.restoreMailboxWithId(context, destMailboxId);
    // Sanity check; account might have been deleted?
    if (searchMailbox == null) return res;
    final ContentValues statusValues = new ContentValues(2);
    try {
      // Set the status of this mailbox to indicate query
      statusValues.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.LIVE_QUERY);
      searchMailbox.update(context, statusValues);

      svc.mMailbox = searchMailbox;
      svc.mAccount = account;
      final Serializer s = new Serializer();
      s.start(Tags.SEARCH_SEARCH).start(Tags.SEARCH_STORE);
      s.data(Tags.SEARCH_NAME, "Mailbox");
      s.start(Tags.SEARCH_QUERY).start(Tags.SEARCH_AND);
      s.data(Tags.SYNC_CLASS, "Email");

      // If this isn't an inbox search, then include the collection id
      final Mailbox inbox = Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_INBOX);
      if (inbox == null) return 0;
      if (searchParams.mMailboxId != inbox.mId) {
        s.data(Tags.SYNC_COLLECTION_ID, inbox.mServerId);
      }

      s.data(Tags.SEARCH_FREE_TEXT, filter);

      // Add the date window if appropriate
      if (searchParams.mStartDate != null) {
        s.start(Tags.SEARCH_GREATER_THAN);
        s.tag(Tags.EMAIL_DATE_RECEIVED);
        s.data(Tags.SEARCH_VALUE, Eas.DATE_FORMAT.format(searchParams.mStartDate));
        s.end(); // SEARCH_GREATER_THAN
      }
      if (searchParams.mEndDate != null) {
        s.start(Tags.SEARCH_LESS_THAN);
        s.tag(Tags.EMAIL_DATE_RECEIVED);
        s.data(Tags.SEARCH_VALUE, Eas.DATE_FORMAT.format(searchParams.mEndDate));
        s.end(); // SEARCH_LESS_THAN
      }
      s.end().end(); // SEARCH_AND, SEARCH_QUERY
      s.start(Tags.SEARCH_OPTIONS);
      if (offset == 0) {
        s.tag(Tags.SEARCH_REBUILD_RESULTS);
      }
      if (searchParams.mIncludeChildren) {
        s.tag(Tags.SEARCH_DEEP_TRAVERSAL);
      }
      // Range is sent in the form first-last (e.g. 0-9)
      s.data(Tags.SEARCH_RANGE, offset + "-" + (offset + limit - 1));
      s.start(Tags.BASE_BODY_PREFERENCE);
      s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML);
      s.data(Tags.BASE_TRUNCATION_SIZE, "20000");
      s.end(); // BASE_BODY_PREFERENCE
      s.end().end().end().done(); // SEARCH_OPTIONS, SEARCH_STORE, SEARCH_SEARCH
      final EasResponse resp = svc.sendHttpClientPost("Search", s.toByteArray());
      try {
        final int code = resp.getStatus();
        if (code == HttpStatus.SC_OK) {
          final InputStream is = resp.getInputStream();
          try {
            final SearchParser sp = new SearchParser(is, svc, filter);
            sp.parse();
            res = sp.getTotalResults();
            /** M: Set and update mailbox flag. @{ */
            int currentCount = offset + sp.getCurrentResults();
            boolean allMessagesLoaded = false;
            if (currentCount >= res) {
              allMessagesLoaded = true;
            }
            searchMailbox.updateAllMessageDownloadFlag(context, allMessagesLoaded);
            /** @} */
          } finally {
            is.close();
          }
        } else {
          svc.userLog("Search returned " + code);
        }
      } finally {
        resp.close();
      }
    } catch (IOException e) {
      svc.userLog("Search exception " + e);
    } finally {
      // TODO: Handle error states
      // Set the status of this mailbox to indicate query over
      statusValues.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
      statusValues.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
      searchMailbox.update(context, statusValues);
    }
    // Return the total count
    return res;
  }