diff --git a/README.md b/README.md
index 6ff6cf1b40c0ecc9bb7fcd7e9259b26ae97ea71b..9946aeb3d85e6c3715b57f9347d10ccdc4edb244 100644
--- a/README.md
+++ b/README.md
@@ -18,10 +18,21 @@ easy to setup and use. The best part is, it's completely free.
 
 Installation
 ------------
-Download the
-[source](./zipball/master) from
-the GitHub page. Follow the instructions for installation on the osTicket
-[wiki](http://osticket.com/wiki/Installation).
+osTicket now supports bleeding-edge installations. The easiest way to
+install the software and track updates is to clone the public repository.
+Create a folder on you web server (using whatever method makes sense for
+you) and cd into it. Then clone the repository (the folder must be empty!):
+
+    git clone https://github.com/osTicket/osTicket-1.7 .
+
+osTicket uses the git flow development model, so you’ll need to switch to
+the develop branch in order to see the bleeding-edge feature additions.
+
+    git checkout develop 
+
+Follow the usual install instructions (beginning from Manual Installation
+above), except, don't delete the setup/ folder. For this reason, such an
+installation is not recommended for a public-facing support system.
 
 Help
 ----
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
diff --git a/setup/inc/class.setup.php b/setup/inc/class.setup.php
index 2660da6a260cc8afd6360f3f6aec4ea5f4131bf7..c4d986c655ad6041591f879f7e4c077ef24ec6b7 100644
--- a/setup/inc/class.setup.php
+++ b/setup/inc/class.setup.php
@@ -253,7 +253,7 @@ class Installer extends SetupWizard {
 
 
         //Staff's email can't be same as system emails.
-        if($vars['admin_email'] && $vars['email'] && !strcasecmp($vars['sysemail'],$vars['email']))
+        if($vars['admin_email'] && $vars['email'] && !strcasecmp($vars['admin_email'],$vars['email']))
             $this->errors['admin_email']='Conflicts with system email above';
         //Admin's pass confirmation. 
         if(!$this->errors && strcasecmp($vars['passwd'],$vars['passwd2']))
@@ -303,8 +303,8 @@ class Installer extends SetupWizard {
         if(!$this->errors) {
             //Create admin user.
             $sql='INSERT INTO '.PREFIX.'staff SET created=NOW() '
-                .', isactive=1, isadmin=1, group_id=1, dept_id=1, timezone_id=1 '
-                .', email='.db_input($_POST['email'])
+                .', isactive=1, isadmin=1, group_id=1, dept_id=1, timezone_id=8 '
+                .', email='.db_input($_POST['admin_email'])
                 .', firstname='.db_input($vars['fname'])
                 .', lastname='.db_input($vars['lname'])
                 .', username='.db_input($vars['username'])
@@ -318,7 +318,7 @@ class Installer extends SetupWizard {
             //XXX: rename ostversion  helpdesk_* ??
             $sql='INSERT INTO '.PREFIX.'config SET updated=NOW(), isonline=0 '
                 .', default_email_id=1, alert_email_id=2, default_dept_id=1 '
-                .', default_sla_id=1, default_timezone_id=1, default_template_id=1 '
+                .', default_sla_id=1, default_timezone_id=8, default_template_id=1 '
                 .', admin_email='.db_input($vars['admin_email'])
                 .', schema_signature='.db_input(md5_file($schemaFile))
                 .', helpdesk_url='.db_input(URL)
@@ -356,7 +356,8 @@ class Installer extends SetupWizard {
         @mysql_query($sql);
                    
         //Create a ticket to make the system warm and happy.
-        $sql='INSERT INTO '.PREFIX.'ticket SET created=NOW(), status="open", source="Web", priority_id=2, dept_id=1 '
+        $sql='INSERT INTO '.PREFIX.'ticket SET created=NOW(), status="open", source="Web" '
+            .' ,priority_id=2, dept_id=1, topic_id=1 '
             .' ,ticketID='.db_input(Misc::randNumber(6))
             .' ,email="support@osticket.com" '
             .' ,name="osTicket Support" '
diff --git a/setup/install.php b/setup/install.php
index e98c7aa7bf4791a3c2dd536b4722e3d4206a4a80..e7f68e9d5f203e3a6960fdb229449a48682021ea 100644
--- a/setup/install.php
+++ b/setup/install.php
@@ -39,7 +39,7 @@ if($_POST && $_POST['s']) {
             break;
         case 'config':
             if(!$installer->config_exists())
-                $errors['err']='Configuratin file does NOT exist. Follow steps below'.$installer->getConfigFile();
+                $errors['err']='Configuration file does NOT exist. Follow steps below to add one.';
             elseif(!$installer->config_writable())
                 $errors['err']='Write access required to continue';
             else
diff --git a/setup/js/tips.js b/setup/js/tips.js
index f24d82512d6b0bd6258ae8c674bce77bfaed0826..76c20421a28c9301a92497a778de881e1e4f91fe 100644
--- a/setup/js/tips.js
+++ b/setup/js/tips.js
@@ -1,60 +1,47 @@
-jQuery(function($) {
-    var tips = $('.tip');
-    for(i=0;i<tips.length;i++) {
-        tips[i].rel = 'tip-' + i;
-    }
-
-    $('.tip').live('mouseover click', function(e) {
-        e.preventDefault();
-        var tip_num = this.rel;
-
-        if($('.' + tip_num).length == 0) {
-
-            var elem = $(this);
-            var pos = elem.offset();
-
-            var y_pos = pos.top - 12;
-            var x_pos = pos.left + elem.width() + 20;
-
-            var tip_arrow = $('<img>').attr('src', './images/tip_arrow.png').addClass('tip_arrow');
-            var tip_box = $('<div>').addClass('tip_box');
-            var tip_content = $('<div>').addClass('tip_content').load('tips.html '+elem.attr('href'), function() {
-                tip_content.prepend('<a href="#" class="tip_close">x</a>');
-            });
-
-            var the_tip = tip_box.append(tip_arrow).append(tip_content);
-            the_tip.css({
-                "top":y_pos + "px",
-                "left":x_pos + "px"
-            }).addClass(tip_num);
-
-            tip_timer = setTimeout(function() {
-                $('.tip_box').remove();
-                $('body').append(the_tip.hide().fadeIn());
-            }, 500);
-
-            $('.' + tip_num + ' .tip_shadow').css({
-                "height":$('.' + tip_num).height() + 5
-            });
-        }
-    }).live('mouseout', function() {
-        clearTimeout(tip_timer);
-    });
-    $('body').delegate('.tip_close', 'click', function(e) {
-        e.preventDefault();
-        $(this).parent().parent().remove();
-    }).delegate('.tip_menu .assign', 'click', function(e) {
-        e.preventDefault();
-        elem = $(this).parent().parent().parent().parent();
-        $('.tip_body', elem).slideToggle(function() {
-            $('.assign_panel', elem).slideToggle();
-        });
-    }).delegate('.assign_panel .cancel', 'click', function(e) {
-        e.preventDefault();
-        elem = $(this).parent().parent().parent();
-        $('.assign_panel', elem).slideToggle(function() {
-            $('.tip_body', elem).slideToggle();
-        });
-    });
-
-});
+jQuery(function($) {
+    var tips = $('.tip');
+    for(i=0;i<tips.length;i++) {
+        tips[i].rel = 'tip-' + i;
+    }
+
+    $('.tip').live('mouseover click', function(e) {
+        e.preventDefault();
+        var tip_num = this.rel;
+
+        if($('.' + tip_num).length == 0) {
+
+            var elem = $(this);
+            var pos = elem.offset();
+
+            var y_pos = pos.top - 12;
+            var x_pos = pos.left + elem.width() + 20;
+
+            var tip_arrow = $('<img>').attr('src', './images/tip_arrow.png').addClass('tip_arrow');
+            var tip_box = $('<div>').addClass('tip_box');
+            var tip_content = $('<div>').addClass('tip_content').load('tips.html '+elem.attr('href'), function() {
+                tip_content.prepend('<a href="#" class="tip_close">x</a>');
+            });
+
+            var the_tip = tip_box.append(tip_arrow).append(tip_content);
+            the_tip.css({
+                "top":y_pos + "px",
+                "left":x_pos + "px"
+            }).addClass(tip_num);
+
+            tip_timer = setTimeout(function() {
+                $('.tip_box').remove();
+                $('body').append(the_tip.hide().fadeIn());
+            }, 500);
+
+            $('.' + tip_num + ' .tip_shadow').css({
+                "height":$('.' + tip_num).height() + 5
+            });
+        }
+    }).live('mouseout', function() {
+        clearTimeout(tip_timer);
+    });
+    $('body').delegate('.tip_close', 'click', function(e) {
+        e.preventDefault();
+        $(this).parent().parent().remove();
+    });
+});