diff --git a/api/cron.php b/api/cron.php
index 0ca641b52062d6a817f67d49297e9e062d63f31b..f5d47e43e2af43e1d26a1f8a5e9af698bc7e0b8f 100644
--- a/api/cron.php
+++ b/api/cron.php
@@ -19,8 +19,6 @@ require('api.inc.php');
 if (!osTicket::is_cli())
     die('cron.php only supports local cron calls - use http -> api/tasks/cron');
 
-@chdir(realpath(dirname(__FILE__)).'/'); //Change dir.
-require('api.inc.php');
 require_once(INCLUDE_DIR.'api.cron.php');
 LocalCronApiController::call();
 ?>
diff --git a/bootstrap.php b/bootstrap.php
index e99b1e339c1216ea3e5f03dcd78eb090465f1bbe..d050960ed4ffdeb0443e5878029d3539e625901f 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -43,6 +43,9 @@ class Bootstrap {
                 ini_set('date.timezone', 'America/New_York');
             }
         }
+
+        if (!isset($_SERVER['REMOTE_ADDR']))
+            $_SERVER['REMOTE_ADDR'] = '';
     }
 
     function https() {
diff --git a/css/redactor.css b/css/redactor.css
index 6a5c8df81fb6f5a2c929221d6bbc92a66d84790a..b179cfd42fe90e378a3ed32c0340f07d811a0cbe 100644
--- a/css/redactor.css
+++ b/css/redactor.css
@@ -82,7 +82,7 @@ body .redactor_box_fullscreen {
 .redactor_editor blockquote,
 .redactor_editor pre {
 	font-size: 15px;
-	line-height: 1.5rem;
+	line-height: 1.25rem;
 }
 
 .redactor_editor,
diff --git a/css/thread.css b/css/thread.css
index 9623bcf25466d22155c67ae03b2b6e2a6dddfbdc..cd3ad3d3679ecd816401570ea9ca0264855657fb 100644
--- a/css/thread.css
+++ b/css/thread.css
@@ -306,7 +306,6 @@
   border-left: 5px solid #eeeeee;
 }
 .thread-body blockquote p {
-  font-size: 17.5px;
   font-weight: 300;
   line-height: 1.25;
 }
@@ -410,3 +409,9 @@
   float: none;
   display: table-cell;
 }
+
+/* Additional style for the mighty Microsoft Office emails "standard" style */
+p.MsoNormal, li.MsoNormal, div.MsoNormal,
+p.MsoPlainText, li.MsoPlainText, div.MsoPlainText
+    {margin:0cm;
+    margin-bottom:.0001pt;}
diff --git a/include/ajax.kbase.php b/include/ajax.kbase.php
index ba0cf1d1cc523f3030017b5cbbc821aed8185933..1e642847bba383d986bafeadadee6370bcec54ed 100644
--- a/include/ajax.kbase.php
+++ b/include/ajax.kbase.php
@@ -32,6 +32,7 @@ class KbaseAjaxAPI extends AjaxController {
             $ticket = Ticket::lookup($_GET['tid']);
         }
 
