diff --git a/include/class.canned.php b/include/class.canned.php
index 00c46de3365e92fef38fe29e154c56fafacb622b..6c6c1ac1c75bcbc25b7c288c2e49909e30b4876a 100644
--- a/include/class.canned.php
+++ b/include/class.canned.php
@@ -143,30 +143,13 @@ class Canned {
         return $i;
     }
 
-    function deleteAttachment($fileId) {
-        
-        $sql='DELETE FROM '.CANNED_ATTACHMENT_TABLE
-             .' WHERE canned_id='.db_input($this->getId())
-             .' AND file_id='.db_input($fileId)
-             .' LIMIT 1';
-       
-        if(!db_query($sql) || !db_affected_rows())
-            return false;
-
-
-        if(($file=AttachmentFile::lookup($fileId)) && !$file->isInuse())
-            $file->delete();
-
-        return true;
-    }
-
     function deleteAttachments(){
 
         $deleted=0;
-        if(($attachments = $this->getAttachments())) {
-            foreach($attachments as $attachment)
-                if($attachment['id'] && $this->deleteAttachment($attachment['id']))
-                    $deleted++;
+        $sql='DELETE FROM '.CANNED_ATTACHMENT_TABLE
+            .' WHERE canned_id='.db_input($this->getId());
+        if(db_query($sql) && db_affected_rows()) {
+            $deleted = AttachmentFile::deleteOrphans();
         }
 
         return $deleted;
diff --git a/include/class.cron.php b/include/class.cron.php
index 3fd2d652cad8551ffa9852cd3d1f8984f060d542..ddc4d2973e77ff93f6f83a4a3fefd6b4b9b82814 100644
--- a/include/class.cron.php
+++ b/include/class.cron.php
@@ -34,10 +34,16 @@ class Cron {
         Sys::purgeLogs();
     }
 
+    function CleanOrphanedFiles() {
+        require_once(INCLUDE_DIR.'class.file.php');
+        AttachmentFile::deleteOrphans();
+    }
+
     function run(){ //called by outside cron NOT autocron
-        Cron::MailFetcher();
-        Cron::TicketMonitor();
-        cron::PurgeLogs();
+        self::MailFetcher();
+        self::TicketMonitor();
+        self::PurgeLogs();
+        self::CleanOrphanedFiles();
     }
 }
 ?>
diff --git a/include/class.faq.php b/include/class.faq.php
index 63b0848e469c7b3b1df6dbb9d61e27fca2c28118..99d6def270ef2f54340dda2cea0f9929a8712f5e 100644
--- a/include/class.faq.php
+++ b/include/class.faq.php
@@ -217,29 +217,13 @@ class FAQ {
         return $i;
     }
 
-    function deleteAttachment($fileId) {
-
-        $sql='DELETE FROM '.FAQ_ATTACHMENT_TABLE
-             .' WHERE faq_id='.db_input($this->getId())
-             .' AND file_id='.db_input($fileId)
-             .' LIMIT 1';
-
-        if(!db_query($sql) || !db_affected_rows())
-            return false;
-
-        if(($file=AttachmentFile::lookup($fileId)) && !$file->isInuse())
-            $file->delete();
-
-        return true;
-    }
-
     function deleteAttachments(){
 
         $deleted=0;
-        if(($attachments = $this->getAttachments())) {
-            foreach($attachments as $attachment)
-                if($attachment['id'] && $this->deleteAttachment($attachment['id']))
-                    $deleted++;
+        $sql='DELETE FROM '.FAQ_ATTACHMENT_TABLE
+            .' WHERE faq_id='.db_input($this->getId());
+        if(db_query($sql) && db_affected_rows()) {
+            $deleted = AttachmentFile::deleteOrphans();
         }
 
         return $deleted;
diff --git a/include/class.file.php b/include/class.file.php
index af38bfd59e43b45471dc35d8e1a256678c05588a..91bd1cddce71f1438580bdffd9bf9782ffe125e9 100644
--- a/include/class.file.php
+++ b/include/class.file.php
@@ -193,6 +193,24 @@ class AttachmentFile {
         
         return ($id && ($file = new AttachmentFile($id)) && $file->getId()==$id)?$file:null;
     }
+    /**
+     * Removes files and associated meta-data for files which no ticket,
+     * canned-response, or faq point to any more.
+     */
+    /* static */ function deleteOrphans() {
+        $res=db_query(
+            'DELETE FROM '.FILE_TABLE.' WHERE id NOT IN ('
+                # DISTINCT implies sort and may not be necessary
+                .'SELECT DISTINCT(file_id) FROM ('
+                    .'SELECT file_id FROM '.TICKET_ATTACHMENT_TABLE
+                    .' UNION ALL '
+                    .'SELECT file_id FROM '.CANNED_ATTACHMENT_TABLE
+                    .' UNION ALL '
+                    .'SELECT file_id FROM '.FAQ_ATTACHMENT_TABLE
+                .') still_loved'
+            .')');
+        return db_affected_rows();
+    }
 }
 
 class AttachmentList {
diff --git a/include/class.ticket.php b/include/class.ticket.php
index edb9ac4608cd6df114bebd62c798d4b205b443aa..20d8734cdf2e3837930e26e660fb9aacf142f197 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -1424,15 +1424,10 @@ class Ticket{
         global $cfg;
 
         $deleted=0;
-        if(($attachments = $this->getAttachments())) {
-            //Clear reference table - XXX: some attachments might be orphaned
-            db_query('DELETE FROM '.TICKET_ATTACHMENT_TABLE.' WHERE ticket_id='.db_input($this->getId()));
-            //Delete file from DB IF NOT inuse.
-            foreach($attachments as $attachment) {
-                if(($file=AttachmentFile::lookup($attachment['file_id'])) && !$file->isInuse() && $file->delete())
-                    $deleted++;
-            }
-        }
+        // Clear reference table
+        $res=db_query('DELETE FROM '.TICKET_ATTACHMENT_TABLE.' WHERE ticket_id='.db_input($this->getId()));
+        if ($res && db_affected_rows())
+            $deleted = AttachmentFile::deleteOrphans();
 
         return $deleted;
     }