Exemplo n.º 1
0
  private void moveOrCopyMessages(
      List<? extends Message> messages, String folderName, boolean isMove)
      throws MessagingException {
    String[] uids = new String[messages.size()];

    for (int i = 0, count = messages.size(); i < count; i++) {
      uids[i] = messages.get(i).getUid();
    }
    String messageBody;
    Map<String, String> headers = new HashMap<String, String>();
    Map<String, String> uidToUrl = getMessageUrls(uids);
    String[] urls = new String[uids.length];

    for (int i = 0, count = uids.length; i < count; i++) {
      urls[i] = uidToUrl.get(uids[i]);
      if (urls[i] == null && messages.get(i) instanceof WebDavMessage) {
        WebDavMessage wdMessage = (WebDavMessage) messages.get(i);
        urls[i] = wdMessage.getUrl();
      }
    }

    messageBody = store.getMoveOrCopyMessagesReadXml(urls, isMove);
    WebDavFolder destFolder = (WebDavFolder) store.getFolder(folderName);
    headers.put("Destination", destFolder.mFolderUrl);
    headers.put("Brief", "t");
    headers.put("If-Match", "*");
    String action = (isMove ? "BMOVE" : "BCOPY");
    Log.i(LOG_TAG, "Moving " + messages.size() + " messages to " + destFolder.mFolderUrl);

    store.processRequest(mFolderUrl, action, messageBody, headers, false);
  }
Exemplo n.º 2
0
  /**
   * Fetches and parses the message envelopes for the supplied messages. The idea is to have this be
   * recursive so that we do a series of medium calls instead of one large massive call or a large
   * number of smaller calls. Call it a happy balance
   */
  private void fetchEnvelope(
      List<WebDavMessage> startMessages, MessageRetrievalListener<WebDavMessage> listener)
      throws MessagingException {
    Map<String, String> headers = new HashMap<String, String>();
    String messageBody;
    String[] uids;
    List<WebDavMessage> messages = new ArrayList<WebDavMessage>(10);

    if (startMessages == null || startMessages.isEmpty()) {
      return;
    }

    if (startMessages.size() > 10) {
      List<WebDavMessage> newMessages = new ArrayList<WebDavMessage>(startMessages.size() - 10);
      for (int i = 0, count = startMessages.size(); i < count; i++) {
        if (i < 10) {
          messages.add(i, startMessages.get(i));
        } else {
          newMessages.add(i - 10, startMessages.get(i));
        }
      }

      fetchEnvelope(newMessages, listener);
    } else {
      messages.addAll(startMessages);
    }

    uids = new String[messages.size()];

    for (int i = 0, count = messages.size(); i < count; i++) {
      uids[i] = messages.get(i).getUid();
    }

    messageBody = store.getMessageEnvelopeXml(uids);
    headers.put("Brief", "t");
    DataSet dataset = store.processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);

    Map<String, ParsedMessageEnvelope> envelopes = dataset.getMessageEnvelopes();

    int count = messages.size();
    for (int i = messages.size() - 1; i >= 0; i--) {
      WebDavMessage message = messages.get(i);
      if (listener != null) {
        listener.messageStarted(messages.get(i).getUid(), i, count);
      }

      ParsedMessageEnvelope envelope = envelopes.get(message.getUid());
      if (envelope != null) {
        message.setNewHeaders(envelope);
        message.setFlagInternal(Flag.SEEN, envelope.getReadStatus());
      } else {
        Log.e(LOG_TAG, "Asked to get metadata for a non-existent message: " + message.getUid());
      }

      if (listener != null) {
        listener.messageFinished(messages.get(i), i, count);
      }
    }
  }
