Beispiel #1
0
  public long getSize() throws MessagingException {

    final StorageManager storageManager = StorageManager.getInstance(context);

    final File attachmentDirectory =
        storageManager.getAttachmentDirectory(uUid, database.getStorageProviderId());

    return database.execute(
        false,
        new DbCallback<Long>() {
          @Override
          public Long doDbWork(final SQLiteDatabase db) {
            final File[] files = attachmentDirectory.listFiles();
            long attachmentLength = 0;
            if (files != null) {
              for (File file : files) {
                if (file.exists()) {
                  attachmentLength += file.length();
                }
              }
            }

            final File dbFile = storageManager.getDatabase(uUid, database.getStorageProviderId());
            return dbFile.length() + attachmentLength;
          }
        });
  }
Beispiel #2
0
  /**
   * local://localhost/path/to/database/uuid.db This constructor is only used by {@link
   * LocalStore#getInstance(Account, Context)}
   *
   * @throws UnavailableStorageException if not {@link StorageProvider#isReady(Context)}
   */
  private LocalStore(final Account account, final Context context) throws MessagingException {
    mAccount = account;
    database = new LockableDatabase(context, account.getUuid(), new StoreSchemaDefinition(this));

    this.context = context;
    mContentResolver = context.getContentResolver();
    database.setStorageProviderId(account.getLocalStorageProviderId());
    uUid = account.getUuid();

    messagePreviewCreator = MessagePreviewCreator.newInstance();
    messageFulltextCreator = MessageFulltextCreator.newInstance();
    attachmentCounter = new AttachmentCounter();

    database.open();
  }
Beispiel #3
0
  public AttachmentInfo getAttachmentInfo(final String attachmentId) throws MessagingException {
    return database.execute(
        false,
        new DbCallback<AttachmentInfo>() {
          @Override
          public AttachmentInfo doDbWork(final SQLiteDatabase db) throws WrappedException {
            Cursor cursor =
                db.query(
                    "message_parts",
                    new String[] {"display_name", "decoded_body_size", "mime_type"},
                    "id = ?",
                    new String[] {attachmentId},
                    null,
                    null,
                    null);
            try {
              if (!cursor.moveToFirst()) {
                return null;
              }
              String name = cursor.getString(0);
              long size = cursor.getLong(1);
              String mimeType = cursor.getString(2);

              final AttachmentInfo attachmentInfo = new AttachmentInfo();
              attachmentInfo.name = name;
              attachmentInfo.size = size;
              attachmentInfo.type = mimeType;

              return attachmentInfo;
            } finally {
              cursor.close();
            }
          }
        });
  }
Beispiel #4
0
  public InputStream getAttachmentInputStream(final String attachmentId) throws MessagingException {
    return database.execute(
        false,
        new DbCallback<InputStream>() {
          @Override
          public InputStream doDbWork(final SQLiteDatabase db) throws WrappedException {
            Cursor cursor =
                db.query(
                    "message_parts",
                    new String[] {"data_location", "data", "encoding"},
                    "id = ?",
                    new String[] {attachmentId},
                    null,
                    null,
                    null);
            try {
              if (!cursor.moveToFirst()) {
                return null;
              }

              int location = cursor.getInt(0);
              String encoding = cursor.getString(2);

              InputStream rawInputStream =
                  getRawAttachmentInputStream(cursor, location, attachmentId);
              return getDecodingInputStream(rawInputStream, encoding);
            } finally {
              cursor.close();
            }
          }
        });
  }
