diff --git a/.gitignore b/.gitignore
index cd7a8bf869987498365fbfaafee0105c48c8c375..00530e1c83fc6858a098fb4c55d71090643c9b7f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
 .htaccess
 php53.cgi
 include/ost-config.php
-setup/cli/modules/importers
+setup/cli/modules/importer
 *.sw[a-z]
 .DS_Store
 .vagrant
diff --git a/assets/default/css/theme.css b/assets/default/css/theme.css
index 2166850b3a402f0e4109d62e81034af4e56b8b69..d68ab78e966a898a2af31a1a92f9650e5ca1ae28 100644
--- a/assets/default/css/theme.css
+++ b/assets/default/css/theme.css
@@ -127,9 +127,9 @@ h1 {
 h3 {
   font-size: 16px;
 }
-h2 {
+h2, .subject {
   font-size: 16px;
-  color: #999;
+  color: black;
 }
 /* Helpers */
 .centered {
diff --git a/include/class.auth.php b/include/class.auth.php
index 313e1a476e7c556ef2a70ea72888ee77bcc38b97..02e8eaf52b7d8c06603afbc604be8c3c11a8ff44 100644
--- a/include/class.auth.php
+++ b/include/class.auth.php
@@ -872,13 +872,13 @@ class UserAuthStrikeBackend extends  AuthStrikeBackend {
         if($authsession['strikes']>$cfg->getClientMaxLogins()) {
             $authsession['laststrike'] = time();
             $alert='Excessive login attempts by a user.'."\n".
-                    'Login: '.$username.': '.$password."\n".
+                    'Username: '.$username."\n".
                     'IP: '.$_SERVER['REMOTE_ADDR']."\n".'Time:'.date('M j, Y, g:i a T')."\n\n".
                     'Attempts #'.$authsession['strikes'];
             $ost->logError('Excessive login attempts (user)', $alert, ($cfg->alertONLoginError()));
             return new AccessDenied('Access Denied');
-        } elseif($authsession['strikes']%3==0) { //Log every other third failed login attempt as a warning.
-            $alert='Login: '.$username.': '.$password."\n".'IP: '.$_SERVER['REMOTE_ADDR'].
+        } elseif($authsession['strikes']%3==0) { //Log every third failed login attempt as a warning.
+            $alert='Username: '.$username."\n".'IP: '.$_SERVER['REMOTE_ADDR'].
                    "\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".'Attempts #'.$authsession['strikes'];
             $ost->logWarning('Failed login attempt (user)', $alert);
         }
diff --git a/include/class.client.php b/include/class.client.php
index 2af67879f6944db3526e4480c99e7eee06a2e08f..3e55929014452469378aece33cd35295dbaed194 100644
--- a/include/class.client.php
+++ b/include/class.client.php
@@ -149,6 +149,10 @@ abstract class TicketUser {
         return $this->_guest;
     }
 
+    function getUserId() {
+        return $this->user->getId();
+    }
+
     abstract function getTicketId();
     abstract function getTicket();
 }
diff --git a/include/class.export.php b/include/class.export.php
index a3335a2e21e33a7a8290358dfdd210f457408451..f112b15a30006c6d5ce79b219f89db7034b649c0 100644
--- a/include/class.export.php
+++ b/include/class.export.php
@@ -213,7 +213,7 @@ class ResultSetExporter {
         $this->options = $options;
         $this->output = $options['output'] ?: fopen('php://output', 'w');
 
-        $this->_res = db_query($sql);
+        $this->_res = db_query($sql, true, true);
         if ($row = db_fetch_array($this->_res)) {
             $query_fields = array_keys($row);
             $this->headers = array();
@@ -296,9 +296,13 @@ require_once INCLUDE_DIR . 'class.json.php';
 require_once INCLUDE_DIR . 'class.migrater.php';
 require_once INCLUDE_DIR . 'class.signal.php';
 
+define('OSTICKET_BACKUP_SIGNATURE', 'osTicket-Backup');
+define('OSTICKET_BACKUP_VERSION', 'B');
+
 class DatabaseExporter {
 
     var $stream;
+    var $options;
     var $tables = array(CONFIG_TABLE, SYSLOG_TABLE, FILE_TABLE,
         FILE_CHUNK_TABLE, STAFF_TABLE, DEPT_TABLE, TOPIC_TABLE, GROUP_TABLE,
         GROUP_DEPT_TABLE, TEAM_TABLE, TEAM_MEMBER_TABLE, FAQ_TABLE,
@@ -311,21 +315,21 @@ class DatabaseExporter {
         TIMEZONE_TABLE, SESSION_TABLE, PAGE_TABLE,
         FORM_SEC_TABLE, FORM_FIELD_TABLE, LIST_TABLE, LIST_ITEM_TABLE,
         FORM_ENTRY_TABLE, FORM_ANSWER_TABLE, USER_TABLE, USER_EMAIL_TABLE,
+        PLUGIN_TABLE, TICKET_COLLABORATOR_TABLE,
+        USER_ACCOUNT_TABLE, ORGANIZATION_TABLE, NOTE_TABLE
     );
 
-    function DatabaseExporter($stream) {
+    function DatabaseExporter($stream, $options=array()) {
         $this->stream = $stream;
+        $this->options = $options;
     }
 
     function write_block($what) {
         fwrite($this->stream, JsonDataEncoder::encode($what));
-        fwrite($this->stream, "\x1e");
+        fwrite($this->stream, "\n");
     }
 
-    function dump($error_stream) {
-        // Allow plugins to change the tables exported
-        Signal::send('export.tables', $this, $this->tables);
-
+    function dump_header() {
         $header = array(
             array(OSTICKET_BACKUP_SIGNATURE, OSTICKET_BACKUP_VERSION),
             array(
@@ -338,30 +342,32 @@ class DatabaseExporter {
             ),
         );
         $this->write_block($header);
+    }
+
+    function dump($error_stream) {
+        // Allow plugins to change the tables exported
+        Signal::send('export.tables', $this, $this->tables);
+        $this->dump_header();
 
         foreach ($this->tables as $t) {
             if ($error_stream) $error_stream->write("$t\n");
+
             // Inspect schema
-            $table = $indexes = array();
-            $res = db_query("show columns from $t");
-            while ($field = db_fetch_array($res))
+            $table = array();
+            $res = db_query("select column_name from information_schema.columns
+                where table_schema=DATABASE() and table_name='$t'");
+            while (list($field) = db_fetch_row($res))
                 $table[] = $field;
 
-            $res = db_query("show indexes from $t");
-            while ($col = db_fetch_array($res))
-                $indexes[] = $col;
-
-            $res = db_query("select * from $t");
-            $types = array();
-
             if (!$table) {
                 if ($error_stream) $error_stream->write(
                     $t.': Cannot export table with no fields'."\n");
                 die();
             }
             $this->write_block(
-                array('table', substr($t, strlen(TABLE_PREFIX)), $table,
-                    $indexes));
+                array('table', substr($t, strlen(TABLE_PREFIX)), $table));
+
+            db_query("select * from $t");
 
             // Dump row data
             while ($row = db_fetch_row($res))
@@ -370,4 +376,32 @@ class DatabaseExporter {
             $this->write_block(array('end-table'));
         }
     }
+
+    function transfer($destination, $query, $callback=false, $options=array()) {
+        $header_out = false;
+        $res = db_query($query, true, false);
+        $i = 0;
+        while ($row = db_fetch_array($res)) {
+            if (is_callable($callback))
+                $callback($row);
+            if (!$header_out) {
+                $fields = array_keys($row);
+                $this->write_block(
+                    array('table', $destination, $fields, $options));
+                $header_out = true;
+
+            }
+            $this->write_block(array_values($row));
+        }
+        $this->write_block(array('end-table'));
+    }
+
+    function transfer_array($destination, $array, $keys, $options=array()) {
+        $this->write_block(
+            array('table', $destination, $keys, $options));
+        foreach ($array as $row) {
+            $this->write_block(array_values($row));
+        }
+        $this->write_block(array('end-table'));
+    }
 }
diff --git a/include/class.ostsession.php b/include/class.ostsession.php
index dc53af14ef11f7eda69ae94fbb78db6e1cfafd93..4c34fda98553e28907cab45577e0189179780a26 100644
--- a/include/class.ostsession.php
+++ b/include/class.ostsession.php
@@ -77,7 +77,7 @@ class osTicketSession {
     }
 
     function read($id){
-        $this->isnew = true;
+        $this->isnew = false;
         if (!$this->data || $this->id != $id) {
             $sql='SELECT session_data FROM '.SESSION_TABLE
                 .' WHERE session_id='.db_input($id)
@@ -86,10 +86,12 @@ class osTicketSession {
                 return false;
             elseif (db_num_rows($res))
                 list($this->data)=db_fetch_row($res);
+            else
+                // No session data on record -- new session
+                $this->isnew = true;
             $this->id = $id;
         }
         $this->data_hash = md5($id.$this->data);
-        $this->isnew = false;
         return $this->data;
     }
 
diff --git a/include/client/header.inc.php b/include/client/header.inc.php
index e6ea209f4048ee21b92244bfc0e504e82a89c8fa..65ce264996fc81d02d14220f68b7a862fe0c14e1 100644
--- a/include/client/header.inc.php
+++ b/include/client/header.inc.php
@@ -1,5 +1,7 @@
 <?php
 $title=($cfg && is_object($cfg) && $cfg->getTitle())?$cfg->getTitle():'osTicket :: Support Ticket System';
+$signin_url = ROOT_PATH . "login.php"
+    . ($thisclient ? "?e=".urlencode($thisclient->getEmail()) : "");
 header("Content-Type: text/html; charset=UTF-8\r\n");
 ?>
 <!DOCTYPE html>
@@ -56,7 +58,7 @@ header("Content-Type: text/html; charset=UTF-8\r\n");
                     Guest User | <?php
                 }
                 if ($cfg->getClientRegistrationMode() != 'disabled') { ?>
-                    <a href="<?php echo ROOT_PATH; ?>login.php">Sign In</a>
+                    <a href="<?php echo $signin_url; ?>">Sign In</a>
 <?php
                 }
             } ?>
diff --git a/include/client/view.inc.php b/include/client/view.inc.php
index f326e2f29a4a759cb1fb7f4b968593f050bdd301..2f7d487ae071631e3b8e0d371b92330423ce2c59 100644
--- a/include/client/view.inc.php
+++ b/include/client/view.inc.php
@@ -14,7 +14,9 @@ if ($thisclient && $thisclient->isGuest()
 <div id="msg_info">
     <i class="icon-compass icon-2x pull-left"></i>
     <strong>Looking for your other tickets?</strong></br>
-    <a href="login.php" style="text-decoration:underline">Sign in</a> or
+    <a href="<?php echo ROOT_PATH; ?>login.php?e=<?php
+        echo urlencode($thisclient->getEmail());
+        ?>" style="text-decoration:underline">Sign in</a> or
     <a href="account.php?do=create" style="text-decoration:underline">register for an account</a>
     for the best experience on our help desk.</div>
 
@@ -26,7 +28,9 @@ if ($thisclient && $thisclient->isGuest()
             <h1>
                 Ticket #<?php echo $ticket->getNumber(); ?> &nbsp;
                 <a href="tickets.php?id=<?php echo $ticket->getId(); ?>" title="Reload"><span class="Icon refresh">&nbsp;</span></a>
-<?php if ($cfg->allowClientUpdates()) { ?>
+<?php if ($cfg->allowClientUpdates()
+        // Only ticket owners can edit the ticket details (and other forms)
+        && $thisclient->getId() == $ticket->getUserId()) { ?>
                 <a class="action-button" href="tickets.php?a=edit&id=<?php
                      echo $ticket->getId(); ?>"><i class="icon-edit"></i> Edit</a>
 <?php } ?>
@@ -93,9 +97,7 @@ foreach (DynamicFormEntry::forTicket($ticket->getId()) as $idx=>$form) {
 </tr>
 </table>
 <br>
-<h2>Subject:<?php echo Format::htmlchars($ticket->getSubject()); ?></h2>
-<br>
-<span class="Icon thread">Ticket Thread</span>
+<div class="subject">Subject: <strong><?php echo Format::htmlchars($ticket->getSubject()); ?></strong></div>
 <div id="ticketThread">
 <?php
 if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) {
diff --git a/include/i18n/en_US/form.yaml b/include/i18n/en_US/form.yaml
index 0d59d1bc67e1c43e09001d55cb220c0399e19838..fe4be2f66098d0c8de9daee25c785ed3d04260f9 100644
--- a/include/i18n/en_US/form.yaml
+++ b/include/i18n/en_US/form.yaml
@@ -78,7 +78,8 @@
       tickets, and will be searchable with advanced search and filterable.
   deletable: false
   fields:
-    - type: text # notrans
+    - id: 20
+      type: text # notrans
       name: subject # notrans
       label: Issue Summary
       required: true
@@ -88,7 +89,8 @@
         size: 40
         length: 50
 
-    - type: thread # notrans
+    - id: 21
+      type: thread # notrans
       name: message # notrans
       label: Issue Details
       hint: Details on the reason(s) for opening the ticket.
@@ -96,7 +98,8 @@
       edit_mask: 15
       sort: 2
 
-    - type: priority # notrans
+    - id: 22
+      type: priority # notrans
       name: priority # notrans
       label: Priority Level
       required: false
diff --git a/include/mysqli.php b/include/mysqli.php
index 8955ea99f335685bba44f0b3570c7715ab39ee49..e8bfeb32ebcfa7f6acd5fe26991a6fc07b32c136 100644
--- a/include/mysqli.php
+++ b/include/mysqli.php
@@ -143,12 +143,18 @@ function db_create_database($database, $charset='utf8',
  * (mixed) MysqliResource if SELECT query succeeds, true if an INSERT,
  * UPDATE, or DELETE succeeds, false or null if the query fails.
  */
-function db_query($query, $logError=true) {
+function db_query($query, $logError=true, $buffered=true) {
     global $ost, $__db;
 
+    if ($__db->unbuffered_result) {
+        $__db->unbuffered_result->free();
+        $__db->unbuffered_result = false;
+    }
+
     $tries = 3;
     do {
-        $res = $__db->query($query);
+        $res = $__db->query($query,
+            $buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT);
         // Retry the query due to deadlock error (#1213)
         // TODO: Consider retry on #1205 (lock wait timeout exceeded)
         // TODO: Log warning
@@ -164,6 +170,9 @@ function db_query($query, $logError=true) {
         //echo $msg; #uncomment during debuging or dev.
     }
 
+    if (is_object($res) && !$buffered)
+        $__db->unbuffered_result = $res;
+
     return $res;
 }
 
@@ -182,11 +191,13 @@ function db_count($query) {
     return db_result(db_query($query));
 }
 
-function db_result($res, $row=0) {
+function db_result($res, $row=false) {
     if (!$res)
         return NULL;
 
-    $res->data_seek($row);
+    if ($row !== false)
+        $res->data_seek($row);
+
     list($value) = db_output($res->fetch_row());
     return $value;
 }
diff --git a/include/pear/Mail/RFC822.php b/include/pear/Mail/RFC822.php
index 58d36465cba21779887651cec4204f222f9271ec..f22e4ecfe880546e3558479066d1ac497a4ee586 100644
--- a/include/pear/Mail/RFC822.php
+++ b/include/pear/Mail/RFC822.php
@@ -362,13 +362,17 @@ class Mail_RFC822 {
      */
     function _hasUnclosedQuotes($string)
     {
+        $matches = array();
+        if (!preg_match_all('/\\|"/S', $string, $matches, PREG_SET_ORDER))
+            return false;
+
         $string = trim($string);
         $iMax = strlen($string);
         $in_quote = false;
         $i = $slashes = 0;
 
-        for (; $i < $iMax; ++$i) {
-            switch ($string[$i]) {
+        foreach ($matches[0] as $m) {
+            switch ($m) {
             case '\\':
                 ++$slashes;
                 break;
@@ -425,7 +429,7 @@ class Mail_RFC822 {
     function _hasUnclosedBracketsSub($string, &$num, $char)
     {
         $parts = explode($char, $string);
-        for ($i = 0; $i < count($parts); $i++){
+        for ($i = 0, $k = count($parts); $i < $k; $i++){
             if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i]))
                 $num--;
             if (isset($parts[$i + 1]))
diff --git a/include/staff/templates/org.tmpl.php b/include/staff/templates/org.tmpl.php
index b1d58427a0b46c7245c5263bc319839c608cd5eb..4331153dec194cdcab36c0c05679ef0cfc0139dd 100644
--- a/include/staff/templates/org.tmpl.php
+++ b/include/staff/templates/org.tmpl.php
@@ -14,9 +14,10 @@ if ($info['error']) {
 <div id="org-profile" style="display:<?php echo $forms ? 'none' : 'block'; ?>;margin:5px;">
     <i class="icon-group icon-4x pull-left icon-border"></i>
     <?php
-    if ($account) { ?>
+    if ($user) { ?>
     <a class="action-button pull-right user-action" style="overflow:inherit"
-        href="#users/<?php echo $account->getUserId(); ?>/org/<?php echo $org->getId(); ?>" ><i class="icon-user"></i> Change Organization</a>
+        href="#users/<?php echo $user->getId(); ?>/org/<?php echo $org->getId(); ?>" ><i class="icon-user"></i> Change</a>
+    <a class="action-button pull-right" href="orgs.php?id=<?php echo $org->getId(); ?>"><i class="icon-share"></i> Manage</a>
     <?php
     } ?>
     <div><b><a href="#" id="editorg"><i class="icon-edit"></i>&nbsp;<?php
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index ed2f5bab5ee98ec05ba9efe26a662098537eb2c6..efb27175820c84e57af2b7ae0f9ced7384196f9d 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -202,7 +202,7 @@ if($ticket->isOverdue())
 <?php if ($user->getOrgId()) { ?>
                                     <li><a href="orgs.php?id=<?php echo $user->getOrgId(); ?>"><i class="icon-building icon-fixed-width"></i> Manage Organization</a></li>
 <?php } ?>
-                                </u>
+                                </ul>
                             </div>
                     <?php
                         }
@@ -323,7 +323,7 @@ foreach (DynamicFormEntry::forTicket($ticket->getId()) as $form) {
     if (count($answers) == 0)
         continue;
     ?>
-        </tr><tr>
+        <tr>
         <td colspan="2">
             <table cellspacing="0" cellpadding="4" width="100%" border="0">
             <?php foreach($answers as $a) {
@@ -339,10 +339,10 @@ foreach (DynamicFormEntry::forTicket($ticket->getId()) as $form) {
                 <?php } ?>
             </table>
         </td>
+        </tr>
     <?php
     $idx++;
     } ?>
-    </tr>
 </table>
 <div class="clear"></div>
 <h2 style="padding:10px 0 5px 0; font-size:11pt;"><?php echo Format::htmlchars($ticket->getSubject()); ?></h2>
@@ -640,7 +640,7 @@ $tcount+= $ticket->getNumNotes();
                         Note title - summary of the note (optional)</div>
                         <input type="text" name="title" id="title" size="60" value="<?php echo $info['title']; ?>" >
                         <br/>
-                        <span class="error"&nbsp;<?php echo $errors['title']; ?></span>
+                        <span class="error">&nbsp;<?php echo $errors['title']; ?></span>
                     </div>
                     <br/>
                     <textarea name="note" id="internal_note" cols="80"
diff --git a/include/staff/user-view.inc.php b/include/staff/user-view.inc.php
index 52af878ed3e475c5e7d2541a0483ad043e6c89c7..6a29a3a0ef740093dd1b62c5bc2cb9a2a6141c95 100644
--- a/include/staff/user-view.inc.php
+++ b/include/staff/user-view.inc.php
@@ -84,8 +84,8 @@ $org = $user->getOrganization();
                         <span id="user-<?php echo $user->getId(); ?>-org">
                         <?php
                             if ($org)
-                                echo sprintf('<a href="orgs.php?id=%d">%s</a>',
-                                        $org->getId(), $org->getName());
+                                echo sprintf('<a href="#users/%d/org" class="user-action">%s</a>',
+                                        $user->getId(), $org->getName());
                             else
                                 echo sprintf('<a href="#users/%d/org"
                                         class="user-action">Add Organization</a>',
diff --git a/js/osticket.js b/js/osticket.js
index e3089ab2b25ab18b81439d2253715ee94793073d..f5fa852e84f5a3fc7ca372c0c37b1be9e0c99691 100644
--- a/js/osticket.js
+++ b/js/osticket.js
@@ -5,7 +5,7 @@
 
 $(document).ready(function(){
 
-    $("input:not(.dp):visible:enabled:first").focus();
+    $('input:not(.dp):visible:enabled:input[value=""]:first').focus();
     $('table.list tbody tr:odd').addClass('odd');
 
     //Overlay
diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js
index fe8c769d986ec560cb51fff78237155f009925bc..e96d92a073da888b2482a54158a7ee75a1ecd87e 100644
--- a/js/redactor-osticket.js
+++ b/js/redactor-osticket.js
@@ -289,7 +289,7 @@ $(function() {
 
 $(document).ajaxError(function(event, request, settings) {
     if (settings.url.indexOf('ajax.php/draft') != -1
-            && settings.type.touppercase() == 'POST') {
+            && settings.type.toUpperCase() == 'POST') {
         $('.richtext').each(function() {
             var redactor = $(this).data('redactor');
             if (redactor) {
diff --git a/setup/cli/modules/export.php b/setup/cli/modules/export.php
index f74647726e0c80ba632de5e61639582f1b01d62e..a6c45fcee9f0f43de791ea17e2745bef70953dc3 100644
--- a/setup/cli/modules/export.php
+++ b/setup/cli/modules/export.php
@@ -14,9 +14,7 @@
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
 require_once dirname(__file__) . "/class.module.php";
-
-define('OSTICKET_BACKUP_SIGNATURE', 'osTicket-Backup');
-define('OSTICKET_BACKUP_VERSION', 'A');
+require_once dirname(__file__) . "../../cli.inc.php";
 
 class Exporter extends Module {
     var $prologue =
@@ -30,19 +28,63 @@ class Exporter extends Module {
             'help'=> "Send zlib compress data to the output stream"),
     );
 
+    var $arguments = array(
+        'module' => array(
+            'required' => false,
+            'help' => 'Module used for export (see help)'
+        ),
+    );
+
+    var $autohelp = false;
+
     function run($args, $options) {
-        require_once dirname(__file__) . '/../../../main.inc.php';
+        require_once dirname(__file__) . '/../../../bootstrap.php';
         require_once INCLUDE_DIR . 'class.export.php';
 
-        global $ost;
+        if (!$args['module']) {
+            $exporter = 'DatabaseExporter';
+        }
+        else {
+            $module = (include dirname(__file__)."/importer/{$args['module']}.php");
+            if ($module) {
+                $module = new $module();
+                return $module->_run($args['module']);
+            }
+            else {
+                $this->stderr->write("Unknown importer module given\n");
+                $this->showHelp();
+            }
+        }
+        if ($exporter)
+            $this->dump($exporter);
+    }
 
-        $stream = $options['stream'];
-        if ($options['compress']) $stream = "compress.zlib://$stream";
+    function dump($module) {
+        $stream = $this->getOption('stream');
+        if ($this->getOption('compress')) $stream = "compress.zlib://$stream";
         $stream = fopen($stream, 'w');
 
-        $x = new DatabaseExporter($stream);
+        $x = new $module($stream, $this->_options);
         $x->dump($this->stderr);
     }
+
+    function showHelp() {
+        $modules = array();
+        foreach (glob(dirname(__file__).'/importer/*.php') as $script) {
+            $info = pathinfo($script);
+            $modules[] = $info['filename'];
+        }
+
+        $this->epilog =
+            "Currently available modules follow. Use 'manage.php export <module>
+            --help' for usage regarding each respective module:";
+
+        parent::showHelp();
+
+        echo "\n";
+        foreach ($modules as $name)
+            echo str_pad($name, 20) . "\n";
+    }
 }
 
 Module::register('export', 'Exporter');
diff --git a/setup/cli/modules/file.php b/setup/cli/modules/file.php
index 01deb67b54f108526f39957220db0504836c7a01..ee83169958c3e25a9c514e979564b7f6b699d588 100644
--- a/setup/cli/modules/file.php
+++ b/setup/cli/modules/file.php
@@ -11,9 +11,12 @@ class FileManager extends Module {
             'options' => array(
                 'list' => 'List files matching criteria',
                 'export' => 'Export files from the system',
+                'import' => 'Load files exported via `export`',
                 'dump' => 'Dump file content to stdout',
+                'load' => 'Load file contents from stdin',
                 'migrate' => 'Migrate a file to another backend',
                 'backends' => 'List configured storage backends',
+                'expunge' => 'Remove matching files from the system',
             ),
         ),
     );
@@ -21,7 +24,7 @@ class FileManager extends Module {
     var $options = array(
         'ticket' => array('-T', '--ticket', 'metavar'=>'id',
             'help' => 'Search by internal ticket id'),
-        'file' => array('-F', '--file', 'metavar'=>'id',
+        'file-id' => array('-F', '--file-id', 'metavar'=>'id',
             'help' => 'Search by file id'),
         'name' => array('-N', '--name', 'metavar'=>'name',
             'help' => 'Search by file name (subsring match)'),
@@ -42,6 +45,9 @@ class FileManager extends Module {
             'help' => 'Target backend for migration. See `backends` action
                 for a list of available backends'),
 
+        'file' => array('-f', '--file', 'metavar'=>'FILE',
+            'help' => 'Filename used for import and export'),
+
         'verbose' => array('-v', '--verbose', 'action'=>'store_true',
             'help' => 'Be more verbose'),
     );
@@ -65,8 +71,11 @@ class FileManager extends Module {
             $files = FileModel::objects();
             $this->_applyCriteria($options, $files);
             foreach ($files as $f) {
-                printf("% 5d %s % 8d %s % 12s %s\n", $f->id, $f->bk,
+                printf("% 5d %s % 8d %s % 16s %s\n", $f->id, $f->bk,
                     $f->size, $f->created, $f->type, $f->name);
+                if ($f->attrs) {
+                    printf("        %s\n", $f->attrs);
+                }
             }
             break;
 
@@ -81,6 +90,62 @@ class FileManager extends Module {
                 $bk->passthru();
             break;
 
+        case 'load':
+            // Load file content from STDIN
+            $files = FileModel::objects();
+            $this->_applyCriteria($options, $files);
+            if ($files->count() != 1)
+                $this->fail('Criteria must select exactly 1 file');
+
+            $f = AttachmentFile::lookup($files[0]->id);
+            try {
+                if ($bk = $f->open())
+                    $bk->unlink();
+            }
+            catch (Exception $e) {}
+
+            if ($options['to'])
+                $bk = FileStorageBackend::lookup($options['to'], $f);
+            else
+                // Use the system default
+                $bk = AttachmentFile::getBackendForFile($f);
+
+            $type = false;
+            $signature = '';
+            $finfo = new finfo(FILEINFO_MIME_TYPE);
+            if ($options['file'] && $options['file'] != '-') {
+                if (!file_exists($options['file']))
+                    $this->fail($options['file'].': Cannot open file');
+                if (!$bk->upload($options['file']))
+                    $this->fail('Unable to upload file contents to backend');
+                $type = $finfo->file($options['file']);
+                list(, $signature) = AttachmentFile::_getKeyAndHash($options['file'], true);
+            }
+            else {
+                $stream = fopen('php://stdin', 'rb');
+                while ($block = fread($stream, $bk->getBlockSize())) {
+                    if (!$bk->write($block))
+                        $this->fail('Unable to send file contents to backend');
+                    if (!$type)
+                        $type = $finfo->buffer($block);
+                }
+                if (!$bk->flush())
+                    $this->fail('Unable to commit file contents to backend');
+            }
+
+            // TODO: Update file metadata
+            $sql = 'UPDATE '.FILE_TABLE.' SET bk='.db_input($bk->getBkChar())
+                .', created=CURRENT_TIMESTAMP'
+                .', type='.db_input($type)
+                .', signature='.db_input($signature)
+                .' WHERE id='.db_input($f->getId());
+
+            if (!db_query($sql) || db_affected_rows()!=1)
+                $this->fail('Unable to update file metadata');
+
+            $this->stdout->write("Successfully saved contents\n");
+            break;
+
         case 'migrate':
             if (!$options['to'])
                 $this->fail('Please specify a target backend for migration');
@@ -110,9 +175,46 @@ class FileManager extends Module {
             }
             $this->stdout->write("Migrated $count files\n");
             break;
-        }
 
+        case 'export':
+            // Create a temporary ZIP file
+            $files = FileModel::objects();
+            $this->_applyCriteria($options, $files);
+
+            if (!$options['file'])
+                $this->fail('Please specify zip file with `-f`');
+
+            $zip = new ZipArchive();
+            if (true !== ($reason = $zip->open($options['file'],
+                    ZipArchive::CREATE)))
+                $this->fail($reason.': Unable to create zip file');
+
+            $manifest = array();
+            foreach ($files as $m) {
+                $f = AttachmentFile::lookup($m->id);
+                $zip->addFromString($f->getId(), $f->getData());
+                $zip->setCommentName($f->getId(), $f->getName());
+                // TODO: Log %attachment and %ticket_attachment entries
+                $info = array('file' => $f->getInfo());
+                foreach ($m->tickets as $t)
+                    $info['tickets'][] = $t->ht;
 
+                $manifest[$f->getId()] = $info;
+            }
+            $zip->addFromString('MANIFEST', serialize($manifest));
+            $zip->close();
+            break;
+
+        case 'expunge':
+            // Create a temporary ZIP file
+            $files = FileModel::objects();
+            $this->_applyCriteria($options, $files);
+
+            foreach ($files as $f) {
+                $f->tickets->expunge();
+                $f->unlink() && $f->delete();
+            }
+        }
     }
 
     function _applyCriteria($options, $qs) {
@@ -122,7 +224,7 @@ class FileManager extends Module {
             case 'ticket':
                 $qs->filter(array('tickets__ticket_id'=>$val));
                 break;
-            case 'file':
+            case 'file-id':
                 $qs->filter(array('id'=>$val));
                 break;
             case 'name':
@@ -191,5 +293,12 @@ class TicketAttachmentModel extends VerySimpleModel {
     );
 }
 
+class AttachmentModel extends VerySimpleModel {
+    static $meta = array(
+        'table' => ATTACHMENT_TABLE,
+        'pk' => array('object_id', 'type', 'file_id'),
+    );
+}
+
 Module::register('file', 'FileManager');
 ?>
diff --git a/setup/cli/modules/import.php b/setup/cli/modules/import.php
index 84c27396d4b790c4a5612c2146490e5da42d9090..68979f330cc8f5f959190448d0f639af4b9a0d03 100644
--- a/setup/cli/modules/import.php
+++ b/setup/cli/modules/import.php
@@ -14,6 +14,9 @@
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
 require_once dirname(__file__) . "/class.module.php";
+require_once dirname(__file__) . "../../cli.inc.php";
+require_once INCLUDE_DIR . "class.export.php";
+require_once INCLUDE_DIR . 'class.json.php';
 
 class Importer extends Module {
     var $prologue =
@@ -33,6 +36,8 @@ class Importer extends Module {
             tables. This option can be specified more than once"),
         'drop' => array('-D', '--drop', 'action'=>'store_true', 'help'=>
             'Issue DROP TABLE statements before the create statemente'),
+        'go-baby' => array('-P', '--prime-time', 'action'=>'store_true',
+            'help'=>'Load data into live database. Use at your own risk'),
     );
 
     var $epilog =
@@ -41,11 +46,46 @@ class Importer extends Module {
     var $stream;
     var $header;
     var $source_ost_info;
+    var $parent;
+
+    function __construct($parent=false) {
+        parent::__construct();
+        if ($parent) {
+            $this->parent = $parent;
+            $this->stream = $parent->stream;
+        }
+    }
+    function __get($what) {
+        return $this->parent->{$what};
+    }
+    function __call($what, $how) {
+        return call_user_func_array(array($this->parent, $what), $how);
+    }
+
+    function run($args, $options) {
+        $stream = $options['stream'];
+        if ($options['compress']) $stream = "compress.zlib://$stream";
+        if (!($this->stream = fopen($stream, 'rb'))) {
+            $this->stderr->write('Unable to open input stream');
+            die();
+        }
+
+        if (!$this->verify_header())
+            die('Unable to verify backup header');
+
+        Bootstrap::connect();
+
+        $class = 'Importer_' . $this->header[1];
+        $importer = new $class($this);
+
+        while ($importer->import_table());
+        @fclose($this->stream);
+    }
 
     function verify_header() {
         list($header, $info) = $this->read_block();
         if (!$header || $header[0] != OSTICKET_BACKUP_SIGNATURE) {
-            $this->stderr->write('Header mismatch -- not an osTicket backup');
+            $this->stderr->write("Header mismatch -- not an osTicket backup\n");
             return false;
         }
         else
@@ -60,9 +100,7 @@ class Importer extends Module {
     }
 
     function read_block() {
-        $block = '';
-        while (!feof($this->stream) && (($c = fgetc($this->stream)) != "\x1e"))
-            $block .= $c;
+        $block = fgets($this->stream);
 
         if ($json = JsonDataParser::decode($block))
             return $json;
@@ -73,6 +111,37 @@ class Importer extends Module {
         }
     }
 
+    function load_row($info, $row, $flush=false) {
+        static $header = null;
+        static $rows = array();
+        static $length = 0;
+
+        if ($info && $header === null) {
+            $header = "INSERT INTO `".TABLE_PREFIX.$info[1].'` (';
+            $cols = array();
+            foreach ($info[2] as $col)
+                $cols[] = "`{$col['Field']}`";
+            $header .= implode(', ', $cols);
+            $header .= ") VALUES ";
+        }
+        if ($row) {
+            $values = array();
+            foreach ($info[2] as $i=>$col)
+                $values[] = (is_numeric($row[$i]))
+                    ? $row[$i]
+                    : ($row[$i] ? '0x'.bin2hex($row[$i]) : "''");
+            $values = "(" . implode(', ', $values) . ")";
+            $length += strlen($values);
+            $rows[] = &$values;
+        }
+        if (($flush || $length > 16000) && $header) {
+            $this->send_statement($header . implode(',', $rows));
+            $header = null;
+            $rows = array();
+            $length = 0;
+        }
+    }
+
     function send_statement($stmt) {
         if ($this->getOption('prime-time'))
             db_query($stmt);
@@ -81,6 +150,9 @@ class Importer extends Module {
             $this->stdout->write(";\n");
         }
     }
+}
+
+class Importer_A extends Importer {
 
     function import_table() {
         if (!($header = $this->read_block()))
@@ -173,18 +245,44 @@ class Importer extends Module {
                 .')');
         }
     }
+}
 
+class Importer_B extends Importer {
     function truncate_table($info) {
         $this->send_statement('TRUNCATE TABLE '.TABLE_PREFIX.$info[1]);
-        $indexes = array();
-        foreach ($info[3] as $idx) {
-            if ($idx['Key_name'] == 'PRIMARY')
-                continue;
-            $indexes[$idx['Key_name']] =
-                '`'.TABLE_PREFIX.$info[1].'`.`'.$idx['Key_name'].'`';
+    }
+
+    function import_table() {
+        if (!($header = $this->read_block()))
+            return false;
+
+        else if ($header[0] != 'table') {
+            $this->stderr->write('Unable to read table header');
+            return false;
         }
-        foreach ($indexes as $T=>$fqn)
-            $this->send_statement('DROP INDEX IF EXISTS '.$fqn);
+
+        // TODO: Consider included tables and excluded tables
+
+        $this->stderr->write("Importing table: {$header[1]}\n");
+
+        $res = db_query("select column_name from information_schema.columns
+            where table_schema=DATABASE() and table_name='$t'");
+        while (list($field) = db_fetch_row($res))
+            if (!in_array($field, $header[2]))
+                $this->fail($header[1]
+                    .": Destination table does not have the `$field` field");
+
+        if (!isset($header[3]['truncate']) || $header[3]['truncate'])
+            $this->truncate_table($header);
+
+        while (($row=$this->read_block())) {
+            if (isset($row[0]) && ($row[0] == 'end-table')) {
+                $this->load_row(null, null, true);
+                return true;
+            }
+            $this->load_row($header, $row);
+        }
+        return false;
     }
 
     function load_row($info, $row, $flush=false) {
@@ -196,7 +294,7 @@ class Importer extends Module {
             $header = "INSERT INTO `".TABLE_PREFIX.$info[1].'` (';
             $cols = array();
             foreach ($info[2] as $col)
-                $cols[] = "`{$col['Field']}`";
+                $cols[] = "`$col`";
             $header .= implode(', ', $cols);
             $header .= ") VALUES ";
         }
@@ -218,23 +316,6 @@ class Importer extends Module {
         }
     }
 
-    function run($args, $options) {
-        require_once dirname(__file__) . '/../../../main.inc.php';
-        require_once INCLUDE_DIR . 'class.json.php';
-
-        $stream = $options['stream'];
-        if ($options['compress']) $stream = "compress.zlib://$stream";
-        if (!($this->stream = fopen($stream, 'rb'))) {
-            $this->stderr->write('Unable to open input stream');
-            die();
-        }
-
-        if (!$this->verify_header())
-            die('Unable to verify backup header');
-
-        while ($this->import_table());
-        @fclose($this->stream);
-    }
 }
 
 Module::register('import', 'Importer');
diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php
index 31213d4c678fcaacc6f720fc42652727d4e0be00..c008566f5546d829ce5375bdc69a0d6cd7c0bf2a 100644
--- a/setup/inc/class.installer.php
+++ b/setup/inc/class.installer.php
@@ -155,19 +155,19 @@ class Installer extends SetupWizard {
             $i18n->loadDefaultData();
 
             $sql='SELECT `id` FROM '.PREFIX.'sla ORDER BY `id` LIMIT 1';
-            $sla_id_1 = db_result(db_query($sql, false), 0);
+            $sla_id_1 = db_result(db_query($sql, false));
 
             $sql='SELECT `dept_id` FROM '.PREFIX.'department ORDER BY `dept_id` LIMIT 1';
-            $dept_id_1 = db_result(db_query($sql, false), 0);
+            $dept_id_1 = db_result(db_query($sql, false));
 
             $sql='SELECT `tpl_id` FROM '.PREFIX.'email_template_group ORDER BY `tpl_id` LIMIT 1';
-            $template_id_1 = db_result(db_query($sql, false), 0);
+            $template_id_1 = db_result(db_query($sql, false));
 
             $sql='SELECT `group_id` FROM '.PREFIX.'groups ORDER BY `group_id` LIMIT 1';
-            $group_id_1 = db_result(db_query($sql, false), 0);
+            $group_id_1 = db_result(db_query($sql, false));
 
             $sql='SELECT `value` FROM '.PREFIX.'config WHERE namespace=\'core\' and `key`=\'default_timezone_id\' LIMIT 1';
-            $default_timezone = db_result(db_query($sql, false), 0);
+            $default_timezone = db_result(db_query($sql, false));
 
             //Create admin user.
             $sql='INSERT INTO '.PREFIX.'staff SET created=NOW() '
@@ -194,7 +194,7 @@ class Installer extends SetupWizard {
 
 
             $sql='SELECT `email_id` FROM '.PREFIX."email WHERE `email`='alerts@$domain' LIMIT 1";
-            $alert_email_id = db_result(db_query($sql, false), 0);
+            $alert_email_id = db_result(db_query($sql, false));
 
             //Create config settings---default settings!
             //XXX: rename ostversion  helpdesk_* ??
diff --git a/tickets.php b/tickets.php
index 9561da31dd62bd7ecbccb4f4b83326845b279e3f..0d675aa88c9075fcbc872e0fdf006161fcd68e96 100644
--- a/tickets.php
+++ b/tickets.php
@@ -40,7 +40,8 @@ if($_POST && is_object($ticket) && $ticket->getId()):
     $errors=array();
     switch(strtolower($_POST['a'])){
     case 'edit':
-        if(!$ticket->checkUserAccess($thisclient)) //double check perm again!
+        if(!$ticket->checkUserAccess($thisclient) //double check perm again!
+                || $thisclient->getId() != $ticket->getUserId())
             $errors['err']='Access Denied. Possibly invalid ticket ID';
         elseif (!$cfg || !$cfg->allowClientUpdates())
             $errors['err']='Access Denied. Client updates are currently disabled';
diff --git a/view.php b/view.php
index 7e865d6a6bd3544ea3c3dbdd9f8aaafababf1839..2299043b0047d4007b3e535b898035b0935e8da5 100644
--- a/view.php
+++ b/view.php
@@ -16,14 +16,23 @@
 **********************************************************************/
 require_once('client.inc.php');
 
+$errors = array();
+// Check if the client is already signed in. Don't corrupt their session!
+if ($_GET['auth']
+        && $thisclient
+        && ($u = TicketUser::lookupByToken($_GET['auth']))
+        && ($u->getUserId() == $thisclient->getId())
+) {
+    Http::redirect('tickets.php?id='.$u->getTicketId());
+}
 // Try autologin the user
 // Authenticated user can be of type ticket owner or collaborator
-$errors = array();
-if (isset($_GET['auth']) || isset($_GET['t']))
+elseif (isset($_GET['auth']) || isset($_GET['t'])) {
     // TODO: Consider receiving an AccessDenied object
     $user =  UserAuthenticationBackend::processSignOn($errors, false);
+}
 
-if ($user && $user->getTicketId())
+if (@$user && is_object($user) && $user->getTicketId())
     Http::redirect('tickets.php?id='.$user->getTicketId());
 
 $nav = new UserNav();