Exemplo n.º 3
0
  /**
   * Fetches and sets the message flags for the supplied messages. The idea is to have this be
   * recursive so that we do a series of medium calls instead of one large massive call or a large
   * number of smaller calls.
   */
  private void fetchFlags(
      List<WebDavMessage> startMessages, MessageRetrievalListener<WebDavMessage> listener)
      throws MessagingException {
    HashMap<String, String> headers = new HashMap<String, String>();
    String messageBody;
    List<Message> messages = new ArrayList<Message>(20);
    String[] uids;

    if (startMessages == null || startMessages.isEmpty()) {
      return;
    }

    if (startMessages.size() > 20) {
      List<WebDavMessage> newMessages = new ArrayList<WebDavMessage>(startMessages.size() - 20);
      for (int i = 0, count = startMessages.size(); i < count; i++) {
        if (i < 20) {
          messages.add(startMessages.get(i));
        } else {
          newMessages.add(startMessages.get(i));
        }
      }

      fetchFlags(newMessages, listener);
    } else {
      messages.addAll(startMessages);
    }

    uids = new String[messages.size()];

    for (int i = 0, count = messages.size(); i < count; i++) {
      uids[i] = messages.get(i).getUid();
    }

    messageBody = store.getMessageFlagsXml(uids);
    headers.put("Brief", "t");
    DataSet dataset = store.processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);

    if (dataset == null) {
      throw new MessagingException("Data Set from request was null");
    }

    Map<String, Boolean> uidToReadStatus = dataset.getUidToRead();

    for (int i = 0, count = messages.size(); i < count; i++) {
      if (!(messages.get(i) instanceof WebDavMessage)) {
        throw new MessagingException("WebDavStore fetch called with non-WebDavMessage");
      }
      WebDavMessage wdMessage = (WebDavMessage) messages.get(i);

      try {
        wdMessage.setFlagInternal(Flag.SEEN, uidToReadStatus.get(wdMessage.getUid()));
      } catch (NullPointerException e) {
        Log.v(
            LOG_TAG,
            "Under some weird circumstances, setting the read status when syncing from webdav threw an NPE. Skipping.");
      }
    }
  }
Exemplo n.º 4
0
  private Map<String, String> getMessageUrls(String[] uids) throws MessagingException {
    Map<String, String> headers = new HashMap<String, String>();
    String messageBody;

    /** Retrieve and parse the XML entity for our messages */
    messageBody = store.getMessageUrlsXml(uids);
    headers.put("Brief", "t");

    DataSet dataset = store.processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);

    return dataset.getUidToUrl();
  }
Exemplo n.º 5
0
  @Override
  public List<WebDavMessage> getMessages(
      int start, int end, Date earliestDate, MessageRetrievalListener<WebDavMessage> listener)
      throws MessagingException {
    List<WebDavMessage> messages = new ArrayList<WebDavMessage>();
    String[] uids;
    Map<String, String> headers = new HashMap<String, String>();
    int uidsLength;

    String messageBody;
    int prevStart = start;

    /** Reverse the message range since 0 index is newest */
    start = this.mMessageCount - end;
    end = start + (end - prevStart);

    if (start < 0 || end < 0 || end < start) {
      throw new MessagingException(
          String.format(Locale.US, "Invalid message set %d %d", start, end));
    }

    if (start == 0 && end < 10) {
      end = 10;
    }

    /** Verify authentication */
    messageBody = store.getMessagesXml();

    headers.put("Brief", "t");
    headers.put("Range", "rows=" + start + "-" + end);
    DataSet dataset = store.processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);
    uids = dataset.getUids();
    Map<String, String> uidToUrl = dataset.getUidToUrl();
    uidsLength = uids.length;

    for (int i = 0; i < uidsLength; i++) {
      if (listener != null) {
        listener.messageStarted(uids[i], i, uidsLength);
      }
      WebDavMessage message = new WebDavMessage(uids[i], this);
      message.setUrl(uidToUrl.get(uids[i]));
      messages.add(message);

      if (listener != null) {
        listener.messageFinished(message, i, uidsLength);
      }
    }

    return messages;
  }
Exemplo n.º 6
0
  private void markServerMessagesRead(String[] uids, boolean read) throws MessagingException {
    String messageBody;
    Map<String, String> headers = new HashMap<String, String>();
    Map<String, String> uidToUrl = getMessageUrls(uids);
    String[] urls = new String[uids.length];

    for (int i = 0, count = uids.length; i < count; i++) {
      urls[i] = uidToUrl.get(uids[i]);
    }

    messageBody = store.getMarkMessagesReadXml(urls, read);
    headers.put("Brief", "t");
    headers.put("If-Match", "*");

    store.processRequest(this.mFolderUrl, "BPROPPATCH", messageBody, headers, false);
  }
Exemplo n.º 7
0
  @Override
  public void fetch(
      List<WebDavMessage> messages,
      FetchProfile fp,
      MessageRetrievalListener<WebDavMessage> listener)
      throws MessagingException {
    if (messages == null || messages.isEmpty()) {
      return;
    }

    /** Fetch message envelope information for the array */
    if (fp.contains(FetchProfile.Item.ENVELOPE)) {
      fetchEnvelope(messages, listener);
    }
    /** Fetch message flag info for the array */
    if (fp.contains(FetchProfile.Item.FLAGS)) {
      fetchFlags(messages, listener);
    }

    if (fp.contains(FetchProfile.Item.BODY_SANE)) {
      int maximumAutoDownloadSize = store.getStoreConfig().getMaximumAutoDownloadMessageSize();
      if (maximumAutoDownloadSize > 0) {
        fetchMessages(messages, listener, (maximumAutoDownloadSize / 76));
      } else {
        fetchMessages(messages, listener, -1);
      }
    }
    if (fp.contains(FetchProfile.Item.BODY)) {
      fetchMessages(messages, listener, -1);
    }
  }