Beispiel #5
0
  /*
   * Given a query string, actually do the query for the messages and
   * call the MessageRetrievalListener for each one
   */
  List<LocalMessage> getMessages(
      final MessageRetrievalListener<LocalMessage> listener,
      final LocalFolder folder,
      final String queryString,
      final String[] placeHolders)
      throws MessagingException {
    final List<LocalMessage> messages = new ArrayList<>();
    final int j =
        database.execute(
            false,
            new DbCallback<Integer>() {
              @Override
              public Integer doDbWork(final SQLiteDatabase db) throws WrappedException {
                Cursor cursor = null;
                int i = 0;
                try {
                  cursor = db.rawQuery(queryString + " LIMIT 10", placeHolders);

                  while (cursor.moveToNext()) {
                    LocalMessage message = new LocalMessage(LocalStore.this, null, folder);
                    message.populateFromGetMessageCursor(cursor);

                    messages.add(message);
                    if (listener != null) {
                      listener.messageFinished(message, i, -1);
                    }
                    i++;
                  }
                  cursor.close();
                  cursor = db.rawQuery(queryString + " LIMIT -1 OFFSET 10", placeHolders);

                  while (cursor.moveToNext()) {
                    LocalMessage message = new LocalMessage(LocalStore.this, null, folder);
                    message.populateFromGetMessageCursor(cursor);

                    messages.add(message);
                    if (listener != null) {
                      listener.messageFinished(message, i, -1);
                    }
                    i++;
                  }
                } catch (Exception e) {
                  Log.d(K9.LOG_TAG, "Got an exception", e);
                } finally {
                  Utility.closeQuietly(cursor);
                }
                return i;
              }
            });
    if (listener != null) {
      listener.messagesFinished(j);
    }

    return Collections.unmodifiableList(messages);
  }
Beispiel #6
0
 public void removePendingCommand(final PendingCommand command) throws MessagingException {
   database.execute(
       false,
       new DbCallback<Void>() {
         @Override
         public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
           db.delete("pending_commands", "id = ?", new String[] {Long.toString(command.mId)});
           return null;
         }
       });
 }
Beispiel #7
0
 public void removePendingCommands() throws MessagingException {
   database.execute(
       false,
       new DbCallback<Void>() {
         @Override
         public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
           db.delete("pending_commands", null, null);
           return null;
         }
       });
 }
Beispiel #8
0
  public void compact() throws MessagingException {
    if (K9.DEBUG) Log.i(K9.LOG_TAG, "Before compaction size = " + getSize());

    database.execute(
        false,
        new DbCallback<Void>() {
          @Override
          public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
            db.execSQL("VACUUM");
            return null;
          }
        });
    if (K9.DEBUG) Log.i(K9.LOG_TAG, "After compaction size = " + getSize());
  }
Beispiel #9
0
  private void markAllMessagePartsDataAsMissing() throws MessagingException {
    database.execute(
        false,
        new DbCallback<Void>() {
          @Override
          public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
            ContentValues cv = new ContentValues();
            cv.put("data_location", DataLocation.MISSING);
            db.update("message_parts", cv, null, null);

            return null;
          }
        });
  }
Beispiel #10
0
  /**
   * Split database operations with a large set of arguments into multiple SQL statements.
   *
   * <p>At the time of this writing (2012-12-06) SQLite only supports around 1000 arguments. That's
   * why we have to split SQL statements with a large set of arguments into multiple SQL statements
   * each working on a subset of the arguments.
   *
   * @param selectionCallback Supplies the argument set and the code to query/update the database.
   * @param batchSize The maximum size of the selection set in each SQL statement.
   * @throws MessagingException
   */
  public void doBatchSetSelection(final BatchSetSelection selectionCallback, final int batchSize)
      throws MessagingException {

    final List<String> selectionArgs = new ArrayList<>();
    int start = 0;

    while (start < selectionCallback.getListSize()) {
      final StringBuilder selection = new StringBuilder();

      selection.append(" IN (");

      int count = Math.min(selectionCallback.getListSize() - start, batchSize);

      for (int i = start, end = start + count; i < end; i++) {
        if (i > start) {
          selection.append(",?");
        } else {
          selection.append("?");
        }

        selectionArgs.add(selectionCallback.getListItem(i));
      }

      selection.append(")");

      try {
        database.execute(
            true,
            new DbCallback<Void>() {
              @Override
              public Void doDbWork(final SQLiteDatabase db)
                  throws WrappedException, UnavailableStorageException {

                selectionCallback.doDbWork(
                    db, selection.toString(), selectionArgs.toArray(EMPTY_STRING_ARRAY));

                return null;
              }
            });

        selectionCallback.postDbWork();

      } catch (WrappedException e) {
        throw (MessagingException) e.getCause();
      }

      selectionArgs.clear();
      start += count;
    }
  }
