diff --git a/include/class.file.php b/include/class.file.php
index 84689f02011afd73cb2871582adb43aa5f3c07cb..a2ce70bfa1a56b33641eda172b40651619e067fb 100644
--- a/include/class.file.php
+++ b/include/class.file.php
@@ -307,21 +307,24 @@ class AttachmentFile {
                 = self::_getKeyAndHash($file['data']);
             if (!$file['key'])
                 $file['key'] = $key;
-
-            if (!isset($file['size']))
-                $file['size'] = strlen($file['data']);
         }
 
-        // Check and see if the file is already on record
-        $sql = 'SELECT id, `key` FROM '.FILE_TABLE
-            .' WHERE signature='.db_input($file['signature'])
-            .' AND size='.db_input($file['size']);
+        if (isset($file['size'])) {
+            // Check and see if the file is already on record
+            $sql = 'SELECT id, `key` FROM '.FILE_TABLE
+                .' WHERE signature='.db_input($file['signature'])
+                .' AND size='.db_input($file['size']);
 
-        // If the record exists in the database already, a file with the
-        // same hash and size is already on file -- just return its ID
-        if (list($id, $key) = db_fetch_row(db_query($sql))) {
-            $file['key'] = $key;
-            return $id;
+            // If the record exists in the database already, a file with the
+            // same hash and size is already on file -- just return its ID
+            if (list($id, $key) = db_fetch_row(db_query($sql))) {
+                $file['key'] = $key;
+                return $id;
+            }
+        }
+        elseif (!isset($file['data'])) {
+            // Unable to determine the file's size
+            return false;
         }
 
         if (!$file['type']) {
@@ -337,15 +340,16 @@ class AttachmentFile {
                 $file['type'] = 'application/octet-stream';
         }
 
-
         $sql='INSERT INTO '.FILE_TABLE.' SET created=NOW() '
             .',type='.db_input(strtolower($file['type']))
-            .',size='.db_input($file['size'])
             .',name='.db_input($file['name'])
             .',`key`='.db_input($file['key'])
             .',ft='.db_input($ft ?: 'T')
             .',signature='.db_input($file['signature']);
 
+        if (isset($file['size']))
+            $sql .= ',size='.db_input($file['size']);
+
         if (!(db_query($sql) && ($id = db_insert_id())))
             return false;
 
@@ -383,8 +387,23 @@ class AttachmentFile {
             return false;
         }
 
-        $sql = 'UPDATE '.FILE_TABLE.' SET bk='.db_input($bk->getBkChar())
-            .' WHERE id='.db_input($f->getId());
+        $sql = 'UPDATE '.FILE_TABLE.' SET bk='.db_input($bk->getBkChar());
+
+        if (!isset($file['size'])) {
+            if ($size = $bk->getSize())
+                $file['size'] = $size;
+            // Prefer mb_strlen, because mbstring.func_overload will
+            // automatically prefer it if configured.
+            elseif (extension_loaded('mbstring'))
+                $file['size'] = mb_strlen($file['data'], '8bit');
+            // bootstrap.php include a compat version of mb_strlen
+            else
+                $file['size'] = strlen($file['data']);
+
+            $sql .= ', `size`='.db_input($file['size']);
+        }
+
+        $sql .= ' WHERE id='.db_input($f->getId());
         db_query($sql);
 
         return $f->getId();
@@ -745,6 +764,18 @@ class FileStorageBackend {
     function getHashDigest($algo) {
         return false;
     }
+
+    /**
+     * getSize
+     *
+     * Retrieves the size of the contents written or available to be read.
+     * The backend should optimize this process if possible by keeping track
+     * of the bytes written in a way apart from `strlen`. This value will be
+     * used instead of inspecting the contents using `strlen`.
+     */
+    function getSize() {
+        return false;
+    }
 }
 
 
@@ -764,7 +795,7 @@ class AttachmentChunkedData extends FileStorageBackend {
         $this->_buffer = false;
     }
 
-    function length() {
+    function getSize() {
         list($length) = db_fetch_row(db_query(
              'SELECT SUM(LENGTH(filedata)) FROM '.FILE_CHUNK_TABLE
             .' WHERE file_id='.db_input($this->file->getId())));
@@ -788,11 +819,12 @@ class AttachmentChunkedData extends FileStorageBackend {
 
     function write($what, $chunk_size=CHUNK_SIZE) {
         $offset=0;
+        $what = bin2hex($what);
         for (;;) {
-            $block = substr($what, $offset, $chunk_size);
+            $block = substr($what, $offset, $chunk_size*2);
             if (!$block) break;
             if (!db_query('REPLACE INTO '.FILE_CHUNK_TABLE
-                    .' SET filedata=0x'.bin2hex($block).', file_id='
+                    .' SET filedata=0x'.$block.', file_id='
                     .db_input($this->file->getId()).', chunk_id='.db_input($this->_chunk++)))
                 return false;
             $offset += strlen($block);
diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php
index 644d866637383fb9faadfce643616a280e8e2d52..0b739e7c82eeb4a9a1668a096effcbc8ade8616e 100644
--- a/include/class.mailfetch.php
+++ b/include/class.mailfetch.php
@@ -462,6 +462,7 @@ class MailFetcher {
                         array(
                             'name'  => $this->mime_decode($filename),
                             'type'  => $this->getMimeType($part),
+                            'size' => $part->bytes ?: null,
                             'encoding' => $part->encoding,
                             'index' => ($index?$index:1),
                             'cid'   => $content_id,
diff --git a/include/class.mailparse.php b/include/class.mailparse.php
index f3feaf25d5e2678a4be426ab36e898dcd48782a2..378c68c1959e2a5c6ff4d442a9b8282c43c7aee3 100644
--- a/include/class.mailparse.php
+++ b/include/class.mailparse.php
@@ -396,6 +396,13 @@ class Mail_Parse {
             else
                 $file['data'] = $part->body;
 
+            // Capture filesize in order to support de-duplication
+            if (extension_loaded('mbstring'))
+                $file['size'] = mb_strlen($file['data'], '8bit');
+            // bootstrap.php include a compat version of mb_strlen
+            else
+                $file['size'] = strlen($file['data']);
+
             if(!$this->decode_bodies && $part->headers['content-transfer-encoding'])
                 $file['encoding'] = $part->headers['content-transfer-encoding'];