diff --git a/setup/inc/class.migrater.php b/include/class.migrater.php
similarity index 100%
rename from setup/inc/class.migrater.php
rename to include/class.migrater.php
diff --git a/setup/inc/class.setup.php b/include/class.setup.php
similarity index 100%
rename from setup/inc/class.setup.php
rename to include/class.setup.php
diff --git a/setup/inc/class.upgrader.php b/include/class.upgrader.php
similarity index 100%
rename from setup/inc/class.upgrader.php
rename to include/class.upgrader.php
diff --git a/setup/inc/upgrade-aborted.inc.php b/include/upgrader/aborted.inc.php
similarity index 100%
rename from setup/inc/upgrade-aborted.inc.php
rename to include/upgrader/aborted.inc.php
diff --git a/setup/inc/upgrade-done.inc.php b/include/upgrader/done.inc.php
similarity index 100%
rename from setup/inc/upgrade-done.inc.php
rename to include/upgrader/done.inc.php
diff --git a/setup/inc/sql/02decaa2-60fcbee1.patch.sql b/include/upgrader/patches/02decaa2-60fcbee1.patch.sql
similarity index 100%
rename from setup/inc/sql/02decaa2-60fcbee1.patch.sql
rename to include/upgrader/patches/02decaa2-60fcbee1.patch.sql
diff --git a/setup/inc/sql/49478749-c2d2fabf.patch.sql b/include/upgrader/patches/49478749-c2d2fabf.patch.sql
similarity index 100%
rename from setup/inc/sql/49478749-c2d2fabf.patch.sql
rename to include/upgrader/patches/49478749-c2d2fabf.patch.sql
diff --git a/setup/inc/sql/522e5b78-02decaa2.patch.sql b/include/upgrader/patches/522e5b78-02decaa2.patch.sql
similarity index 100%
rename from setup/inc/sql/522e5b78-02decaa2.patch.sql
rename to include/upgrader/patches/522e5b78-02decaa2.patch.sql
diff --git a/setup/inc/sql/60fcbee1-f8856d56.patch.sql b/include/upgrader/patches/60fcbee1-f8856d56.patch.sql
similarity index 100%
rename from setup/inc/sql/60fcbee1-f8856d56.patch.sql
rename to include/upgrader/patches/60fcbee1-f8856d56.patch.sql
diff --git a/setup/inc/sql/7be60a84-522e5b78.patch.sql b/include/upgrader/patches/7be60a84-522e5b78.patch.sql
similarity index 100%
rename from setup/inc/sql/7be60a84-522e5b78.patch.sql
rename to include/upgrader/patches/7be60a84-522e5b78.patch.sql
diff --git a/setup/inc/sql/abe9c0cb-bbb021fb.patch.sql b/include/upgrader/patches/abe9c0cb-bbb021fb.patch.sql
similarity index 100%
rename from setup/inc/sql/abe9c0cb-bbb021fb.patch.sql
rename to include/upgrader/patches/abe9c0cb-bbb021fb.patch.sql
diff --git a/setup/inc/sql/bbb021fb-49478749.patch.sql b/include/upgrader/patches/bbb021fb-49478749.patch.sql
similarity index 100%
rename from setup/inc/sql/bbb021fb-49478749.patch.sql
rename to include/upgrader/patches/bbb021fb-49478749.patch.sql
diff --git a/setup/inc/sql/v1.6st-1.7-upgrade-mysql.sql b/include/upgrader/patches/c00511c7-7be60a84.patch.sql
similarity index 100%
rename from setup/inc/sql/v1.6st-1.7-upgrade-mysql.sql
rename to include/upgrader/patches/c00511c7-7be60a84.patch.sql
diff --git a/setup/inc/sql/c2d2fabf-aa4664af.patch.sql b/include/upgrader/patches/c2d2fabf-aa4664af.patch.sql
similarity index 100%
rename from setup/inc/sql/c2d2fabf-aa4664af.patch.sql
rename to include/upgrader/patches/c2d2fabf-aa4664af.patch.sql
diff --git a/setup/inc/sql/f8856d56-abe9c0cb.patch.sql b/include/upgrader/patches/f8856d56-abe9c0cb.patch.sql
similarity index 100%
rename from setup/inc/sql/f8856d56-abe9c0cb.patch.sql
rename to include/upgrader/patches/f8856d56-abe9c0cb.patch.sql
diff --git a/setup/inc/upgrade-prereq.inc.php b/include/upgrader/prereq.inc.php
similarity index 100%
rename from setup/inc/upgrade-prereq.inc.php
rename to include/upgrader/prereq.inc.php
diff --git a/setup/inc/upgrade.inc.php b/include/upgrader/upgrade.inc.php
similarity index 100%
rename from setup/inc/upgrade.inc.php
rename to include/upgrader/upgrade.inc.php
diff --git a/scp/images/cog.png b/scp/images/cog.png
new file mode 100755
index 0000000000000000000000000000000000000000..20171bc655e21cb8df4b800230f7b2867d2f0ca2
Binary files /dev/null and b/scp/images/cog.png differ
diff --git a/scp/images/no.png b/scp/images/no.png
new file mode 100644
index 0000000000000000000000000000000000000000..7fc50c6bc73a1a93754306483dd15aaa45e188dd
Binary files /dev/null and b/scp/images/no.png differ
diff --git a/scp/images/yes.png b/scp/images/yes.png
new file mode 100644
index 0000000000000000000000000000000000000000..8ccfa4569a5fcdc4a9de556dba0c19a20d807233
Binary files /dev/null and b/scp/images/yes.png differ
diff --git a/scp/js/upgrader.js b/scp/js/upgrader.js
new file mode 100644
index 0000000000000000000000000000000000000000..8c39b73fe767c14e566b53b6fe6a75bda7407458
--- /dev/null
+++ b/scp/js/upgrader.js
@@ -0,0 +1,58 @@
+jQuery(function($) {
+            
+    $("#overlay").css({
+        opacity : 0.3,
+        top     : 0,
+        left    : 0,
+        width   : $(window).width(),
+        height  : $(window).height()
+        });
+
+    $("#loading").css({
+        top  : ($(window).height() / 3),
+        left : ($(window).width() / 2 - 160)
+        });
+        
+    $('form#upgrade').submit(function(e) {
+        e.preventDefault();
+        var form = $(this);
+        $('input[type=submit]', this).attr('disabled', 'disabled');
+        $('#overlay, #loading').show();
+        doTasks('upgrade.php',form.serialize());
+
+        return false;
+        });
+
+    function doTasks(url, data) {
+        function _lp(count) {
+            $.ajax({
+                type: 'POST',
+                url: 'ajax.php/upgrader',
+                async: true,
+                cache: false,
+                data: data,
+                dataType: 'text',
+                success: function(res) {
+                    if (res) { 
+                        $('#loading #msg').html(res);
+                    }
+                },
+                statusCode: {
+                    200: function() {
+                        setTimeout(function() { _lp(count+1); }, 2);
+                    },
+
+                    304: function() {
+                        $('#loading #msg').html("We're done... cleaning up!");
+                        setTimeout(function() { location.href =url;}, 3000);
+                    }
+                },
+                error: function() {
+                    $('#loading #msg').html("Something went wrong");
+                    setTimeout(function() { location.href =url;}, 1000);
+                }
+            });
+        };
+        _lp(0);
+    }
+});
diff --git a/setup/upgrade.php b/scp/upgrade.php
similarity index 100%
rename from setup/upgrade.php
rename to scp/upgrade.php
diff --git a/setup/inc/class.attachment.migrate.php b/setup/inc/class.attachment.migrate.php
deleted file mode 100644
index 20385fd936439fbe868c2069b4fde0601a80b0ac..0000000000000000000000000000000000000000
--- a/setup/inc/class.attachment.migrate.php
+++ /dev/null
@@ -1,162 +0,0 @@
-<?php
-/*********************************************************************
-    class.attachment.migrate.php
-
-    Attachment migration from file-based attachments in pre-1.7 to
-    database-backed attachments in osTicket v1.7. This class provides the
-    hardware to find and retrieve old attachments and move them into the new
-    database scheme with the data in the actual database.
-
-    Copyright (c)  2006-2012 osTicket
-    http://www.osticket.com
-
-    Released under the GNU General Public License WITHOUT ANY WARRANTY.
-    See LICENSE.TXT for details.
-
-    vim: expandtab sw=4 ts=4 sts=4:
-**********************************************************************/
-include_once(INCLUDE_DIR.'class.file.php');
-
-class AttachmentMigrater {
-    function AttachmentMigrater() {
-        $this->queue = array();
-        $this->current = 0;
-        $this->errors = 0;
-        $this->errorList = array();
-    }
-    /**
-     * Identifies attachments in need of migration and queues them for
-     * migration to the new database schema.
-     * 
-     * @see ::next() for output along the way
-     *
-     * Returns:
-     * TRUE/FALSE to indicate if migration finished without any errors
-     */
-    function start_migration() {
-        $this->findAttachments();
-        $this->total = count($this->queue);
-    }
-    /**
-     * Process the migration for a unit of time. This will be used to
-     * overcome the execution time restriction of PHP. This instance can be
-     * stashed in a session and have this method called periodically to
-     * process another batch of attachments
-     */
-    function do_batch($max, $time=20) {
-        $start = time();
-        $this->errors = 0;
-        while (count($this->queue) && $count++ < $max && time()-$start < $time)
-            $this->next();
-        # TODO: Log success/error indication of migration of attachments
-        return (!$this->errors);
-    }
-
-    function queue($fileinfo) {
-        $this->queue[] = $fileinfo;
-    }
-    function getQueueLength() { return count($this->queue); }
-    /**
-     * Processes the next item on the work queue. Emits a JSON messages to
-     * indicate current progress.
-     *
-     * Returns:
-     * TRUE/NULL if the migration was successful
-     */
-    function next() {
-        # Fetch next item -- use the last item so the array indices don't
-        # need to be recalculated for every shift() operation.
-        $info = array_pop($this->queue);
-        # Attach file to the ticket
-        if (!($info['data'] = @file_get_contents($info['path']))) {
-            # Continue with next file
-            return $this->error(
-                sprintf('%s: Cannot read file contents', $info['path']));
-        }
-        # Get the mime/type of each file
-        # XXX: Use finfo_buffer for PHP 5.3+
-        $info['type'] = mime_content_type($info['path']);
-        if (!($fileId = AttachmentFile::save($info))) {
-            return $this->error(
-                sprintf('%s: Unable to migrate attachment', $info['path']));
-        }
-        # Update the ATTACHMENT_TABLE record to set file_id
-        db_query('update '.TICKET_ATTACHMENT_TABLE
-                .' set file_id='.db_input($fileId)
-                .' where attach_id='.db_input($info['attachId']));
-        # Remove disk image of the file. If this fails, the migration for
-        # this file would not be retried, because the file_id in the
-        # TICKET_ATTACHMENT_TABLE has a nonzero value now
-        if (!@unlink($info['path']))
-            $this->error(
-                sprintf('%s: Unable to remove file from disk',
-                $info['path']));
-        # TODO: Log an internal note to the ticket?
-        return true;
-    }
-    /**
-     * From (class Ticket::fixAttachments), used to detect the locations of
-     * attachment files
-     */
-    /* static */ function findAttachments(){
-        global $cfg;
-
-        $res=db_query('SELECT attach_id, file_name, file_key, Ti.created'
-            .' FROM '.TICKET_ATTACHMENT_TABLE.' TA'
-            .' JOIN '.TICKET_TABLE.' Ti ON Ti.ticket_id=TA.ticket_id'
-            .' WHERE NOT file_id');
-        if (!$res) {
-            return $this->error('Unable to query for attached files');
-        } elseif (!db_num_rows($res)) {
-            return true;
-        }
-        $dir=$cfg->getUploadDir();
-        while (list($id,$name,$key,$created)=db_fetch_row($res)) {
-            $month=date('my',strtotime($created));
-            $info=array(
-                'name'=>        $name,
-                'attachId'=>    $id,
-            );
-            $filename15=sprintf("%s/%s_%s",rtrim($dir,'/'),$key,$name);
-            $filename16=sprintf("%s/%s/%s_%s",rtrim($dir,'/'),$month,$key,$name); //new destination.
-            if (file_exists($filename15)) { 
-                $info['path'] = $filename15;
-            } elseif (file_exists($filename16)) {  
-                $info['path'] = $filename16;
-            } else {
-                # XXX Cannot find file for attachment
-                $this->error(sprintf('%s: Unable to locate attachment file',
-                    $name));
-                # No need to further process this file
-                continue;
-            }
-            # TODO: Get the size and mime/type of each file.
-            #
-            # NOTE: If filesize() fails and file_get_contents() doesn't,
-            # then the AttachmentFile::save() method will automatically
-            # estimate the filesize based on the length of the string data
-            # received in $info['data'] -- ie. no need to do that here.
-            #
-            # NOTE: The size is done here because it should be quick to
-            # lookup out of file inode already loaded. The mime/type may
-            # take a while because it will require a second IO to read the
-            # file data.  To ensure this will finish before the
-            # max_execution_time, perform the type match in the ::next()
-            # method since the entire file content will be read there
-            # anyway.
-            $info['size'] = @filesize($info['path']);
-            # Coroutines would be nice ..
-            $this->queue($info);
-        }
-    }
-
-    function error($what) {
-        $this->errors++;
-        $this->errorList[] = $what;
-        # Assist in returning FALSE for inline returns with this method
-        return false;
-    }
-    function getErrors() {
-        return $this->errorList;
-    }
-}