+        $resp = array();
         switch($format) {
             case 'json':
                 $resp['id'] = $canned->getId();
@@ -42,7 +43,7 @@ class KbaseAjaxAPI extends AjaxController {
                 $resp['files'] = $canned->attachments->getSeparates();
 
                 if (!$cfg->isHtmlThreadEnabled()) {
-                    $resp['response'] = convert_html_to_text($resp['response'], 90);
+                    $resp['response'] = Format::html2text($resp['response'], 90);
                     $resp['files'] += $canned->attachments->getInlines();
                 }
 
@@ -54,7 +55,7 @@ class KbaseAjaxAPI extends AjaxController {
                 $response =$ticket?$ticket->replaceVars($canned->getResponse()):$canned->getResponse();
 
                 if (!$cfg->isHtmlThreadEnabled())
-                    $response = convert_html_to_text($response, 90);
+                    $response = Format::html2text($response, 90);
         }
 
         return $response;
diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index c0f69e8a95c35c8546b6f9d67f9f1a7c4f314a5f..a142fc1056deab841b3cbf5f590a19295313ac80 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -214,8 +214,8 @@ class TicketsAjaxAPI extends AjaxController {
             if ($f->get('name') && isset($req[$f->getFormName()])
                     && ($val = $req[$f->getFormName()])) {
                 $name = $f->get('name');
-                $vals[] = "MAX(IF(field.name = '$name', ans.value_id, NULL)) as `{$name}_id`"; # nolint
-                $vals[] = "MAX(IF(field.name = '$name', ans.value, NULL)) as `$name`"; # nolint
+                $vals[] = "MAX(IF(field.name = '$name', ans.value_id, NULL)) as `{$name}_id`";
+                $vals[] = "MAX(IF(field.name = '$name', ans.value, NULL)) as `$name`";
                 $where .= " AND (dyn.`{$name}_id` = ".db_input($val)
                     . " OR dyn.`$name` LIKE '%".db_real_escape($val)."%')";
             }
@@ -236,6 +236,7 @@ class TicketsAjaxAPI extends AjaxController {
 
     function search() {
         $tickets = self::_search($_REQUEST);
+        $result = array();
 
         if (count($tickets)) {
             $uid = md5($_SERVER['QUERY_STRING']);
@@ -414,6 +415,8 @@ class TicketsAjaxAPI extends AjaxController {
             $ticket->getEmail());
         echo '
             </table>';
+
+        $options = array();
         $options[]=array('action'=>'Thread ('.$ticket->getThreadCount().')','url'=>"tickets.php?id=$tid");
         if($ticket->getNumNotes())
             $options[]=array('action'=>'Notes ('.$ticket->getNumNotes().')','url'=>"tickets.php?id=$tid#notes");
diff --git a/include/class.api.php b/include/class.api.php
index 6dec23efbc71969df796ecb8d9fc206f090b4129..23ae8a589e9192a08394134929ff9429695b2f30 100644
--- a/include/class.api.php
+++ b/include/class.api.php
@@ -396,7 +396,7 @@ class ApiJsonDataParser extends JsonDataParser {
                         "name" => key($info),
                     );
                 }
-                unset($value);
+                unset($info);
             }
             if (is_array($value)) {
                 $value = $this->fixup($value);
diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index 79a092cbbc98dcb7c26e8771a419c7e06be0207e..e0a2f1e8619299de4cb6198d749d4f33613d0451 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -708,6 +708,8 @@ class DynamicListItem extends VerySimpleModel {
 }
 
 class SelectionField extends FormField {
+    static $widget = 'SelectionWidget';
+
     function getListId() {
         list(,$list_id) = explode('-', $this->get('type'));
         return $list_id;
@@ -719,10 +721,6 @@ class SelectionField extends FormField {
         return $this->_list;
     }
 
-    function getWidget() {
-        return new SelectionWidget($this);
-    }
-
     function parse($value) {
         return $this->to_php($value);
     }
diff --git a/include/class.faq.php b/include/class.faq.php
index 7cb3c9a950ee9e2461fabf79d5f8b3ec57c07f62..70a7e2540dd91335c91e470c163abf430d0b63f1 100644
--- a/include/class.faq.php
+++ b/include/class.faq.php
@@ -112,7 +112,7 @@ class FAQ {
     function setKeywords($words) { $this->ht['keywords'] = $words; }
     function setNotes($text) { $this->ht['notes'] = $text; }
 
-    /* For ->attach() and ->detach(), use $this->attachments() */
+    /* For ->attach() and ->detach(), use $this->attachments() (nolint) */
     function attach($file) { return $this->_attachments->add($file); }
     function detach($file) { return $this->_attachments->remove($file); }
 
@@ -130,8 +130,9 @@ class FAQ {
 
     /* Same as update - but mainly called after one or more setters are changed. */
     function apply() {
+        $errors = array();
         //XXX: set errors and add ->getErrors() & ->getError()
-        return $this->update($this->ht, $errors);               # nolint
+        return $this->update($this->ht, $errors);
     }
 
     function updateTopics($ids){
diff --git a/include/class.file.php b/include/class.file.php
index 5083c76f9fc7b9a53dc6c2275597f18b003dab0e..8acd7e42949f0c097bcb75d11c35823a224b013c 100644
--- a/include/class.file.php
+++ b/include/class.file.php
@@ -254,12 +254,14 @@ class AttachmentFile {
 
     function save($file) {
 
-        if(!$file['hash'])
-            $file['hash']=MD5(MD5($file['data']).time());
         if (is_callable($file['data']))
             $file['data'] = $file['data']();
+
+        if(!$file['hash'])
+            $file['hash'] = MD5(MD5($file['data']).time());
+
         if(!$file['size'])
-            $file['size']=strlen($file['data']);
+            $file['size'] = strlen($file['data']);
 
         $sql='INSERT INTO '.FILE_TABLE.' SET created=NOW() '
             .',type='.db_input(strtolower($file['type']))
diff --git a/include/class.filter.php b/include/class.filter.php
index 81c6a39fe17a86e8e5981385a0dd417d6e47955f..15c68cc3170596abef12fb44e1eaf4365b7c447c 100644
--- a/include/class.filter.php
+++ b/include/class.filter.php
@@ -175,11 +175,12 @@ class Filter {
     }
 
     function addRule($what, $how, $val,$extra=array()) {
+        $errors = array();
 
         $rule= array_merge($extra,array('what'=>$what, 'how'=>$how, 'val'=>$val));
         $rule['filter_id']=$this->getId();
 
-        return FilterRule::create($rule,$errors);               # nolint
+        return FilterRule::create($rule,$errors);
     }
 
     function removeRule($what, $how, $val) {
@@ -512,7 +513,8 @@ class Filter {
 
         //Success with update/create...save the rules. We can't recover from any errors at this point.
         # Don't care about errors stashed in $xerrors
-        self::save_rules($id,$vars,$xerrors);               # nolint
+        $xerrors = array();
+        self::save_rules($id,$vars,$xerrors);
 
         return true;
     }
diff --git a/include/class.format.php b/include/class.format.php
index 843033ae205875af379cff31c7c26026df1a2855..fd51d95867d76918bb85e5a34d1eb524ee2066ff 100644
--- a/include/class.format.php
+++ b/include/class.format.php
@@ -128,8 +128,8 @@ class Format {
         return is_array($var)?array_map(array('Format','strip_slashes'),$var):stripslashes($var);
     }
 
-    function wrap($text,$len=75) {
-        return wordwrap($text,$len,"\n",true);
+    function wrap($text, $len=75) {
+        return $len ? wordwrap($text, $len, "\n", true) : $text;
     }
 
     function html($html, $config=array('balance'=>1)) {
@@ -137,13 +137,64 @@ class Format {
         return htmLawed($html, $config);
     }
 
+    function html2text($html, $width=74, $tidy=true) {
+
+
+        # Tidy html: decode, balance, sanitize tags
+        if($tidy)
+            $html = Format::html(Format::htmldecode($html), array('balance' => 1));
+
+        # See if advanced html2text is available (requires xml extension)
+        if (function_exists('convert_html_to_text')
+                && extension_loaded('xml'))
+            return convert_html_to_text($html, $width);
+
+        # Try simple html2text  - insert line breaks after new line tags.
+        $html = preg_replace(
+                array(':<br ?/?\>:i', ':(</div>)\s*:i', ':(</p>)\s*:i'),
+                array("\n", "$1\n", "$1\n\n"),
+                $html);
+
+        # Strip tags, decode html chars and wrap resulting text.
+        return Format::wrap(
+                Format::htmldecode( Format::striptags($html, false)),
+                $width);
+    }
+
     function safe_html($html) {
+        // Remove HEAD and STYLE sections
+        $html = preg_replace(':<(head|style).+</\1>:is','', $html);
         $config = array(
-                'safe' => 1, //Exclude applet, embed, iframe, object and script tags.
-                'balance' => 1, //balance and close unclosed tags.
-                'comment' => 1, //Remove html comments (OUTLOOK LOVE THEM)
-                'schemes' => 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https; src: cid, http, https, data'
-                );
+            'safe' => 1, //Exclude applet, embed, iframe, object and script tags.
+            'balance' => 1, //balance and close unclosed tags.
+            'comment' => 1, //Remove html comments (OUTLOOK LOVE THEM)
+            'deny_attribute' => 'id',
+            'schemes' => 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https; src: cid, http, https, data',
+            'hook_tag' => function ($el, $attributes=0) {
+                static $eE = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1,
+                    'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1);
+                if (isset($attributes['class'])) {
+                    $classes = explode(' ', $attributes['class']);
+                    foreach ($classes as $i=>$a)
+                        // Unset all unsupported style classes -- anything by M$
+                        if (strpos($a, 'Mso') !== 0)
+                            unset($classes[$i]);
+                    if ($classes)
+                        $attributes['class'] = implode(' ', $classes);
+                    else
+                        unset($attributes['class']);
+                }
+                $at = '';
+                if (is_array($attributes)) {
+                    foreach ($attributes as $k=>$v)
+                        $at .= " $k=\"$v\"";
+                    return "<{$el}{$at}".(isset($eE[$el])?" /":"").">";
+                }
+                else {
+                    return "</{$el}>";
+                }
+            }
+        );
 
         if (!preg_match('/style="[^"]*white-space:\s*pre/i', $html) !== false)
             $config['tidy'] = -1; // Clean extra whitspace
@@ -209,8 +260,8 @@ class Format {
         //Wrap long words...
         #$text=preg_replace_callback('/\w{75,}/',
         #    create_function(
-        #        '$matches',                                     # nolint
-        #        'return wordwrap($matches[0],70,"\n",true);'),  # nolint
+        #        '$matches',
+        #        'return wordwrap($matches[0],70,"\n",true);'),
         #    $text);
 
         // Make showing offsite images optional
@@ -244,14 +295,14 @@ class Format {
         $token = $ost->getLinkToken();
         //Not perfect but it works - please help improve it.
         $text=preg_replace_callback('/(?<!"|>)(((f|ht)tp(s?):\/\/)[-a-zA-Z0-9@:%_\+.~#?&;\/\/=]+)/',
-                create_function('$matches', # nolint
-                    sprintf('return "<a href=\"l.php?url=".urlencode($matches[1])."&auth=%s\" target=\"_blank\">".$matches[1]."</a>";', # nolint
+                create_function('$matches',
+                    sprintf('return "<a href=\"l.php?url=".urlencode($matches[1])."&auth=%s\" target=\"_blank\">".$matches[1]."</a>";',
                         $token)),
                 $text);
 
         $text=preg_replace_callback("/(^|[ \\n\\r\\t])(www\.([a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+)(\/[^\/ \\n\\r]*)*)/",
-                create_function('$matches', # nolint
-                    sprintf('return "<a href=\"l.php?url=".urlencode("http://".$matches[2])."&auth=%s\" target=\"_blank\">".$matches[2]."</a>";', # nolint
+                create_function('$matches',
+                    sprintf('return "<a href=\"l.php?url=".urlencode("http://".$matches[2])."&auth=%s\" target=\"_blank\">".$matches[2]."</a>";',
                         $token)),
                 $text);
 
diff --git a/include/class.mailer.php b/include/class.mailer.php
index e13eff86e450589c82253468e14864d0b97d4c1b..61dda2c001b1895ed1ad9643e55c0c5bde4db1e4 100644
--- a/include/class.mailer.php
+++ b/include/class.mailer.php
@@ -143,8 +143,8 @@ class Mailer {
         if (!(isset($options['text']) && $options['text'])
                 && preg_match('/^\s*</', $message)) {
             // Make sure nothing unsafe has creeped into the message
-            $message = Format::safe_html($message);
-            $mime->setTXTBody(convert_html_to_text($message));
+            $message = Format::safe_html($message); //XXX??
+            $mime->setTXTBody(Format::html2text($message), 90, false);
         }
         else {
             $mime->setTXTBody($message);
diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php
index b9c9602fd9aa741bb9d955621ae5286f59523b36..f71c002517911d3fc3769086130eba35f8f622eb 100644
--- a/include/class.mailfetch.php
+++ b/include/class.mailfetch.php
@@ -422,24 +422,31 @@ class MailFetcher {
     function getBody($mid) {
         global $cfg;
 
-        if ($body = $this->getPart($mid,'TEXT/HTML', $this->charset)) {
-            //Convert tags of interest before we striptags
-            //$body=str_replace("</DIV><DIV>", "\n", $body);
-            //$body=str_replace(array("<br>", "<br />", "<BR>", "<BR />"), "\n", $body);
-            $body=Format::safe_html($body); //Balance html tags & neutralize unsafe tags.
-            if (!$cfg->isHtmlThreadEnabled())
-                $body = convert_html_to_text($body);
+        if ($cfg->isHtmlThreadEnabled()) {
+            if ($body=$this->getPart($mid, 'text/html', $this->charset)) {
+                //Cleanup the html.
+                $body = (trim($body, " <>br/\t\n\r"))
+                    ? Format::safe_html($body)
+                    : '--';
+            }
+            elseif ($body=$this->getPart($mid, 'text/plain', $this->charset)) {
+                $body = trim($body)
+                    ? sprintf('<div style="white-space:pre-wrap">%s</div>',
+                        Format::htmlchars($body))
+                    : '--';
+            }
         }
-        elseif ($body = $this->getPart($mid,'TEXT/PLAIN', $this->charset)) {
-            // Escape anything that looks like HTML chars since what's in
-            // the database will be considered HTML
-            // TODO: Consider the reverse of the above edits (replace \n
-            //       <br/>
-            $body=Format::htmlchars($body);
-            if ($cfg->isHtmlThreadEnabled()) {
-                $body = wordwrap($body, 90);
-                $body = "<div style=\"white-space:pre\">$body</div>";
+        else {
+            if ($body=$this->getPart($mid, 'text/plain', $this->charset)) {
+                $body = Format::htmlchars($body);
+            }
+            elseif ($body=$this->getPart($mid, 'text/html', $this->charset)) {
+                $body = Format::html2text(Format::safe_html($body), 100, false);
             }
+            $body = trim($body)
+                ? sprintf('<div style="white-space:pre-wrap">%s</div>',
+                    $body)
+                : '--';
         }
         return $body;
     }
diff --git a/include/class.mailparse.php b/include/class.mailparse.php
index fa88c393d342136af381dc9f39a4a5b372fd90e5..15247689ca10f3ea5c059c11efd26956002ae70f 100644
--- a/include/class.mailparse.php
+++ b/include/class.mailparse.php
@@ -60,11 +60,11 @@ class Mail_Parse {
     }
 
     function splitBodyHeader() {
-
+        $match = array();
         if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s",
                 $this->mime_message,
-                $match)) {                                  # nolint
-            $this->header=$match[1];                        # nolint
+                $match)) {
+            $this->header=$match[1];
         }
     }
     /**
@@ -172,17 +172,31 @@ class Mail_Parse {
     function getBody(){
         global $cfg;
 
-        if ($body=$this->getPart($this->struct,'text/html')) {
-            //Cleanup the html.
-            $body=Format::safe_html($body); //Balance html tags & neutralize unsafe tags.
-            if (!$cfg->isHtmlThreadEnabled()) {
-                $body = convert_html_to_text($body, 120);
-                $body = "<div style=\"white-space:pre-wrap\">$body</div>";
+        if ($cfg->isHtmlThreadEnabled()) {
+            if ($body=$this->getPart($this->struct,'text/html')) {
+                // Cleanup the html -- Balance html tags & neutralize unsafe tags.
+                $body = (trim($body, " <>br/\t\n\r"))
+                    ? Format::safe_html($body)
+                    : '--';
+            }
+            elseif ($body=$this->getPart($this->struct,'text/plain')) {
+                $body = trim($body)
+                    ? sprintf('<div style="white-space:pre-wrap">%s</div>',
+                        Format::htmlchars($body))
+                    : '--';
             }
         }
-        elseif ($body=$this->getPart($this->struct,'text/plain')) {
-            $body = Format::htmlchars($body);
-            $body = "<div style=\"white-space:pre-wrap\">$body</div>";
+        else {
+            if ($body=$this->getPart($this->struct,'text/plain')) {
+                $body = Format::htmlchars($body);
+            }
+            elseif ($body=$this->getPart($this->struct,'text/html')) {
+                $body = Format::html2text(Format::safe_html($body), 100, false);
+            }
+            $body = trim($body)
+                ? sprintf('<div style="white-space:pre-wrap">%s</div>',
+                    $body)
+                : '--';
         }
         return $body;
     }
diff --git a/include/class.orm.php b/include/class.orm.php
index 90557fa7677df8c44dc68125c97ad8bfd72a9b86..05201c6682bc38cb4579b14492e6f62c3d7c406e 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -139,6 +139,7 @@ class VerySimpleModel {
     function delete($pk=false) {
         $table = static::$meta['table'];
         $sql = 'DELETE FROM '.$table;
+        $filter = array();
 
         if (!$pk) $pk = static::$meta['pk'];
         if (!is_array($pk)) $pk=array($pk);
@@ -626,7 +627,7 @@ class SqlCompiler {
     }
 
     function compileWhere($where, $model) {
-        $constrints = array();
+        $constraints = array();
         foreach ($where as $constraint) {
             $filter = array();
             foreach ($constraint as $field=>$value) {
diff --git a/include/class.osticket.php b/include/class.osticket.php
index 467831d5b6e4a10720010d3a5f2a223a91257087..10f16bf95c370273799e0c5dc85b063671a07db1 100644
--- a/include/class.osticket.php
+++ b/include/class.osticket.php
@@ -367,7 +367,7 @@ class osTicket {
          * Secondly, if the directory of main.inc.php is the same as the
          * document root, the the ROOT path truly is '/'
          */
-        if(!$_SERVER['DOCUMENT_ROOT']
+        if(!isset($_SERVER['DOCUMENT_ROOT'])
                 || !strcasecmp($_SERVER['DOCUMENT_ROOT'], $dir))
             return '/';
 
@@ -411,7 +411,9 @@ class osTicket {
     /* returns true if script is being executed via commandline */
     function is_cli() {
         return (!strcasecmp(substr(php_sapi_name(), 0, 3), 'cli')
-                || (!$_SERVER['REQUEST_METHOD'] && !$_SERVER['HTTP_HOST']) //Fallback when php-cgi binary is used via cli
+                || (!isset($_SERVER['REQUEST_METHOD']) &&
+                    !isset($_SERVER['HTTP_HOST']))
+                    //Fallback when php-cgi binary is used via cli
                 );
     }
 
diff --git a/include/class.thread.php b/include/class.thread.php
index eca16c58e595f57d597c4535658e36fb13e8e163..ef1d24ed8fdd15dc79d9e0013aadfca541ffc816 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -403,9 +403,7 @@ Class ThreadEntry {
             return null;
 
         $id=0;
-        if (!$attachment['error'] && ($id=$this->saveAttachment($attachment)))
-            $files[] = $id;
-        else {
+        if ($attachment['error'] || !($id=$this->saveAttachment($attachment))) {
             $error = $attachment['error'];
 
             if(!$error)
diff --git a/include/class.variable.php b/include/class.variable.php
index 3a71bd66de4239c47439cbd196061d1557ebf0f6..36d49e6a39accdcdee68aaa06cd8059779474e00 100644
--- a/include/class.variable.php
+++ b/include/class.variable.php
@@ -121,7 +121,9 @@ class VariableReplacer {
     function _parse($text) {
 
         $input = $text;
-        if(!preg_match_all('/'.$this->start_delim.'([A-Za-z_][\w._]+)'.$this->end_delim.'/', $input, $result))
+        $result = array();
+        if(!preg_match_all('/'.$this->start_delim.'([A-Za-z_][\w._]+)'.$this->end_delim.'/',
+                $input, $result))
             return null;
 
         $vars = array();
diff --git a/include/htmLawed.php b/include/htmLawed.php
index 28d234824fb4765e4e60544774c64732ef0d4e67..6d25f1f98a714269c3b65d87ca6f3dcb5db96dcb 100644
--- a/include/htmLawed.php
+++ b/include/htmLawed.php
@@ -1,9 +1,9 @@
 <?php
 
 /*
-htmLawed 1.1.10, 22 October 2011
+htmLawed 1.1.16, 29 August 2013
 Copyright Santosh Patnaik
-LGPL v3 license
+Dual licensed with LGPL 3 and GPL 2+
 A PHP Labware internal utility; www.bioinformatics.org/phplabware/internal_utilities/htmLawed
 
 See htmLawed_README.txt/htm
@@ -68,7 +68,7 @@ $C['css_expression'] = empty($C['css_expression']) ? 0 : 1;
 $C['direct_list_nest'] = empty($C['direct_list_nest']) ? 0 : 1;
 $C['hexdec_entity'] = isset($C['hexdec_entity']) ? $C['hexdec_entity'] : 1;
 $C['hook'] = (!empty($C['hook']) && function_exists($C['hook'])) ? $C['hook'] : 0;
-$C['hook_tag'] = (!empty($C['hook_tag']) && function_exists($C['hook_tag'])) ? $C['hook_tag'] : 0;
+$C['hook_tag'] = (!empty($C['hook_tag']) && is_callable($C['hook_tag'])) ? $C['hook_tag'] : 0;
 $C['keep_bad'] = isset($C['keep_bad']) ? $C['keep_bad'] : 6;
 $C['lc_std_val'] = isset($C['lc_std_val']) ? (bool)$C['lc_std_val'] : 1;
 $C['make_tag_strict'] = isset($C['make_tag_strict']) ? $C['make_tag_strict'] : 1;
@@ -194,7 +194,10 @@ for($i=-1, $ci=count($t); ++$i<$ci;){
   echo '&lt;', $s, $e, $a, '&gt;';
  }
  if(isset($x[0])){
-  if($do < 3 or isset($ok['#pcdata'])){echo $x;}
+  if(strlen(trim($x)) && (($ql && isset($cB[$p])) or (isset($cB[$in]) && !$ql))){
+   echo '<div>', $x, '</div>';
+  }
+  elseif($do < 3 or isset($ok['#pcdata'])){echo $x;}
   elseif(strpos($x, "\x02\x04")){
    foreach(preg_split('`(\x01\x02[^\x01\x02]+\x02\x01)`', $x, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $v){
     echo (substr($v, 0, 2) == "\x01\x02" ? $v : ($do > 4 ? preg_replace('`\S`', '', $v) : ''));
@@ -202,14 +205,14 @@ for($i=-1, $ci=count($t); ++$i<$ci;){
   }elseif($do > 4){echo preg_replace('`\S`', '', $x);}
  }
  // get markup
- if(!preg_match('`^(/?)([a-zA-Z1-6]+)([^>]*)>(.*)`sm', $t[$i], $r)){$x = $t[$i]; continue;}
+ if(!preg_match('`^(/?)([a-z1-6]+)([^>]*)>(.*)`sm', $t[$i], $r)){$x = $t[$i]; continue;}
  $s = null; $e = null; $a = null; $x = null; list($all, $s, $e, $a, $x) = $r;
  // close tag
  if($s){
   if(isset($cE[$e]) or !in_array($e, $q)){continue;} // Empty/unopen
   if($p == $e){array_pop($q); echo '</', $e, '>'; unset($e); continue;} // Last open
   $add = ''; // Nesting - close open tags that need to be
-  for($j=-1, $cj=count($q); ++$j<$cj;){  
+  for($j=-1, $cj=count($q); ++$j<$cj;){
    if(($d = array_pop($q)) == $e){break;}
    else{$add .= "</{$d}>";}
   }
@@ -333,7 +336,7 @@ $c = isset($C['schemes'][$c]) ? $C['schemes'][$c] : $C['schemes']['*'];
 static $d = 'denied:';
 if(isset($c['!']) && substr($p, 0, 7) != $d){$p = "$d$p";}
 if(isset($c['*']) or !strcspn($p, '#?;') or (substr($p, 0, 7) == $d)){return "{$b}{$p}{$a}";} // All ok, frag, query, param
-if(preg_match('`^([a-z\d\-+.&#; ]+?)(:|&#(58|x3a);|%3a|\\\\0{0,4}3a).`i', $p, $m) && !isset($c[strtolower($m[1])])){ // Denied prot
+if(preg_match('`^([^:?[@!$()*,=/\'\]]+?)(:|&#(58|x3a);|%3a|\\\\0{0,4}3a).`i', $p, $m) && !isset($c[strtolower($m[1])])){ // Denied prot
  return "{$b}{$d}{$p}{$a}";
 }
 if($C['abs_url']){
@@ -376,7 +379,7 @@ return $r;
 function hl_spec($t){
 // final $spec
 $s = array();
-$t = str_replace(array("\t", "\r", "\n", ' '), '', preg_replace('/"(?>(`.|[^"])*)"/sme', 'substr(str_replace(array(";", "|", "~", " ", ",", "/", "(", ")", \'`"\'), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\""), "$0"), 1, -1)', trim($t))); 
+$t = str_replace(array("\t", "\r", "\n", ' '), '', preg_replace('/"(?>(`.|[^"])*)"/sme', 'substr(str_replace(array(";", "|", "~", " ", ",", "/", "(", ")", \'`"\'), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\""), "$0"), 1, -1)', trim($t)));
 for($i = count(($t = explode(';', $t))); --$i>=0;){
  $w = $t[$i];
  if(empty($w) or ($e = strpos($w, '=')) === false or !strlen(($a =  substr($w, $e+1)))){continue;}
@@ -427,7 +430,7 @@ if($C['make_tag_strict'] && isset($eD[$e])){
 // close tag
 static $eE = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1); // Empty ele
 if(!empty($m[1])){
- return (!isset($eE[$e]) ? "</$e>" : (($C['keep_bad'])%2 ? str_replace(array('<', '>'), array('&lt;', '&gt;'), $t) : ''));
+ return (!isset($eE[$e]) ? (empty($C['hook_tag']) ? "</$e>" : $C['hook_tag']($e)) : (($C['keep_bad'])%2 ? str_replace(array('<', '>'), array('&lt;', '&gt;'), $t) : ''));
 }
 
 // open tag & attr
@@ -470,8 +473,8 @@ while(strlen($a)){
     $aA[$nm] = '';
    }
   break; case 2: // Val
-   if(preg_match('`^"[^"]*"`', $a, $m) or preg_match("`^'[^']*'`", $a, $m) or preg_match("`^\s*[^\s\"']+`", $a, $m)){
-    $m = $m[0]; $w = 1; $mode = 0; $a = ltrim(substr_replace($a, '', 0, strlen($m)));
+   if(preg_match('`^((?:"[^"]*")|(?:\'[^\']*\')|(?:\s*[^\s"\']+))(.*)`', $a, $m)){
+    $a = ltrim($m[2]); $m = $m[1]; $w = 1; $mode = 0;
     $aA[$nm] = trim(($m[0] == '"' or $m[0] == '\'') ? substr($m, 1, -1) : $m);
    }
   break;
@@ -488,7 +491,7 @@ global $S;
 $rl = isset($S[$e]) ? $S[$e] : array();
 $a = array(); $nfr = 0;
 foreach($aA as $k=>$v){
- if(((isset($C['deny_attribute']['*']) ? isset($C['deny_attribute'][$k]) : !isset($C['deny_attribute'][$k])) or isset($rl[$k])) && ((!isset($rl['n'][$k]) && !isset($rl['n']['*'])) or isset($rl[$k])) && (isset($aN[$k][$e]) or (isset($aNU[$k]) && !isset($aNU[$k][$e])))){
+  if(((isset($C['deny_attribute']['*']) ? isset($C['deny_attribute'][$k]) : !isset($C['deny_attribute'][$k])) && (isset($aN[$k][$e]) or (isset($aNU[$k]) && !isset($aNU[$k][$e]))) && !isset($rl['n'][$k]) && !isset($rl['n']['*'])) or isset($rl[$k])){
   if(isset($aNE[$k])){$v = $k;}
   elseif(!empty($lcase) && (($e != 'button' or $e != 'input') or $k == 'type')){ // Rather loose but ?not cause issues
    $v = (isset($aNL[($v2 = strtolower($v))])) ? $v2 : $v;
@@ -622,7 +625,7 @@ if($e == 'u'){$e = 'span'; return 'text-decoration: underline;';}
 static $fs = array('0'=>'xx-small', '1'=>'xx-small', '2'=>'small', '3'=>'medium', '4'=>'large', '5'=>'x-large', '6'=>'xx-large', '7'=>'300%', '-1'=>'smaller', '-2'=>'60%', '+1'=>'larger', '+2'=>'150%', '+3'=>'200%', '+4'=>'300%');
 if($e == 'font'){
  $a2 = '';
- if(preg_match('`face\s*=\s*(\'|")([^=]+?)\\1`i', $a, $m) or preg_match('`face\s*=\s*([^"])(\S+)`i', $a, $m)){
+ if(preg_match('`face\s*=\s*(\'|")([^=]+?)\\1`i', $a, $m) or preg_match('`face\s*=(\s*)(\S+)`i', $a, $m)){
   $a2 .= ' font-family: '. str_replace('"', '\'', trim($m[2])). ';';
  }
  if(preg_match('`color\s*=\s*(\'|")?(.+?)(\\1|\s|$)`i', $a, $m)){
@@ -641,41 +644,50 @@ return '';
 function hl_tidy($t, $w, $p){
 // Tidy/compact HTM
 if(strpos(' pre,script,textarea', "$p,")){return $t;}
-$t = str_replace(' </', '</', preg_replace(array('`(<\w[^>]*(?<!/)>)\s+`', '`\s+`', '`(<\w[^>]*(?<!/)>) `'), array(' $1', ' ', '$1'), preg_replace_callback(array('`(<(!\[CDATA\[))(.+?)(\]\]>)`sm', '`(<(!--))(.+?)(-->)`sm', '`(<(pre|script|textarea)[^>]*?>)(.+?)(</\2>)`sm'), create_function('$m', 'return $m[1]. str_replace(array("<", ">", "\n", "\r", "\t", " "), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), $m[3]). $m[4];'), $t)));
+$t = preg_replace('`\s+`', ' ', preg_replace_callback(array('`(<(!\[CDATA\[))(.+?)(\]\]>)`sm', '`(<(!--))(.+?)(-->)`sm', '`(<(pre|script|textarea)[^>]*?>)(.+?)(</\2>)`sm'), create_function('$m', 'return $m[1]. str_replace(array("<", ">", "\n", "\r", "\t", " "), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), $m[3]). $m[4];'), $t));
 if(($w = strtolower($w)) == -1){
  return str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), array('<', '>', "\n", "\r", "\t", ' '), $t);
 }
 $s = strpos(" $w", 't') ? "\t" : ' ';
 $s = preg_match('`\d`', $w, $m) ? str_repeat($s, $m[0]) : str_repeat($s, ($s == "\t" ? 1 : 2));
-$n = preg_match('`[ts]([1-9])`', $w, $m) ? $m[1] : 0;
+$N = preg_match('`[ts]([1-9])`', $w, $m) ? $m[1] : 0;
 $a = array('br'=>1);
-$b = array('button'=>1, 'input'=>1, 'option'=>1);
+$b = array('button'=>1, 'input'=>1, 'option'=>1, 'param'=>1);
 $c = array('caption'=>1, 'dd'=>1, 'dt'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'isindex'=>1, 'label'=>1, 'legend'=>1, 'li'=>1, 'object'=>1, 'p'=>1, 'pre'=>1, 'td'=>1, 'textarea'=>1, 'th'=>1);
-$d = array('address'=>1, 'blockquote'=>1, 'center'=>1, 'colgroup'=>1, 'dir'=>1, 'div'=>1, 'dl'=>1, 'fieldset'=>1, 'form'=>1, 'hr'=>1, 'iframe'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'optgroup'=>1, 'rbc'=>1, 'rtc'=>1, 'ruby'=>1, 'script'=>1, 'select'=>1, 'table'=>1, 'tfoot'=>1, 'thead'=>1, 'tr'=>1, 'ul'=>1);
-ob_start();
-if(isset($d[$p])){echo str_repeat($s, ++$n);}
-$t = explode('<', $t);
-echo ltrim(array_shift($t));
-for($i=-1, $j=count($t); ++$i<$j;){
- $r = ''; list($e, $r) = explode('>', $t[$i]);
- $x = $e[0] == '/' ? 0 : (substr($e, -1) == '/' ? 1 : ($e[0] != '!' ? 2 : -1));
- $y = !$x ? ltrim($e, '/') : ($x > 0 ? substr($e, 0, strcspn($e, ' ')) : 0);
- $e = "<$e>"; 
- if(isset($d[$y])){
-  if(!$x){echo "\n", str_repeat($s, --$n), "$e\n", str_repeat($s, $n);}
-  else{echo "\n", str_repeat($s, $n), "$e\n", str_repeat($s, ($x != 1 ? ++$n : $n));}
-  echo ltrim($r); continue;
+$d = array('address'=>1, 'blockquote'=>1, 'center'=>1, 'colgroup'=>1, 'dir'=>1, 'div'=>1, 'dl'=>1, 'fieldset'=>1, 'form'=>1, 'hr'=>1, 'iframe'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'optgroup'=>1, 'rbc'=>1, 'rtc'=>1, 'ruby'=>1, 'script'=>1, 'select'=>1, 'table'=>1, 'tbody'=>1, 'tfoot'=>1, 'thead'=>1, 'tr'=>1, 'ul'=>1);
+$T = explode('<', $t);
+$X = 1;
+while($X){
+ $n = $N;
+ $t = $T;
+ ob_start();
+ if(isset($d[$p])){echo str_repeat($s, ++$n);}
+ echo ltrim(array_shift($t));
+ for($i=-1, $j=count($t); ++$i<$j;){
+  $r = ''; list($e, $r) = explode('>', $t[$i]);
+  $x = $e[0] == '/' ? 0 : (substr($e, -1) == '/' ? 1 : ($e[0] != '!' ? 2 : -1));
+  $y = !$x ? ltrim($e, '/') : ($x > 0 ? substr($e, 0, strcspn($e, ' ')) : 0);
+  $e = "<$e>";
+  if(isset($d[$y])){
+   if(!$x){
+    if($n){echo "\n", str_repeat($s, --$n), "$e\n", str_repeat($s, $n);}
+    else{++$N; ob_end_clean(); continue 2;}
+   }
+   else{echo "\n", str_repeat($s, $n), "$e\n", str_repeat($s, ($x != 1 ? ++$n : $n));}
+   echo $r; continue;
+  }
+  $f = "\n". str_repeat($s, $n);
+  if(isset($c[$y])){
+   if(!$x){echo $e, $f, $r;}
+   else{echo $f, $e, $r;}
+  }elseif(isset($b[$y])){echo $f, $e, $r;
+  }elseif(isset($a[$y])){echo $e, $f, $r;
+  }elseif(!$y){echo $f, $e, $f, $r;
+  }else{echo $e, $r;}
  }
- $f = "\n". str_repeat($s, $n);
- if(isset($c[$y])){
-  if(!$x){echo $e, $f, ltrim($r);}
-  else{echo $f, $e, $r;}
- }elseif(isset($b[$y])){echo $f, $e, $r;
- }elseif(isset($a[$y])){echo $e, $f, ltrim($r);
- }elseif(!$y){echo $f, $e, $f, ltrim($r);
- }else{echo $e, $r;}
-}
-$t = preg_replace('`[\n]\s*?[\n]+`', "\n", ob_get_contents());
+ $X = 0;
+}
+$t = str_replace(array("\n ", " \n"), "\n", preg_replace('`[\n]\s*?[\n]+`', "\n", ob_get_contents()));
 ob_end_clean();
 if(($l = strpos(" $w", 'r') ? (strpos(" $w", 'n') ? "\r\n" : "\r") : 0)){
  $t = str_replace("\n", $l, $t);
@@ -686,7 +698,7 @@ return str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), array(
 
 function hl_version(){
 // rel
-return '1.1.10';
+return '1.1.16';
 // eof
 }
 
@@ -708,4 +720,4 @@ function kses_hook($t, &$C, &$S){
 // kses compat
 return $t;
 // eof
-}
\ No newline at end of file
+}
diff --git a/include/html2text.php b/include/html2text.php
index a4997d1b2b978131b5ad2d155035abf981c40898..f0fdf4fefdda0a1abd87b47cc5fdecbe9d07ff8a 100644
--- a/include/html2text.php
+++ b/include/html2text.php
@@ -25,8 +25,8 @@
  * @return the HTML converted, as best as possible, to text
  */
 function convert_html_to_text($html, $width=74) {
-    $html = fix_newlines($html);
 
+    $html = fix_newlines($html);
     $doc = new DOMDocument('1.0', 'utf-8');
     if (!@$doc->loadHTML($html))
         return $html;
@@ -151,6 +151,7 @@ class HtmlInlineElement {
     var $children = array();
     var $style = false;
     var $stylesheets = array();
+    var $footnotes = array();
     var $ws = false;
 
     function __construct($node, $parent) {
@@ -200,6 +201,11 @@ class HtmlInlineElement {
             elseif (is_string($more))
                 $output .= $more;
         }
+        if ($this->footnotes) {
+            $output .= "\n\n" . str_repeat('-', $width/2) . "\n";
+            foreach ($this->footnotes as $name=>$content)
+                $output .= "[$name] ".$content."\n";
+        }
         return $output;
     }
 
@@ -253,6 +259,10 @@ class HtmlInlineElement {
     function addStylesheet(&$s) {
         $this->stylesheets[] = $s;
     }
+
+    function addFootNote($name, $content) {
+        $this->footnotes[$name] = $content;
+    }
 }
 
 class HtmlBlockElement extends HtmlInlineElement {
@@ -397,7 +407,9 @@ class HtmlImgElement extends HtmlInlineElement {
         $alt = $this->node->getAttribute("alt");
         return "[image:$alt$title] ";
     }
-    function getWeight() { return parent::getWeight() + 4; }
+    function getWeight() {
+        return strlen($this->node->getAttribute("alt")) + 8;
+    }
 }
 
 class HtmlAElement extends HtmlInlineElement {
@@ -410,6 +422,10 @@ class HtmlAElement extends HtmlInlineElement {
             if ($this->node->getAttribute("name") != null) {
                 $output = "[$output]";
             }
+        } elseif (strlen($href) > $width / 2) {
+            $output = "[$output][]";
+            if ($href != $output)
+                $this->getRoot()->addFootnote($output, $href);
         } else {
             if ($href != $output) {
                 $output = "[$output]($href)";
@@ -627,7 +643,7 @@ class HtmlTable extends HtmlBlockElement {
                 # Stash the computed width so it doesn't need to be
                 # recomputed again below
                 $cell->width = $cwidth;
-                unset($data); # nolint
+                unset($data);
                 $data = explode("\n", $cell->render($cwidth, $options));
                 $heights[$y] = max(count($data), $heights[$y]);
                 $contents[$y][$i] = &$data;
@@ -687,7 +703,7 @@ class HtmlTableCell extends HtmlBlockElement {
     }
 
     function getMinWidth() {
-        return parent::getMinWidth() / $this->cols;
+        return max(4, parent::getMinWidth() / $this->cols);
     }
 }
 
diff --git a/include/mysql.php b/include/mysql.php
index 4e3bd7eb8caf242c92b3846fb1b7d5c845f31a87..95be46f5a93b8ee239f13fa441bd94494dad35bc 100644
--- a/include/mysql.php
+++ b/include/mysql.php
@@ -51,10 +51,11 @@
     function db_version() {
 
         $version=0;
+        $matches = array();
         if(preg_match('/(\d{1,2}\.\d{1,2}\.\d{1,2})/',
                 mysql_result(db_query('SELECT VERSION()'),0,0),
-                $matches))                                      # nolint
-            $version=$matches[1];                               # nolint
+                $matches))
+            $version=$matches[1];
 
         return $version;
     }
@@ -129,6 +130,7 @@
     }
 
     function db_assoc_array($res, $mode=false) {
+        $result = array();
 	    if($res && db_num_rows($res)) {
       	    while ($row=db_fetch_array($res, $mode))
          	    $result[]=$row;
diff --git a/include/mysqli.php b/include/mysqli.php
index 06d4032fdc5419f3ffd24bd5a0fe6abe2ea46fb4..7d765b428ac35b350dc9c9f4dd58f3e6b22af5c7 100644
--- a/include/mysqli.php
+++ b/include/mysqli.php
@@ -30,7 +30,7 @@ function db_connect($host, $user, $passwd, $options = array()) {
 
     // Setup SSL if enabled
     if (isset($options['ssl']))
-        $__db->ssl_set( # nolint
+        $__db->ssl_set(
                 $options['ssl']['key'],
                 $options['ssl']['cert'],
                 $options['ssl']['ca'],
@@ -50,16 +50,16 @@ function db_connect($host, $user, $passwd, $options = array()) {
 
     // Connect
     $start = microtime(true);
-    if (!@$__db->real_connect($host, $user, $passwd, null, $port)) # nolint
+    if (!@$__db->real_connect($host, $user, $passwd, null, $port))
         return NULL;
 
     //Select the database, if any.
-    if(isset($options['db'])) $__db->select_db($options['db']); # nolint
+    if(isset($options['db'])) $__db->select_db($options['db']);
 
     //set desired encoding just in case mysql charset is not UTF-8 - Thanks to FreshMedia
-    @$__db->query('SET NAMES "utf8"');                          # nolint
-    @$__db->query('SET CHARACTER SET "utf8"');                  # nolint
-    @$__db->query('SET COLLATION_CONNECTION=utf8_general_ci');  # nolint
+    @$__db->query('SET NAMES "utf8"');
+    @$__db->query('SET CHARACTER SET "utf8"');
+    @$__db->query('SET COLLATION_CONNECTION=utf8_general_ci');
 
     @db_set_variable('sql_mode', '');
 
@@ -77,10 +77,11 @@ function db_close() {
 function db_version() {
 
     $version=0;
+    $matches = array();
     if(preg_match('/(\d{1,2}\.\d{1,2}\.\d{1,2})/',
             db_result(db_query('SELECT VERSION()')),
-            $matches))                                      # nolint
-        $version=$matches[1];                               # nolint
+            $matches))
+        $version=$matches[1];
 
     return $version;
 }
@@ -102,13 +103,13 @@ function db_set_variable($variable, $value, $type='session') {
 
 function db_select_database($database) {
     global $__db;
-    return ($database && @$__db->select_db($database)); # nolint
+    return ($database && @$__db->select_db($database));
 }
 
 function db_create_database($database, $charset='utf8',
         $collate='utf8_general_ci') {
     global $__db;
-    return @$__db->query( # nolint
+    return @$__db->query(
         sprintf('CREATE DATABASE %s DEFAULT CHARACTER SET %s COLLATE %s',
             $database, $charset, $collate));
 }
@@ -147,24 +148,25 @@ function db_result($res, $row=0) {
     if (!$res)
         return NULL;
 
-    $res->data_seek($row); # nolint
+    $res->data_seek($row);
     list($value) = db_output($res->fetch_row());
     return $value;
 }
 
 function db_fetch_array($res, $mode=MYSQL_ASSOC) {
-    return ($res) ? db_output($res->fetch_array($mode)) : NULL; # nolint
+    return ($res) ? db_output($res->fetch_array($mode)) : NULL;
 }
 
 function db_fetch_row($res) {
-    return ($res) ? db_output($res->fetch_row()) : NULL; # nolint
+    return ($res) ? db_output($res->fetch_row()) : NULL;
 }
 
 function db_fetch_field($res) {
-    return ($res) ? $res->fetch_field() : NULL; # nolint
+    return ($res) ? $res->fetch_field() : NULL;
 }
 
 function db_assoc_array($res, $mode=false) {
+    $result = array();
     if($res && db_num_rows($res)) {
         while ($row=db_fetch_array($res, $mode))
             $result[]=$row;
@@ -173,7 +175,7 @@ function db_assoc_array($res, $mode=false) {
 }
 
 function db_num_rows($res) {
-    return ($res) ? $res->num_rows : 0; # nolint
+    return ($res) ? $res->num_rows : 0;
 }
 
 function db_affected_rows() {
@@ -182,7 +184,7 @@ function db_affected_rows() {
 }
 
 function db_data_seek($res, $row_number) {
-    return ($res && $res->data_seek($row_number)); # nolint
+    return ($res && $res->data_seek($row_number));
 }
 
 function db_data_reset($res) {
@@ -195,7 +197,7 @@ function db_insert_id() {
 }
 
 function db_free_result($res) {
-    return ($res && $res->free()); # nolint
+    return ($res && $res->free());
 }
 
 function db_output($var) {
@@ -232,7 +234,7 @@ function db_input($var, $quote=true) {
 
 function db_field_type($res, $col=0) {
     global $__db;
-    return $res->fetch_field_direct($col); # nolint
+    return $res->fetch_field_direct($col);
 }
 
 function db_prepare($stmt) {
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index a3ef79d5d4d29f0752ef720c41cd769ab318d6df..aecf2edd6f2cfc9fa422b1826eaaafeaace1b981 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -444,12 +444,12 @@ if(!$cfg->showNotesInline()) { ?>
         <input type="hidden" name="msgId" value="<?php echo $msgId; ?>">
         <input type="hidden" name="a" value="reply">
         <span class="error"></span>
-        <table border="0" cellspacing="0" cellpadding="3">
+        <table style="width:100%" border="0" cellspacing="0" cellpadding="3">
             <tr>
-                <td width="160">
+                <td width="120">
                     <label><strong>TO:</strong></label>
                 </td>
-                <td width="765">
+                <td>
                     <?php
                     $to = $ticket->getReplyToEmail();
                     if(($name=$ticket->getName()) && !strpos($name,'@'))
@@ -463,14 +463,14 @@ if(!$cfg->showNotesInline()) { ?>
             </tr>
             <?php
             if($errors['response']) {?>
-            <tr><td width="160">&nbsp;</td><td class="error"><?php echo $errors['response']; ?>&nbsp;</td></tr>
+            <tr><td width="120">&nbsp;</td><td class="error"><?php echo $errors['response']; ?>&nbsp;</td></tr>
             <?php
             }?>
             <tr>
-                <td width="160">
+                <td width="120" style="vertical-align:top">
                     <label><strong>Response:</strong></label>
                 </td>
-                <td width="765">
+                <td>
                     <?php
                     if(($cannedResponses=Canned::responsesByDeptId($ticket->getDeptId()))) {?>
                         <select id="cannedResp" name="cannedResp">
@@ -489,6 +489,7 @@ if(!$cfg->showNotesInline()) { ?>
                     <input type="hidden" name="draft_id" value=""/>
                     <textarea name="response" id="response" cols="50"
                         data-draft-namespace="ticket.response"
+                        placeholder="Start writing your response here. Use canned responses from the drop-down above"
                         data-draft-object-id="<?php echo $ticket->getId(); ?>"
                         rows="9" wrap="soft"
                         class="richtext ifhtml draft"><?php
@@ -498,10 +499,10 @@ if(!$cfg->showNotesInline()) { ?>
             <?php
             if($cfg->allowAttachments()) { ?>
             <tr>
-                <td width="160">
+                <td width="120">
                     <label for="attachment">Attachments:</label>
                 </td>
-                <td width="765" id="reply_form_attachments" class="attachments">
+                <td id="reply_form_attachments" class="attachments">
                     <div class="canned_attachments">
                     </div>
                     <div class="uploads">
@@ -514,10 +515,10 @@ if(!$cfg->showNotesInline()) { ?>
             <?php
             }?>
             <tr>
-                <td width="160">
+                <td width="120">
                     <label for="signature" class="left">Signature:</label>
                 </td>
-                <td width="765">
+                <td>
                     <?php
                     $info['signature']=$info['signature']?$info['signature']:$thisstaff->getDefaultSignatureType();
                     ?>
@@ -540,10 +541,10 @@ if(!$cfg->showNotesInline()) { ?>
             <?php
             if($ticket->isClosed() || $thisstaff->canCloseTickets()) { ?>
             <tr>
-                <td width="160">
+                <td width="120">
                     <label><strong>Ticket Status:</strong></label>
                 </td>
-                <td width="765">
+                <td>
                     <?php
                     $statusChecked=isset($info['reply_ticket_status'])?'checked="checked"':'';
                     if($ticket->isClosed()) { ?>
@@ -573,42 +574,45 @@ if(!$cfg->showNotesInline()) { ?>
         <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>">
         <input type="hidden" name="locktime" value="<?php echo $cfg->getLockTime(); ?>">
         <input type="hidden" name="a" value="postnote">
-        <table border="0" cellspacing="0" cellpadding="3">
+        <table width="100%" border="0" cellspacing="0" cellpadding="3">
             <?php
             if($errors['postnote']) {?>
             <tr>
-                <td width="160">&nbsp;</td>
+                <td width="120">&nbsp;</td>
                 <td class="error"><?php echo $errors['postnote']; ?></td>
             </tr>
             <?php
             } ?>
             <tr>
-                <td width="160">
-                    <label><strong>Internal Note:</strong></label>
+                <td width="120" style="vertical-align:top">
+                    <label><strong>Internal Note:</strong><span class='error'>&nbsp;*</span></label>
                 </td>
-                <td width="765">
-                    <div><span class="faded">Note details</span>&nbsp;
-                        <span class="error">*&nbsp;<?php echo $errors['note']; ?></span>
+                <td>
+                    <div>
+                        <div class="faded" style="padding-left:0.15em">
+                        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>
                     </div>
+                    <br/>
                     <textarea name="note" id="internal_note" cols="80"
+                        placeholder="Note details"
                         rows="9" wrap="soft" data-draft-namespace="ticket.note"
                         data-draft-object-id="<?php echo $ticket->getId(); ?>"
                         class="richtext ifhtml draft"><?php echo $info['note'];
-                        ?></textarea><br>
-                    <div>
-                        <span class="faded">Note title - summary of the note (optional)</span>&nbsp;
-                        <span class="error"&nbsp;<?php echo $errors['title']; ?></span>
-                    </div>
-                    <input type="text" name="title" id="title" size="60" value="<?php echo $info['title']; ?>" >
+                        ?></textarea>
+                        <span class="error"><?php echo $errors['note']; ?></span>
+                        <br>
                 </td>
             </tr>
             <?php
             if($cfg->allowAttachments()) { ?>
             <tr>
-                <td width="160">
+                <td width="120">
                     <label for="attachment">Attachments:</label>
                 </td>
-                <td width="765" class="attachments">
+                <td class="attachments">
                     <div class="uploads">
                     </div>
                     <div class="file_input">
@@ -621,10 +625,10 @@ if(!$cfg->showNotesInline()) { ?>
             ?>
             <tr><td colspan="2">&nbsp;</td></tr>
             <tr>
-                <td width="160">
+                <td width="120">
                     <label>Ticket Status:</label>
                 </td>
-                <td width="765">
+                <td>
                     <div class="faded"></div>
                     <select name="state">
                         <option value="" selected="selected">&mdash; unchanged &mdash;</option>
@@ -683,21 +687,21 @@ if(!$cfg->showNotesInline()) { ?>
         <?php csrf_token(); ?>
         <input type="hidden" name="ticket_id" value="<?php echo $ticket->getId(); ?>">
         <input type="hidden" name="a" value="transfer">
-        <table border="0" cellspacing="0" cellpadding="3">
+        <table width="100%" border="0" cellspacing="0" cellpadding="3">
             <?php
             if($errors['transfer']) {
                 ?>
             <tr>
-                <td width="160">&nbsp;</td>
+                <td width="120">&nbsp;</td>
                 <td class="error"><?php echo $errors['transfer']; ?></td>
             </tr>
             <?php
             } ?>
             <tr>
-                <td width="160">
+                <td width="120">
                     <label for="deptId"><strong>Department:</strong></label>
                 </td>
-                <td width="765">
+                <td>
                     <?php
                         echo sprintf('<span class="faded">Ticket is currently in <b>%s</b> department.</span>', $ticket->getDeptName());
                     ?>
@@ -717,15 +721,15 @@ if(!$cfg->showNotesInline()) { ?>
                 </td>
             </tr>
             <tr>
-                <td width="160">
-                    <label><strong>Comments:</strong></label>
+                <td width="120" style="vertical-align:top">
+                    <label><strong>Comments:</strong><span class='error'>&nbsp;*</span></label>
                 </td>
-                <td width="765">
-                    <span class="faded">Enter reasons for the transfer.</span>
-                    <span class="error">*&nbsp;<?php echo $errors['transfer_comments']; ?></span><br>
+                <td>
                     <textarea name="transfer_comments" id="transfer_comments"
+                        placeholder="Enter reasons for the transfer"
                         class="richtext ifhtml no-bar" cols="80" rows="7" wrap="soft"><?php
                         echo $info['transfer_comments']; ?></textarea>
+                    <span class="error"><?php echo $errors['transfer_comments']; ?></span>
                 </td>
             </tr>
         </table>
@@ -742,31 +746,22 @@ if(!$cfg->showNotesInline()) { ?>
         <?php csrf_token(); ?>
         <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>">
         <input type="hidden" name="a" value="assign">
-        <table border="0" cellspacing="0" cellpadding="3">
+        <table style="width:100%" border="0" cellspacing="0" cellpadding="3">
 
             <?php
             if($errors['assign']) {
                 ?>
             <tr>
-                <td width="160">&nbsp;</td>
+                <td width="120">&nbsp;</td>
                 <td class="error"><?php echo $errors['assign']; ?></td>
             </tr>
             <?php
             } ?>
             <tr>
-                <td width="160">
+                <td width="120" style="vertical-align:top">
                     <label for="assignId"><strong>Assignee:</strong></label>
                 </td>
-                <td width="765">
-                    <?php
-                    if($ticket->isAssigned() && $ticket->isOpen()) {
-                        echo sprintf('<span class="faded">Ticket is currently assigned to <b>%s</b></span>',
-                                $ticket->getAssignee());
-                    } else {
-                        echo '<span class="faded">Assigning a closed ticket will <b>reopen</b> it!</span>';
-                    }
-                    ?>
-                    <br>
+                <td>
                     <select id="assignId" name="assignId">
                         <option value="0" selected="selected">&mdash; Select Staff Member OR a Team &mdash;</option>
                         <?php
@@ -803,17 +798,25 @@ if(!$cfg->showNotesInline()) { ?>
                         }
                         ?>
                     </select>&nbsp;<span class='error'>*&nbsp;<?php echo $errors['assignId']; ?></span>
+                    <?php
+                    if($ticket->isAssigned() && $ticket->isOpen()) {
+                        echo sprintf('<div class="faded">Ticket is currently assigned to <b>%s</b></div>',
+                                $ticket->getAssignee());
+                    } elseif ($ticket->isClosed()) { ?>
+                        <div class="faded">Assigning a closed ticket will <b>reopen</b> it!</div>
+                    <?php } ?>
                 </td>
             </tr>
             <tr>
-                <td width="160">
-                    <label><strong>Comments:</strong><span class='error'>&nbsp;</span></label>
+                <td width="120" style="vertical-align:top">
+                    <label><strong>Comments:</strong><span class='error'>&nbsp;*</span></label>
                 </td>
-                <td width="765">
-                    <span class="faded">Enter reasons for the assignment or instructions for assignee.</span>
-                    <span class="error">*&nbsp;<?php echo $errors['assign_comments']; ?></span><br>
-                    <textarea name="assign_comments" id="assign_comments" cols="80" rows="7" wrap="soft"
+                <td>
+                    <textarea name="assign_comments" id="assign_comments"
+                        cols="80" rows="7" wrap="soft"
+                        placeholder="Enter reasons for the assignment or instructions for assignee"
                         class="richtext ifhtml no-bar"><?php echo $info['assign_comments']; ?></textarea>
+                    <span class="error"><?php echo $errors['assign_comments']; ?></span><br>
                 </td>
             </tr>
         </table>
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 6c0eb1b26bceb46945710ad2768d7a9c69450959..0e0a7ec0df709188d85c743d9629f52f59a86eb7 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -76,6 +76,11 @@ if($status) {
     $qwhere.=' AND status='.db_input(strtolower($status));
 }
 
+if (isset($_REQUEST['ownerId'])) {
+    $qwhere .= ' AND ticket.user_id='.db_input($_REQUEST['ownerId']);
+    $qstr .= '&ownerId='.urlencode($_REQUEST['ownerId']);
+}
+
 //Queues: Overloaded sub-statuses  - you've got to just have faith!
 if($staffId && ($staffId==$thisstaff->getId())) { //My tickets
     $results_type='Assigned Tickets';
diff --git a/include/upgrader/streams/core/d51f303a-dad45ca2.patch.sql b/include/upgrader/streams/core/d51f303a-dad45ca2.patch.sql
index 94b8f8aa7fcd59f202bcc281324c58894f160a72..19ddac83bf11b1d262c167e6f73b190db63babb7 100644
--- a/include/upgrader/streams/core/d51f303a-dad45ca2.patch.sql
+++ b/include/upgrader/streams/core/d51f303a-dad45ca2.patch.sql
@@ -47,89 +47,92 @@ CREATE TABLE `%TABLE_PREFIX%draft` (
 UPDATE `%TABLE_PREFIX%email_template`
     SET `body` = REPLACE( REPLACE( REPLACE( REPLACE(
         `body`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
+
+UPDATE `%TABLE_PREFIX%email_template`
+    SET `body` = CONCAT('<div>', `body`, '</div>');
 
 -- Migrate notes to HTML
 UPDATE `%TABLE_PREFIX%api_key`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 UPDATE `%TABLE_PREFIX%email`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 UPDATE `%TABLE_PREFIX%email_template_group`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 UPDATE `%TABLE_PREFIX%faq`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 UPDATE `%TABLE_PREFIX%faq_category`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 UPDATE `%TABLE_PREFIX%filter`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 UPDATE `%TABLE_PREFIX%groups`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 UPDATE `%TABLE_PREFIX%help_topic`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 UPDATE `%TABLE_PREFIX%page`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 UPDATE `%TABLE_PREFIX%sla`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 UPDATE `%TABLE_PREFIX%staff`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 UPDATE `%TABLE_PREFIX%team`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
@@ -142,16 +145,16 @@ UPDATE `%TABLE_PREFIX%team`
 UPDATE `%TABLE_PREFIX%canned_response`
     SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE(
         `notes`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
         '\n', '<br/>'),
-        '&', '&amp;'),
     `response` = REPLACE( REPLACE( REPLACE( REPLACE(
         `response`,
+        '&', '&amp;'),
         '<', '&lt;'),
         '>', '&gt;'),
-        '\n', '<br/>'),
-        '&', '&amp;');
+        '\n', '<br/>');
 
 -- Migrate ticket-thread to HTML
 -- XXX: Migrate & -> &amp; ? -- the problem is that there's a fix in 1.7.1
diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js
index 0fd53146f7e986b754f48e565476dffcf0dbbe18..486cadd7281d2467a437dbb3af9b6cde0ab3b1a1 100644
--- a/js/redactor-osticket.js
+++ b/js/redactor-osticket.js
@@ -134,7 +134,9 @@ $(function() {
                 'focus': false,
                 'plugins': ['fontcolor','fontfamily'],
                 'imageGetJson': 'ajax.php/draft/images/browse',
-                'syncBeforeCallback': captureImageSizes
+                'syncBeforeCallback': captureImageSizes,
+                'linebreaks': true,
+                'tabFocus': false
             };
         if (el.data('redactor')) return;
         if (el.hasClass('draft')) {
diff --git a/main.inc.php b/main.inc.php
index 56da2cb710261bb7fa21c04c700aa9823981f669..e62d6e5e43bec6fed2bdee435005c9eae94c6d44 100644
--- a/main.inc.php
+++ b/main.inc.php
@@ -16,7 +16,9 @@ vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
 
 #Disable direct access.
-if(!strcasecmp(basename($_SERVER['SCRIPT_NAME']),basename(__FILE__))) die('kwaheri rafiki!');
+if(isset($_SERVER['SCRIPT_NAME'])
+        && !strcasecmp(basename($_SERVER['SCRIPT_NAME']),basename(__FILE__)))
+    die('kwaheri rafiki!');
 
 require('bootstrap.php');
 Bootstrap::loadConfig();
diff --git a/scp/js/ticket.js b/scp/js/ticket.js
index cbe042f4435d8bb46af55f4506947a9d04fab0ef..fb9f7370ed84ce178bdc0f233ef2ab16c066d7a4 100644
--- a/scp/js/ticket.js
+++ b/scp/js/ticket.js
@@ -428,10 +428,18 @@ showImagesInline = function(urls, thread_id) {
             e = $(el);
         if (info) {
             // Add a hover effect with the filename
-            var caption = $('<div class="image-hover">')
+            var timeout, caption = $('<div class="image-hover">')
                 .hover(
-                    function() { $(this).find('.caption').slideDown(250); },
-                    function() { $(this).find('.caption').slideUp(250); }
+                    function() {
+                        var self = this;
+                        timeout = setTimeout(
+                            function() { $(self).find('.caption').slideDown(250); },
+                            500);
+                    },
+                    function() {
+                        clearTimeout(timeout);
+                        $(this).find('.caption').slideUp(250);
+                    }
                 ).append($('<div class="caption">')
                     .append('<span class="filename">'+info.filename+'</span>')
                     .append('<a href="'+info.download_url+'" class="action-button"><i class="icon-download-alt"></i> Download</a>')
diff --git a/scp/tickets.php b/scp/tickets.php
index 455cbb41ae71df41ce49a9aa1f61ea913915b5b9..1058b80d95f67fed03e39c191292f76e5c8b2774 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -167,23 +167,25 @@ if($_POST && !$errors):
                     $errors['state'] = "You don't have permission to set the state";
             }
 
-            $wasOpen = ($ticket->isOpen());
-
             $vars = $_POST;
             if($_FILES['attachments'])
                 $vars['files'] = AttachmentFile::format($_FILES['attachments']);
 
+            $wasOpen = ($ticket->isOpen());
             if(($note=$ticket->postNote($vars, $errors, $thisstaff))) {
 
+                // Cleanup drafts for the ticket. If not closed, only clean
+                // note drafts for this staff. Else clean all drafts for the ticket.
+                Draft::deleteForNamespace(
+                        sprintf('ticket.%s.%d',
+                              $ticket->isClosed() ? '%' : 'note',
+                              $ticket->getId()),
+                        $ticket->isOpen() ? $thisstaff->getId() : false);
+
                 $msg='Internal note posted successfully';
                 if($wasOpen && $ticket->isClosed())
                     $ticket = null; //Going back to main listing.
 
-                // Cleanup drafts for the ticket. If not closed, only clean
-                // for this staff. Else clean all drafts for the ticket.
-                Draft::deleteForNamespace('ticket.%.' . $ticket->getId(),
-                    $ticket->isClosed() ? false : $thisstaff->getId());
-
             } else {
 
                 if(!$errors['err'])
diff --git a/setup/cli/modules/class.module.php b/setup/cli/modules/class.module.php
index 788cb21fbde330821ab9feb9855475ab9b156a57..437f87c609eb6b63f6799978ce324f437a456ca5 100644
--- a/setup/cli/modules/class.module.php
+++ b/setup/cli/modules/class.module.php
@@ -72,10 +72,10 @@ class Option {
         $short = explode(':', $this->short);
         $long = explode(':', $this->long);
         if ($this->nargs === '?')
-            $switches = sprintf('    %s [%3$s], %s[=%3$s]', $short[0], # nolint
+            $switches = sprintf('    %s [%3$s], %s[=%3$s]', $short[0],
                 $long[0], $this->metavar);
         elseif ($this->hasArg())
-            $switches = sprintf('    %s %3$s, %s=%3$s', $short[0], $long[0], # nolint
+            $switches = sprintf('    %s %3$s, %s=%3$s', $short[0], $long[0],
                 $this->metavar);
         else
             $switches = sprintf("    %s, %s", $short[0], $long[0]);
@@ -143,7 +143,7 @@ class Module {
 
         echo "Usage:\n";
         echo "    " . str_replace(
-                array('$script', '$args'), # nolint
+                array('$script', '$args'),
                 array($manager ." ". $this->module_name, implode(' ', array_keys($this->arguments))),
             $this->usage) . "\n";
 
diff --git a/setup/cli/modules/deploy.php b/setup/cli/modules/deploy.php
index 77798797b9e5aebd2fd3810af570cf0263170df6..8ba0c0fafbf9740957072d12ea803efb9e2c6a16 100644
--- a/setup/cli/modules/deploy.php
+++ b/setup/cli/modules/deploy.php
@@ -50,8 +50,7 @@ class Deployment extends Unpacker {
         $include = ($upgrade) ? $this->get_include_dir()
             : ($options['include'] ? $options['include']
                 : "{$this->destination}/include");
-        if (substr($include, -1) !== '/')
-            $include .= '/';
+        $include = rtrim($include, '/').'/';
 
         # Locate the upload folder
         $root = $this->find_root_folder();
@@ -67,8 +66,8 @@ class Deployment extends Unpacker {
         # Unpack the include folder
         $this->unpackage("$root/include/{,.}*", $include, -1,
             array("*/include/ost-config.php"));
-        if (!$options['dry-run'] && !$upgrade
-                 && $include != "{$this->destination}/include")
+        if (!$options['dry-run']
+                && $include != "{$this->destination}/include")
             $this->change_include_dir($include);
     }
 }
diff --git a/setup/cli/modules/import.php b/setup/cli/modules/import.php
index e0f23d57a1a11e8b8b8c24df592edc4b36cd992e..84c27396d4b790c4a5612c2146490e5da42d9090 100644
--- a/setup/cli/modules/import.php
+++ b/setup/cli/modules/import.php
@@ -113,6 +113,7 @@ class Importer extends Module {
         $sql = 'CREATE TABLE `'.TABLE_PREFIX.$info[1].'` (';
         $pk = array();
         $fields = array();
+        $queries = array();
         foreach ($info[2] as $col) {
             $field = "`{$col['Field']}` {$col['Type']}";
             if ($col['Null'] == 'NO')
diff --git a/setup/cli/modules/unpack.php b/setup/cli/modules/unpack.php
index 50156ea7e77d9ecc1627de92887ba4f6f25e5c79..55fe6ab5a35299bd4e1c3017a40b7aab8342823f 100644
--- a/setup/cli/modules/unpack.php
+++ b/setup/cli/modules/unpack.php
@@ -46,8 +46,9 @@ class Unpacker extends Module {
 
     function change_include_dir($include_path) {
         # Read the main.inc.php script
-        $main_inc_php = $this->destination . '/main.inc.php';
-        $lines = explode("\n", file_get_contents($main_inc_php));
+        $bootstrap_php = $this->destination . '/bootstrap.php';
+        $lines = explode("\n", file_get_contents($bootstrap_php));
+        $include_path = preg_replace('://+:', '/', $include_path);
         # Try and use ROOT_DIR
         if (strpos($include_path, $this->destination) === 0)
             $include_path = "ROOT_DIR . '" .
@@ -57,6 +58,7 @@ class Unpacker extends Module {
         # Find the line that defines INCLUDE_DIR
         $match = array();
         foreach ($lines as &$line) {
+            // TODO: Change THIS_VERSION inline to be current `git describe`
             if (preg_match("/(\s*)define\s*\(\s*'INCLUDE_DIR'/", $line, $match)) {
                 # Replace the definition with the new locatin
                 $line = $match[1] . "define('INCLUDE_DIR', "
@@ -65,7 +67,7 @@ class Unpacker extends Module {
                 break;
             }
         }
-        if (!file_put_contents($main_inc_php, implode("\n", $lines)))
+        if (!file_put_contents($bootstrap_php, implode("\n", $lines)))
             die("Unable to configure location of INCLUDE_DIR in main.inc.php\n");
     }
 
@@ -134,17 +136,18 @@ class Unpacker extends Module {
     }
 
     function get_include_dir() {
-        $main_inc_php = $this->destination . '/main.inc.php';
+        $bootstrap_php = $this->destination . '/bootstrap.php';
         $lines = preg_grep("/define\s*\(\s*'INCLUDE_DIR'/",
-            explode("\n", file_get_contents($main_inc_php)));
+            explode("\n", file_get_contents($bootstrap_php)));
 
         // NOTE: that this won't work for crafty folks who have a define or some
         //       variable in the value of their include path
-        if (!defined('ROOT_DIR')) define('ROOT_DIR', $this->destination . '/');
+        if (!defined('ROOT_DIR'))
+            define('ROOT_DIR', rtrim($this->destination, '/').'/');
         foreach ($lines as $line)
             eval($line);
 
-        return INCLUDE_DIR;
+        return rtrim(INCLUDE_DIR, '/').'/';
     }
 
     function run($args, $options) {
diff --git a/setup/test/tests/class.php_analyze.php b/setup/test/tests/class.php_analyze.php
new file mode 100644
index 0000000000000000000000000000000000000000..9690c980afe51162da54164cc6c1d6b1b7fd08d2
--- /dev/null
+++ b/setup/test/tests/class.php_analyze.php
@@ -0,0 +1,300 @@
+<?php
+require_once('class.test.php');
+
+class SourceAnalyzer extends Test {
+    static $super_globals = array(
+        '$_SERVER'=>1, '$_FILES'=>1, '$_SESSION'=>1, '$_GET'=>1,
+        '$_POST'=>1, '$_REQUEST'=>1, '$_ENV'=>1, '$_COOKIE'=>1);
+
+    var $bugs = array();
+    var $globals = array();
+    var $file = '';
+
+    function __construct($source) {
+        $this->tokens = token_get_all(file_get_contents($source));
+        $this->file = $source;
+    }
+
+    function parseFile() {
+        $this->checkVariableUsage(
+            array('line'=>array(0, $this->file), 'name'=>'(main)'),
+            array(),
+            1);
+    }
+
+    function traverseClass($line) {
+        $class = array('line'=>$line);
+        $token = false;
+        $blocks = 0;
+        while (list($i,$token) = each($this->tokens)) {
+            switch ($token[0]) {
+            case '{':
+                $blocks++;
+                break;
+            case '}':
+                if (--$blocks == 0)
+                    return;
+                break;
+            case T_STRING:
+                if (!isset($class['name']))
+                    $class['name'] = $token[1];
+                break;
+            case T_FUNCTION:
+                $this->traverseFunction(
+                    array($token[2], $line[1]),
+                    array('allow_this'=>true));
+                break;
+            case T_VAR:
+                // var $variable
+                // used inside classes to define instance variables
+                while (list(,$token) = each($this->tokens)) {
+                    if (is_array($token) && $token[0] == T_VARIABLE)
+                        // TODO: Add this to some class context in the
+                        // future to support indefined access to $this->blah
+                        break;
+                }
+                break;
+            }
+        }
+    }
+
+    function traverseFunction($line=0, $options=array()) {
+        // Scan for function name
+        $function = array('line'=>$line, 'name'=>'(inline)');
+        $token = false;
+        $scope = array();
+        while ($token != "{") {
+            list(,$token) = each($this->tokens);
+            if (!is_array($token)
+                    || $token[0] == T_WHITESPACE)
+                continue;
+            if ($token[0] == T_STRING)
+                $function['name'] = $token[1];
+            elseif ($token[0] == T_VARIABLE)
+                $scope[$token[1]] = 1;
+        }
+        // Start inside a block -- we've already consumed the {
+        $this->checkVariableUsage($function, $scope, 1, $options);
+    }
+
+    function checkVariableUsage($function, $scope=array(), $blocks=0,
+            $options=array()) {
+        // Merge in defaults to the options array
+        $options = array_merge(array(
+            'allow_this' => false,
+            ), $options);
+        // Unpack function[line][file] if set
+        if (is_array($function['line']))
+            $function['file'] = $function['line'][1];
+        while (list($i,$token) = each($this->tokens)) {
+            // Check variable usage and for nested blocks
+            switch ($token[0]) {
+            case '{':
+                $blocks++;
+                break;
+            case '}':
+                if (--$blocks == 0)
+                    return;
+                break;
+            case T_VARIABLE:
+                // Look-ahead for assignment
+                $assignment = false;
+                while ($next = @$this->tokens[++$i])
+                    if (!is_array($next) || $next[0] != T_WHITESPACE)
+                        break;
+                switch ($next[0]) {
+                case '=':
+                    // For assignment, check if the variable is explictly
+                    // assigned to NULL. If so, treat the assignment as an
+                    // unset()
+                    while ($next = @$this->tokens[++$i])
+                        if (!is_array($next) || $next[0] != T_WHITESPACE)
+                            break;
+                    if (is_array($next) && strcasecmp('NULL', $next[1]) === 0) {
+                        $scope[$token[1]] = 'null';
+                        $assignment = true;
+                        break;
+                    }
+                case T_AND_EQUAL:
+                case T_CONCAT_EQUAL:
+                case T_DIV_EQUAL:
+                case T_MINUS_EQUAL:
+                case T_MOD_EQUAL:
+                case T_MUL_EQUAL:
+                case T_OR_EQUAL:
+                case T_PLUS_EQUAL:
+                case T_SL_EQUAL:
+                case T_SR_EQUAL:
+                case T_XOR_EQUAL:
+                    $assignment = true;
+                    $scope[$token[1]] = 1;
+                    break;
+                }
+                if ($assignment)
+                    break;
+
+                if (!isset($scope[$token[1]])) {
+                    if ($token[1] == '$this' && $options['allow_this']) {
+                        // Always valid in a non-static class method
+                        // TODO: Determine if this function is defined in a class
+                        break;
+                    }
+                    elseif (isset(static::$super_globals[$token[1]]))
+                        // Super globals are always in scope
+                        break;
+                    elseif (!isset($function['name']) || $function['name'] == '(main)')
+                        // Not in a function. Cowardly continue.
+                        // TODO: Recurse into require() statements to
+                        // determine if global variables are actually
+                        // defined somewhere in the code base
+                        break;
+                    $this->bugs[] = array(
+                        'type' => 'UNDEF_ACCESS',
+                        'func' => $function['name'],
+                        'line' => array($token[2], $function['file']),
+                        'name' => $token[1],
+                    );
+                }
+                elseif ($scope[$token[1]] == 'null') {
+                    // See if the next token is accessing a property of the
+                    // object
+                    $c = current($this->tokens);
+                    switch ($c[0]) {
+                    case T_OBJECT_OPERATOR:
+                    case T_PAAMAYIM_NEKUDOTAYIM:
+                    case '[':
+                        $this->bugs[] = array(
+                            'type' => 'MAYBE_UNDEF_ACCESS',
+                            'func' => $function['name'],
+                            'line' => array($token[2], $function['file']),
+                            'name' => $token[1],
+                        );
+                    }
+                }
+                break;
+            case T_PAAMAYIM_NEKUDOTAYIM:
+                // Handle static variables $instance::$static
+                $current = current($this->tokens);
+                if ($current[0] == T_VARIABLE)
+                    next($this->tokens);
+                break;
+            case T_CLASS:
+                // XXX: PHP does not allow nested classes
+                $this->traverseClass(
+                    array($token[2], $function['file']));
+                break;
+            case T_FUNCTION:
+                // PHP does not automatically nest scopes. Variables
+                // available inside the closure must be explictly defined.
+                // Therefore, there is no need to pass the current scope.
+                // However, $this is not allowed inside inline functions
+                // unless declared in the use () parameters.
+                $this->traverseFunction(
+                    array($token[2], $function['file']),
+                    array('allow_this'=>false));
+                break;
+            case T_STATIC:
+                $c = current($this->tokens);
+                // (nolint) static::func() or static::$var
+                if ($c[0] == T_PAAMAYIM_NEKUDOTAYIM)
+                    break;
+            case T_GLOBAL:
+                while (list(,$token) = each($this->tokens)) {
+                    if ($token == ';')
+                        break;
+                    elseif (!is_array($token))
+                        continue;
+                    elseif ($token[0] == T_VARIABLE)
+                        $scope[$token[1]] = 1;
+                }
+                break;
+            case T_FOR:
+                // for ($i=0;...)
+                // Find first semi-colon, variables defined before it should
+                // be added to the current scope
+                while (list(,$token) = each($this->tokens)) {
+                    if ($token == ';')
+                        break;
+                    elseif (!is_array($token))
+                        continue;
+                    elseif ($token[0] == T_VARIABLE)
+                        $scope[$token[1]] = 1;
+                }
+                break;
+            case T_FOREACH:
+                // foreach ($blah as $a=>$b) -- add $a, $b to the local
+                // scope
+                $after_as = false;
+                $parens = 0;
+                // Scan for the variables defined for the scope of the
+                // foreach block
+                while (list(,$token) = each($this->tokens)) {
+                    if ($token == '(')
+                        $parens++;
+                    elseif ($token == ')' && --$parens == 0)
+                        break;
+                    elseif (!is_array($token))
+                        continue;
+                    elseif ($token[0] == T_AS)
+                        $after_as = true;
+                    elseif ($after_as && $token[0] == T_VARIABLE)
+                        // Technically, variables defined in a foreach()
+                        // block are still accessible after the completion
+                        // of the foreach block
+                        $scope[$token[1]] = 1;
+                }
+                break;
+            case T_LIST:
+                // list($a, $b) = ...
+                // Find all variables defined up to the closing parenthesis
+                while (list(,$token) = each($this->tokens)) {
+                    if ($token == ')')
+                        break;
+                    elseif (!is_array($token))
+                        continue;
+                    elseif ($token[0] == T_VARIABLE)
+                        $scope[$token[1]] = 1;
+                }
+                break;
+            case T_ISSET:
+                // isset($var)
+                // $var is allowed to be undefined and not be an error.
+                // Consume tokens until close parentheses
+                while (list(,$token) = each($this->tokens)) {
+                    if ($token == ')')
+                        break;
+                }
+                break;
+            case T_UNSET:
+                // unset($var)
+                // Var will no longer be in scope
+                while (list(,$token) = each($this->tokens)) {
+                    if ($token == ')')
+                        break;
+                    elseif (is_array($token) && $token[0] == T_VARIABLE) {
+                        // Check for unset($var[key]) -- don't unset anything
+                        // Check for unset($this->blah)
+                        $next = current($this->tokens);
+                        switch ($next[0]) {
+                        case '[':
+                        case T_OBJECT_OPERATOR:
+                            break;
+                        default:
+                            unset($scope[$token[1]]);
+                        }
+                        break;
+                    }
+                }
+                break;
+            case T_DOLLAR_OPEN_CURLY_BRACES:
+            case T_CURLY_OPEN:
+                // "{$a .. }"
+                // This screws up block detection. We will see another close
+                // brace somewhere along the way
+                $blocks++;
+                break;
+            }
+        }
+    }
+}
+?>
diff --git a/setup/test/tests/class.test.php b/setup/test/tests/class.test.php
index 5ce1297619714533c19bdfd7f2c083b4b9d255b8..44842b3fef80d125623ae3ff4f6df8eeadcce9ec 100644
--- a/setup/test/tests/class.test.php
+++ b/setup/test/tests/class.test.php
@@ -2,6 +2,7 @@
 
 class Test {
     var $fails = array();
+    var $warnings = array();
     var $name = "";
 
     var $third_party_paths = array(
@@ -11,6 +12,9 @@ class Test {
         '/include/pear/',
         '/include/Spyc.php',
         '/setup/cli/stage/',
+        '/include/plugins/',
+        '/include/h2o/',
+        '/include/fpdf/',
     );
 
     function Test() {
@@ -57,7 +61,7 @@ class Test {
     }
 
     function warn($message) {
-        $this->fails[] = array(get_class($this), '', '', 'WARNING: '.$message);
+        $this->warnings[] = array(get_class($this), '', '', 'WARNING: '.$message);
         fputs(STDOUT, 'w');
     }
 
diff --git a/setup/test/tests/lib/phplint.tcl b/setup/test/tests/lib/phplint.tcl
deleted file mode 100755
index 30e10074baba6f31705c1c0b152995b33f564f66..0000000000000000000000000000000000000000
--- a/setup/test/tests/lib/phplint.tcl
+++ /dev/null
@@ -1,217 +0,0 @@
-#!/usr/bin/tclsh
-
-# Copyright (C) 2007 Salvatore Sanfilippo <antirez at gmail dot com>
-# This software is released under the GPL license version 2
-
-proc scan file {
-    set fd [open $file]
-    set infunc 0
-    set linenr 0
-    set fnre {(^\s*)((public|private|protected|static)\s*)*function(?=\s|\()\s*([^(]+)?\s*\(((\(\)|[^)])*)\)(\s*use\s*\((.*)\))?}
-    while {[gets $fd line] != -1} {
-        incr linenr
-        if {[regexp $fnre $line - ind - - - fa - - ua]} {
-            # If $infunc is true we miss the end of the last function
-            # so we analyze it now.
-            if {$infunc} {
-                analyze $file $arglist $body
-            }
-            set body {}
-            set arglist {}
-            foreach arg [split $fa ,] {
-                # remove default value
-                regsub {=.*} $arg {} arg
-                # remove optional type spec
-                regsub {^.*\s+} [string trim $arg] {} arg
-                set arg [string trim $arg " $&"]
-                lappend arglist $arg
-            }
-            # Support closure variables
-            foreach arg [split $ua ,] {
-                set arg [string trim $arg " $"]
-                lappend arglist $arg
-            }
-            set infunc 1
-        } elseif {$infunc && [regexp "^$ind\}" $line]} {
-            set infunc 0
-            analyze $file $arglist $body
-        } elseif {$infunc} {
-            lappend body $linenr [string trim $line]
-        }
-    }
-}
-
-proc analyze {file arglist body} {
-    set initialized(this) 1
-    set linton 1
-    foreach arg $arglist {
-        set initialized($arg) 1
-    }
-    # Superglobals
-    set superglobals {
-        "GLOBALS"
-        "_SESSION"
-        "_SESSION"
-        "_GET"
-        "_POST"
-        "_REQUEST"
-        "_ENV"
-        "_SERVER"
-        "_FILES"
-        "php_errormsg"
-    }
-    foreach sg $superglobals {
-        set initialized($sg) 1
-    }
-    # analyze body
-    foreach {linenr line} $body {
-        # Handle annotations
-        if {[string first {nolint} [string tolower $line]] != -1} continue
-        if {[string first {linton} [string tolower $line]] != -1} {
-            if {$linton == 1} {
-                puts "! Warning 'linton' annotation with lint already ON"
-                continue
-            }
-            set linton 1
-            puts ". $skipped lines skipped in $file from line $skipstart"
-        }
-        if {[string first {lintoff} [string tolower $line]] != -1} {
-            if {$linton == 0} {
-                puts "! Warning 'lintoff' annotation with lint already OFF"
-                continue
-            }
-            set linton 0
-            set skipped 0
-            set skipstart [expr {$linenr+1}]
-            continue
-        }
-        if {$linton == 0} {
-            incr skipped
-            continue
-        }
-        # Skip comments
-        if {[string index $line 0] eq {#}} continue
-        if {[string index $line 0] eq {/} && [string index $line 1] eq {/}} continue
-        # PHP variable regexp
-        set varre {\$[_A-Za-z]+[_A-Za-z0-9]*(\[[^\]]*\])*}
-        # Check for globals
-        set re {\s*(global|static)\s+((?:\$[^;,]+[ ,]*)+)(;|$)}
-        if {[regexp $re $line -> - g]} {
-            set g [split [string trim $g ";"] ,]
-            foreach v $g {
-                set v [string trim $v "$ "]
-                set initialized($v) 1
-            }
-        }
-        # Check for assignment via foreach ... as &$varname
-        set re {}
-        append re {foreach\s*\(.*\s+as\s+&?(} $varre {)\s*\)}
-        set l [regexp -all -inline -nocase $re $line]
-        foreach {- a -} $l {
-            set initialized([string trim $a "$ "]) 1
-        }
-        # Check for assignment via foreach ... as $key => &$val
-        set re {}
-        append re {foreach\s*\(.*\s+as\s+(} $varre {)\s*=>\s*&?(} $varre {)\s*\)}
-        set l [regexp -all -inline -nocase $re $line]
-        foreach {- a1 - a2 -} $l {
-            set initialized([string trim $a1 "$ "]) 1
-            set initialized([string trim $a2 "$ "]) 1
-        }
-        # Check for assigments in the form list($a,$b,$c) = ...
-        set re {list\s*\(([^=]*)\)\s*=}
-        set l [regexp -all -inline $re $line]
-        foreach {- vars} $l {
-            foreach v [split $vars ,] {
-                set v [string trim $v "$ "]
-                set initialized($v) 1
-            }
-        }
-        # Check for assigments via = operator
-        set re $varre
-        append re {\s*=}
-        set l [regexp -all -inline $re $line]
-        foreach {a -} $l {
-            set a [string trim $a "=$ "]
-            regsub -all {\[.*\]} $a {[]} a
-            #puts "assigmnent of $a"
-            set initialized($a) 1
-            regsub -all {\[\]} $a {} a
-            set initialized($a) 1
-        }
-        # Check for assignments via catch(Exception $e)
-        set re {}
-        append re {catch\s*\(.*\s+(} $varre {)}
-        set l [regexp -all -inline -nocase $re $line]
-        foreach {- a} $l {
-            set initialized([string trim $a "$ "]) 1
-        }
-        # Check for assignments by reference
-        #
-        # funclist format is {type funcname spos epos} where spos is the
-        # zero-based index of the first argument that can be considered
-        # an assignment, while epos is the last.
-        #
-        # name is the function name to match, and type is what
-        # to do with the args. "assignment" to consider them assigned
-        # or "ingore" to ingore them for the current line.
-        #
-        # The "ignore" is used for isset() and other functions that can
-        # deal with not initialized vars.
-        unset -nocomplain -- ignore
-        array set ignore {}
-        set funclist {
-            assignment scanf 2 100
-            assignment preg_match 2 100
-            assignment preg_match_all 2 100
-            assignment ereg 2 100
-            ignore isset 0 0
-        }
-        set cline $line
-        regsub -all {'[^']+'} $cline {''} cline
-        foreach {type name spos epos} $funclist {
-            set re {}
-            append re $name {\s*\(([^()]*)\)}
-            foreach {- fargs} [regexp -all -inline $re $cline] {
-                set argidx 0
-                foreach a [split $fargs ,] {
-                    set a [string trim $a ", $"]
-                    regsub -all {\[.*\]} $a {} a
-                    if {$argidx >= $spos && $argidx <= $epos} {
-                        if {$type eq {assignment}} {
-                            set initialized($a) 1
-                        } elseif {$type eq {ignore}} {
-                            set ignore($a) 1
-                        }
-                    }
-                    incr argidx
-                }
-            }
-        }
-
-        # Check for var accesses
-        set varsimplere {(?:::)?\$[_A-Za-z]+[_A-Za-z0-9]*}
-        set l [regexp -all -inline $varsimplere $line]
-        foreach a $l {
-            set a [string trim $a "=$ "]
-            regsub -all {\[.*\]} $a {} a
-            # Skip access to class-static variables
-            if {[string first :: $a] == 0} {
-                continue
-            }
-            #puts "access of $a"
-            if {![info exists initialized($a)] &&
-                ![info exists ignore($a)]} {
-                puts "* In $file line $linenr: access to uninitialized var '$a'"
-            }
-        }
-    }
-}
-
-proc main argv {
-    foreach file $argv {
-        scan $file
-    }
-}
-
-main $argv
diff --git a/setup/test/tests/stubs.php b/setup/test/tests/stubs.php
index fba61da31c613e7a3cae65f8ba37c8f08f599a27..73afec7cd326565fe56d30f0690281a6dd565b7f 100644
--- a/setup/test/tests/stubs.php
+++ b/setup/test/tests/stubs.php
@@ -6,14 +6,23 @@ class mysqli {
     function real_escape_string() {}
     function fetch_row() {}
     function prepare() {}
+    function ssl_set() {}
+    function real_connect() {}
+    function select_db() {}
 }
 
 class mysqli_stmt {
+    var $num_rows;
+
     function store_result() {}
     function data_seek() {}
     function fetch() {}
+    function fetch_array() {}
     function fetch_field() {}
+    function fetch_field_direct() {}
+    function fetch_row() {}
     function result_metadata() {}
+    function free() {}
 }
 
 class ReflectionClass {
diff --git a/setup/test/tests/test.undefinedmethods.php b/setup/test/tests/test.undefinedmethods.php
index 4d22d1aafed62fcfe6d1b68dc985a03db6020343..2e06a452ba20d648f6b52c5f2c3cfd8fc3f0e818 100644
--- a/setup/test/tests/test.undefinedmethods.php
+++ b/setup/test/tests/test.undefinedmethods.php
@@ -32,7 +32,7 @@ function find_function_calls($scripts) {
         $lineno=0;
         foreach ($lines as $line) {
             $lineno++; $matches=array();
-            preg_match_all('/(?:-[>]|::)([a-zA-Z0-9_]+)\(.*/', $line, $matches,
+            preg_match_all('/^.*\w+(?:-[>]|::)([a-zA-Z0-9_]+)\(.*/', $line, $matches,
                 PREG_SET_ORDER);
             foreach ($matches as $m)
                 if (strpos($m[0], 'nolint') === false)
diff --git a/setup/test/tests/test.unitialized.php b/setup/test/tests/test.unitialized.php
index 09bd0509e155072ccb6044429248965c2e318b36..47978fdb2fd9741bd2c4f554d527795a4927cbed 100644
--- a/setup/test/tests/test.unitialized.php
+++ b/setup/test/tests/test.unitialized.php
@@ -1,5 +1,6 @@
 <?php
 require_once "class.test.php";
+require_once "class.php_analyze.php";
 
 class UnitializedVars extends Test {
     var $name = "Access to unitialized variables";
@@ -7,16 +8,21 @@ class UnitializedVars extends Test {
     function testUnitializedUsage() {
         $scripts = $this->getAllScripts();
         $matches = array();
-        foreach (range(0, count($scripts), 40) as $start) {
-            $slice = array_slice($scripts, $start, 40);
-            ob_start();
-            # XXX: This won't run well on Windoze
-            system(dirname(__file__)."/lib/phplint.tcl ".implode(" ", $slice));
-            $lint_errors = ob_get_clean();
-            preg_match_all("/\* In (.*) line (\d+): access to uninitialized var '([^']+)'/m",
-                    $lint_errors, $matches, PREG_SET_ORDER);
-            foreach ($matches as $match)
-                $this->fail($match[1], $match[2], "'\${$match[3]}'");
+        foreach ($scripts as $s) {
+            $a = new SourceAnalyzer($s);
+            $a->parseFile();
+            foreach ($a->bugs as $bug) {
+                if ($bug['type'] == 'UNDEF_ACCESS') {
+                    list($line, $file) = $bug['line'];
+                    $this->fail($file, $line, "'{$bug['name']}'");
+                }
+                elseif ($bug['type'] == 'MAYBE_UNDEF_ACCESS') {
+                    list($line, $file) = $bug['line'];
+                    $this->warn("Possible access to NULL object @ $file : $line");
+                }
+            }
+            if (!$a->bugs)
+                $this->pass();
         }
     }
 }