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.filter.php b/include/class.filter.php index 9a1caf2b1b0792c9cea14d9cbf28cf4a458ea0ce..9b9d7279052ad0e5624642cb9f9f1c2ed127a6c5 100644 --- a/include/class.filter.php +++ b/include/class.filter.php @@ -38,7 +38,7 @@ class Filter { return false; $this->ht=db_fetch_array($res); - $this->id=$info['id']; + $this->id=$this->ht['id']; return true; } @@ -589,16 +589,46 @@ class EmailFilter { array_push($this->filters, new Filter($id)); return $this->filters; } + /** + * Fetches the short list of filters that match the email received in the + * constructor. This function is memoized so subsequent calls will + * return immediately. + */ + function getMatchingFilterList() { + if (!isset($this->short_list)) { + $this->short_list = array(); + foreach ($this->filters as $filter) + if ($filter->matches($this->email)) + $this->short_list[] = $filter; + } + return $this->short_list; + } + /** + * Determine if the filters that match the received email indicate that + * the email should be rejected + * + * Returns FALSE if the email should be acceptable. If the email should + * be rejected, the first filter that matches and has rejectEmail set is + * returned. + */ + function shouldReject() { + foreach ($this->getMatchingFilterList() as $filter) { + # Set reject if this filter indicates that the email should + # be blocked; however, don't unset $reject, because if it + # was set by another rule that did not set stopOnMatch(), we + # should still honor its configuration + if ($filter->rejectEmail()) return $filter; + } + return false; + } /** * Determine if any filters match the received email, and if so, apply * actions defined in those filters to the ticket-to-be-created. */ function apply(&$ticket) { - foreach ($this->filters as $filter) { - if ($filter->matches($this->email)) { - $filter->apply($ticket, $this->email); - if ($filter->stopOnMatch()) break; - } + foreach ($this->getMatchingFilterList() as $filter) { + $filter->apply($ticket, $this->email); + if ($filter->stopOnMatch()) break; } } diff --git a/include/class.ticket.php b/include/class.ticket.php index edb9ac4608cd6df114bebd62c798d4b205b443aa..2c111cec2f5a733716c73d7c2a03402690f7ff10 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; } @@ -1627,12 +1622,21 @@ class Ticket{ function create($vars,&$errors, $origin, $autorespond=true, $alertstaff=true) { global $cfg,$thisclient,$_FILES; - //Make sure the email is not banned + //Make sure the email address is not banned if ($vars['email'] && EmailFilter::isBanned($vars['email'])) { $errors['err']='Ticket denied. Error #403'; Sys::log(LOG_WARNING,'Ticket denied','Banned email - '.$vars['email']); return 0; - } + } + // Make sure email contents should not be rejected + if (($email_filter=new EmailFilter($vars)) + && ($filter=$email_filter->shouldReject())) { + $errors['err']='Ticket denied. Error #403'; + Sys::log(LOG_WARNING,'Ticket denied', + sprintf('Banned email - %s by filter "%s"', $vars['email'], + $filter->getName())); + return 0; + } $id=0; $fields=array(); @@ -1692,7 +1696,7 @@ class Ticket{ } # Perform email filter actions on the new ticket arguments XXX: Move filter to the top and check for reject... - if (!$errors && $ef = new EmailFilter($vars)) $ef->apply($vars); + if (!$errors && $email_filter) $email_filter->apply($vars); # Some things will need to be unpacked back into the scope of this # function