Beispiel #11
0
 public void resetVisibleLimits(int visibleLimit) throws MessagingException {
   final ContentValues cv = new ContentValues();
   cv.put("visible_limit", Integer.toString(visibleLimit));
   cv.put("more_messages", MoreMessages.UNKNOWN.getDatabaseName());
   database.execute(
       false,
       new DbCallback<Void>() {
         @Override
         public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
           db.update("folders", cv, null, null);
           return null;
         }
       });
 }
Beispiel #12
0
  public void createFolders(final List<LocalFolder> foldersToCreate, final int visibleLimit)
      throws MessagingException {
    database.execute(
        true,
        new DbCallback<Void>() {
          @Override
          public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
            for (LocalFolder folder : foldersToCreate) {
              String name = folder.getName();
              final LocalFolder.PreferencesHolder prefHolder = folder.new PreferencesHolder();

              // When created, special folders should always be displayed
              // inbox should be integrated
              // and the inbox and drafts folders should be syncced by default
              if (mAccount.isSpecialFolder(name)) {
                prefHolder.inTopGroup = true;
                prefHolder.displayClass = LocalFolder.FolderClass.FIRST_CLASS;
                if (name.equalsIgnoreCase(mAccount.getInboxFolderName())) {
                  prefHolder.integrate = true;
                  prefHolder.notifyClass = LocalFolder.FolderClass.FIRST_CLASS;
                  prefHolder.pushClass = LocalFolder.FolderClass.FIRST_CLASS;
                } else {
                  prefHolder.pushClass = LocalFolder.FolderClass.INHERITED;
                }
                if (name.equalsIgnoreCase(mAccount.getInboxFolderName())
                    || name.equalsIgnoreCase(mAccount.getDraftsFolderName())) {
                  prefHolder.syncClass = LocalFolder.FolderClass.FIRST_CLASS;
                } else {
                  prefHolder.syncClass = LocalFolder.FolderClass.NO_CLASS;
                }
              }
              folder.refresh(name, prefHolder); // Recover settings from Preferences

              db.execSQL(
                  "INSERT INTO folders (name, visible_limit, top_group, display_class, poll_class, notify_class, push_class, integrate) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
                  new Object[] {
                    name,
                    visibleLimit,
                    prefHolder.inTopGroup ? 1 : 0,
                    prefHolder.displayClass.name(),
                    prefHolder.syncClass.name(),
                    prefHolder.notifyClass.name(),
                    prefHolder.pushClass.name(),
                    prefHolder.integrate ? 1 : 0,
                  });
            }
            return null;
          }
        });
  }
Beispiel #13
0
  private void deleteAllMessagePartsDataFromDisk() {
    final StorageManager storageManager = StorageManager.getInstance(context);
    File attachmentDirectory =
        storageManager.getAttachmentDirectory(uUid, database.getStorageProviderId());
    File[] files = attachmentDirectory.listFiles();
    if (files == null) {
      return;
    }

    for (File file : files) {
      if (file.exists() && !file.delete()) {
        file.deleteOnExit();
      }
    }
  }
Beispiel #14
0
 public int getFolderCount() throws MessagingException {
   return database.execute(
       false,
       new DbCallback<Integer>() {
         @Override
         public Integer doDbWork(final SQLiteDatabase db) {
           Cursor cursor = null;
           try {
             cursor = db.rawQuery("SELECT COUNT(*) FROM folders", null);
             cursor.moveToFirst();
             return cursor.getInt(0); // folder count
           } finally {
             Utility.closeQuietly(cursor);
           }
         }
       });
 }
Beispiel #15
0
 public void addPendingCommand(PendingCommand command) throws MessagingException {
   for (int i = 0; i < command.arguments.length; i++) {
     command.arguments[i] = UrlEncodingHelper.encodeUtf8(command.arguments[i]);
   }
   final ContentValues cv = new ContentValues();
   cv.put("command", command.command);
   cv.put("arguments", Utility.combine(command.arguments, ','));
   database.execute(
       false,
       new DbCallback<Void>() {
         @Override
         public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
           db.insert("pending_commands", "command", cv);
           return null;
         }
       });
 }