Exemplo n.º 8
0
  private void deleteServerMessages(String[] uids) throws MessagingException {
    Map<String, String> uidToUrl = getMessageUrls(uids);

    for (String uid : uids) {
      Map<String, String> headers = new HashMap<String, String>();
      String url = uidToUrl.get(uid);
      String destinationUrl = generateDeleteUrl(url);

      /** If the destination is the same as the origin, assume delete forever */
      if (destinationUrl.equals(url)) {
        headers.put("Brief", "t");
        store.processRequest(url, "DELETE", null, headers, false);
      } else {
        headers.put("Destination", generateDeleteUrl(url));
        headers.put("Brief", "t");
        store.processRequest(url, "MOVE", null, headers, false);
      }
    }
  }
Exemplo n.º 9
0
  private void buildFolderUrl() {
    String encodedName;
    String[] urlParts = this.mName.split("/");
    String url = "";
    for (int i = 0, count = urlParts.length; i < count; i++) {
      if (i != 0) {
        url = url + "/" + encodeUtf8(urlParts[i]);
      } else {
        url = encodeUtf8(urlParts[i]);
      }
    }
    encodedName = url;

    encodedName = encodedName.replaceAll("\\+", "%20");

    this.mFolderUrl = store.getUrl();
    if (!store.getUrl().endsWith("/")) {
      this.mFolderUrl += "/";
    }
    this.mFolderUrl += encodedName;
  }
Exemplo n.º 10
0
  private int getMessageCount(boolean read) throws MessagingException {
    String isRead;
    int messageCount = 0;
    Map<String, String> headers = new HashMap<String, String>();
    String messageBody;

    if (read) {
      isRead = "True";
    } else {
      isRead = "False";
    }

    messageBody = store.getMessageCountXml(isRead);
    headers.put("Brief", "t");
    DataSet dataset = store.processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);
    if (dataset != null) {
      messageCount = dataset.getMessageCount();
    }
    if (K9MailLib.isDebug() && DEBUG_PROTOCOL_WEBDAV) {
      Log.v(LOG_TAG, "Counted messages and webdav returned: " + messageCount);
    }

    return messageCount;
  }
Exemplo n.º 11
0
 @Override
 public void open(int mode) throws MessagingException {
   store.getHttpClient();
   this.mIsOpen = true;
 }
Exemplo n.º 12
0
  public List<? extends Message> appendWebDavMessages(List<? extends Message> messages)
      throws MessagingException {
    List<Message> retMessages = new ArrayList<Message>(messages.size());

    WebDavHttpClient httpclient = store.getHttpClient();

    for (Message message : messages) {
      HttpGeneric httpmethod;
      HttpResponse response;
      StringEntity bodyEntity;
      int statusCode;

      try {
        ByteArrayOutputStream out;

        out = new ByteArrayOutputStream(message.getSize());

        open(Folder.OPEN_MODE_RW);
        EOLConvertingOutputStream msgOut =
            new EOLConvertingOutputStream(new BufferedOutputStream(out, 1024));
        message.writeTo(msgOut);
        msgOut.flush();

        bodyEntity = new StringEntity(out.toString(), "UTF-8");
        bodyEntity.setContentType("message/rfc822");

        String messageURL = mFolderUrl;
        if (!messageURL.endsWith("/")) {
          messageURL += "/";
        }
        messageURL += encodeUtf8(message.getUid() + ":" + System.currentTimeMillis() + ".eml");

        Log.i(LOG_TAG, "Uploading message as " + messageURL);

        httpmethod = new HttpGeneric(messageURL);
        httpmethod.setMethod("PUT");
        httpmethod.setEntity(bodyEntity);

        String mAuthString = store.getAuthString();

        if (mAuthString != null) {
          httpmethod.setHeader("Authorization", mAuthString);
        }

        response = httpclient.executeOverride(httpmethod, store.getContext());
        statusCode = response.getStatusLine().getStatusCode();

        if (statusCode < 200 || statusCode > 300) {

          // TODO: Could we handle a login timeout here?

          throw new IOException(
              "Error with status code "
                  + statusCode
                  + " while sending/appending message.  Response = "
                  + response.getStatusLine().toString()
                  + " for message "
                  + messageURL);
        }
        WebDavMessage retMessage = new WebDavMessage(message.getUid(), this);

        retMessage.setUrl(messageURL);
        retMessages.add(retMessage);
      } catch (Exception e) {
        throw new MessagingException("Unable to append", e);
      }
    }
    return retMessages;
  }