Beispiel #16
0
  // TODO this takes about 260-300ms, seems slow.
  @Override
  public List<LocalFolder> getPersonalNamespaces(boolean forceListAll) throws MessagingException {
    final List<LocalFolder> folders = new LinkedList<>();
    try {
      database.execute(
          false,
          new DbCallback<List<? extends Folder>>() {
            @Override
            public List<? extends Folder> doDbWork(final SQLiteDatabase db)
                throws WrappedException {
              Cursor cursor = null;

              try {
                cursor =
                    db.rawQuery(
                        "SELECT " + GET_FOLDER_COLS + " FROM folders " + "ORDER BY name ASC", null);
                while (cursor.moveToNext()) {
                  if (cursor.isNull(FOLDER_ID_INDEX)) {
                    continue;
                  }
                  String folderName = cursor.getString(FOLDER_NAME_INDEX);
                  LocalFolder folder = new LocalFolder(LocalStore.this, folderName);
                  folder.open(cursor);

                  folders.add(folder);
                }
                return folders;
              } catch (MessagingException e) {
                throw new WrappedException(e);
              } finally {
                Utility.closeQuietly(cursor);
              }
            }
          });
    } catch (WrappedException e) {
      throw (MessagingException) e.getCause();
    }
    return folders;
  }
Beispiel #17
0
  public void clear() throws MessagingException {
    if (K9.DEBUG) Log.i(K9.LOG_TAG, "Before prune size = " + getSize());

    deleteAllMessageDataFromDisk();
    if (K9.DEBUG) {
      Log.i(K9.LOG_TAG, "After prune / before compaction size = " + getSize());

      Log.i(K9.LOG_TAG, "Before clear folder count = " + getFolderCount());
      Log.i(K9.LOG_TAG, "Before clear message count = " + getMessageCount());

      Log.i(K9.LOG_TAG, "After prune / before clear size = " + getSize());
    }

    database.execute(
        false,
        new DbCallback<Void>() {
          @Override
          public Void doDbWork(final SQLiteDatabase db) {
            // We don't care about threads of deleted messages, so delete the whole table.
            db.delete("threads", null, null);

            // Don't delete deleted messages. They are essentially placeholders for UIDs of messages
            // that have
            // been deleted locally.
            db.delete("messages", "deleted = 0", null);
            return null;
          }
        });

    compact();

    if (K9.DEBUG) {
      Log.i(K9.LOG_TAG, "After clear message count = " + getMessageCount());

      Log.i(K9.LOG_TAG, "After clear size = " + getSize());
    }
  }
Beispiel #18
0
 public List<PendingCommand> getPendingCommands() throws MessagingException {
   return database.execute(
       false,
       new DbCallback<List<PendingCommand>>() {
         @Override
         public List<PendingCommand> doDbWork(final SQLiteDatabase db) throws WrappedException {
           Cursor cursor = null;
           try {
             cursor =
                 db.query(
                     "pending_commands",
                     new String[] {"id", "command", "arguments"},
                     null,
                     null,
                     null,
                     null,
                     "id ASC");
             List<PendingCommand> commands = new ArrayList<>();
             while (cursor.moveToNext()) {
               PendingCommand command = new PendingCommand();
               command.mId = cursor.getLong(0);
               command.command = cursor.getString(1);
               String arguments = cursor.getString(2);
               command.arguments = arguments.split(",");
               for (int i = 0; i < command.arguments.length; i++) {
                 command.arguments[i] = Utility.fastUrlDecode(command.arguments[i]);
               }
               commands.add(command);
             }
             return commands;
           } finally {
             Utility.closeQuietly(cursor);
           }
         }
       });
 }
Beispiel #19
0
 File getAttachmentFile(String attachmentId) {
   final StorageManager storageManager = StorageManager.getInstance(context);
   final File attachmentDirectory =
       storageManager.getAttachmentDirectory(uUid, database.getStorageProviderId());
   return new File(attachmentDirectory, attachmentId);
 }
Beispiel #20
0
 public void delete() throws UnavailableStorageException {
   database.delete();
 }
Beispiel #21
0
 public void recreate() throws UnavailableStorageException {
   database.recreate();
 }
Beispiel #22
0
 public void switchLocalStorage(final String newStorageProviderId) throws MessagingException {
   database.switchProvider(newStorageProviderId);
 }