Exemplo n.º 13
0
  private String generateDeleteUrl(String startUrl) {
    String[] urlParts = startUrl.split("/");
    String filename = urlParts[urlParts.length - 1];

    return store.getUrl() + "Deleted%20Items/" + filename;
  }
Exemplo n.º 14
0
  /**
   * Fetches the full messages or up to {@param lines} lines and passes them to the message parser.
   */
  private void fetchMessages(
      List<WebDavMessage> messages, MessageRetrievalListener<WebDavMessage> listener, int lines)
      throws MessagingException {
    WebDavHttpClient httpclient;
    httpclient = store.getHttpClient();

    /** We can't hand off to processRequest() since we need the stream to parse. */
    for (int i = 0, count = messages.size(); i < count; i++) {
      WebDavMessage wdMessage = messages.get(i);
      int statusCode = 0;

      if (listener != null) {
        listener.messageStarted(wdMessage.getUid(), i, count);
      }

      /**
       * If fetch is called outside of the initial list (ie, a locally stored message), it may not
       * have a URL associated. Verify and fix that
       */
      if (wdMessage.getUrl().equals("")) {
        wdMessage.setUrl(getMessageUrls(new String[] {wdMessage.getUid()}).get(wdMessage.getUid()));
        Log.i(
            LOG_TAG,
            "Fetching messages with UID = '"
                + wdMessage.getUid()
                + "', URL = '"
                + wdMessage.getUrl()
                + "'");
        if (wdMessage.getUrl().equals("")) {
          throw new MessagingException("Unable to get URL for message");
        }
      }

      try {
        Log.i(
            LOG_TAG,
            "Fetching message with UID = '"
                + wdMessage.getUid()
                + "', URL = '"
                + wdMessage.getUrl()
                + "'");
        HttpGet httpget = new HttpGet(new URI(wdMessage.getUrl()));
        HttpResponse response;
        HttpEntity entity;

        httpget.setHeader("translate", "f");
        if (store.getAuthentication() == WebDavConstants.AUTH_TYPE_BASIC) {
          httpget.setHeader("Authorization", store.getAuthString());
        }
        response = httpclient.executeOverride(httpget, store.getContext());

        statusCode = response.getStatusLine().getStatusCode();

        entity = response.getEntity();

        if (statusCode < 200 || statusCode > 300) {
          throw new IOException(
              "Error during with code "
                  + statusCode
                  + " during fetch: "
                  + response.getStatusLine().toString());
        }

        if (entity != null) {
          InputStream istream = null;
          StringBuilder buffer = new StringBuilder();
          String tempText;
          String resultText;
          BufferedReader reader = null;
          int currentLines = 0;

          try {
            istream = WebDavHttpClient.getUngzippedContent(entity);

            if (lines != -1) {
              // Convert the ungzipped input stream into a StringBuilder
              // containing the given line count
              reader = new BufferedReader(new InputStreamReader(istream), 8192);

              while ((tempText = reader.readLine()) != null && (currentLines < lines)) {
                buffer.append(tempText).append("\r\n");
                currentLines++;
              }

              IOUtils.closeQuietly(istream);

              resultText = buffer.toString();
              istream = new ByteArrayInputStream(resultText.getBytes("UTF-8"));
            }
            // Parse either the entire message stream, or a stream of the given lines
            wdMessage.parse(istream);

          } catch (IOException ioe) {
            Log.e(
                LOG_TAG,
                "IOException: "
                    + ioe.getMessage()
                    + "\nTrace: "
                    + WebDavUtils.processException(ioe));
            throw new MessagingException("I/O Error", ioe);
          } finally {
            IOUtils.closeQuietly(reader);
            IOUtils.closeQuietly(istream);
          }
        } else {
          Log.v(LOG_TAG, "Empty response");
        }

      } catch (IllegalArgumentException iae) {
        Log.e(
            LOG_TAG,
            "IllegalArgumentException caught "
                + iae
                + "\nTrace: "
                + WebDavUtils.processException(iae));
        throw new MessagingException("IllegalArgumentException caught", iae);
      } catch (URISyntaxException use) {
        Log.e(
            LOG_TAG,
            "URISyntaxException caught " + use + "\nTrace: " + WebDavUtils.processException(use));
        throw new MessagingException("URISyntaxException caught", use);
      } catch (IOException ioe) {
        Log.e(
            LOG_TAG,
            "Non-success response code loading message, response code was "
                + statusCode
                + "\nURL: "
                + wdMessage.getUrl()
                + "\nError: "
                + ioe.getMessage()
                + "\nTrace: "
                + WebDavUtils.processException(ioe));
        throw new MessagingException("Failure code " + statusCode, ioe);
      }

      if (listener != null) {
        listener.messageFinished(wdMessage, i, count);
      }
    }
  }