diff --git a/README.md b/README.md
index 9946aeb3d85e6c3715b57f9347d10ccdc4edb244..f09b94d050780ec31d5953b61118b417edb6c6eb 100644
--- a/README.md
+++ b/README.md
@@ -47,3 +47,20 @@ Create your own fork of the project and use
 [git-flow](https://github.com/nvie/gitflow) to create a new feature. Once
 the feature is published in your fork, send a pull request to begin the
 conversation of integrating your new feature into osTicket.
+
+License
+-------
+osTicket is released under the GPL2 license. See the included LICENSE.txt
+file for the gory details of the General Public License.
+
+osTicket is supported by several magical open source projects including:
+
+  * [HTMLawed](http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed)
+  * [PasswordHash](http://www.openwall.com/phpass/)
+  * [PEAR](http://pear.php.net/package/PEAR)
+  * [PEAR/Auth_SASL](http://pear.php.net/package/Auth_SASL)
+  * [PEAR/Mail](http://pear.php.net/package/mail)
+  * [PEAR/Net_SMTP](http://pear.php.net/package/Net_SMTP)
+  * [PEAR/Net_Socket](http://pear.php.net/package/Net_Socket)
+  * [PEAR/Serivces_JSON](http://pear.php.net/package/Services_JSON)
+  * [phplint](http://antirez.com/page/phplint.html) 
diff --git a/include/class.attachment.php b/include/class.attachment.php
index 8644d671c4986f6b5541e6b9b592fa06fde91652..442c9865e495d8e6d32e380f712524b66c5ef0e6 100644
--- a/include/class.attachment.php
+++ b/include/class.attachment.php
@@ -93,10 +93,12 @@ class Attachment {
         return db_result(db_query($sql));
     }
 
-    function lookup($id,$tid=0) {
-        $id=is_numeric($id)?$id:self::getIdByFileHash($hash,$tid);
+    function lookup($var,$tid=0) {
+        $id=is_numeric($var)?$var:self::getIdByFileHash($var,$tid);
 
-        return ($id && is_numeric($id) && ($attach = new Attachment($id,$tid)) && $attach->getId()==$id)?$attach:null;
+        return ($id && is_numeric($id)
+            && ($attach = new Attachment($id,$tid))
+            && $attach->getId()==$id)?$attach:null;
     }
 
 }
diff --git a/include/class.canned.php b/include/class.canned.php
index 6c6c1ac1c75bcbc25b7c288c2e49909e30b4876a..51d39ca48024410bdefecba03f20b7b030f30649 100644
--- a/include/class.canned.php
+++ b/include/class.canned.php
@@ -130,6 +130,7 @@ class Canned {
     */
     function uploadAttachments($files) {
 
+        $i=0;
         foreach($files as $file) {
             if(($fileId=is_numeric($file)?$file:AttachmentFile::upload($file)) && is_numeric($fileId)) {
                 $sql ='INSERT INTO '.CANNED_ATTACHMENT_TABLE
@@ -174,7 +175,7 @@ class Canned {
         return self::save(0,$vars,$errors);
     }
 
-    function getIdByTitle($titke) {
+    function getIdByTitle($title) {
         $sql='SELECT canned_id FROM '.CANNED_TABLE.' WHERE title='.db_input($title);
         if(($res=db_query($sql)) && db_num_rows($res))
             list($id)=db_fetch_row($res);
diff --git a/include/class.faq.php b/include/class.faq.php
index 7e23803dc458272ed7f81184c190213ddabd0e85..b0fdc4ac2b0890895f0adfa3a3268f5e63531221 100644
--- a/include/class.faq.php
+++ b/include/class.faq.php
@@ -127,7 +127,7 @@ class FAQ {
     /* Same as update - but mainly called after one or more setters are changed. */
     function apply() {
         //XXX: set errors and add ->getErrors() & ->getError()
-        return $this->update($this->ht, $errors);
+        return $this->update($this->ht, $errors);               # nolint
     }
 
     function updateTopics($ids){
@@ -204,6 +204,7 @@ class FAQ {
     
     function uploadAttachments($files) {
 
+        $i=0;
         foreach($files as $file) {
             if(($fileId=is_numeric($file)?$file:AttachmentFile::upload($file)) && is_numeric($fileId)) {
                 $sql ='INSERT INTO '.FAQ_ATTACHMENT_TABLE
diff --git a/include/class.filter.php b/include/class.filter.php
index 4f18e96514c307077b7300a02674f8ad1698039e..9c3065c10aba5aa1e359e4c36a67e4de83aec9fe 100644
--- a/include/class.filter.php
+++ b/include/class.filter.php
@@ -156,7 +156,7 @@ class Filter {
         $rule= array_merge($extra,array('w'=>$what, 'h'=>$how, 'v'=>$val));
         $rule['filter_id']=$this->getId();
 
-        return FilterRule::create($rule,$errors);
+        return FilterRule::create($rule,$errors);               # nolint
     }
 
     function removeRule($what, $how, $val) {
@@ -419,7 +419,8 @@ class Filter {
         if($errors || !$id) return false;
 
         //Success with update/create...save the rules. We can't recover from any errors at this point.
-        self::save_rules($id,$vars,$xerrors);
+        # Don't care about errors stashed in $xerrors
+        self::save_rules($id,$vars,$xerrors);               # nolint
       
         return true;
     }
diff --git a/include/class.format.php b/include/class.format.php
index 45f4301913072fa49d93d7dc3a10019b82536563..f7b624d852b5dda8b6dc2e7eedf20a2ef13de5c3 100644
--- a/include/class.format.php
+++ b/include/class.format.php
@@ -104,7 +104,11 @@ class Format {
             $text=Format::clickableurls($text);
 
         //Wrap long words...
-        $text=preg_replace_callback('/\w{75,}/',create_function('$matches','return wordwrap($matches[0],70,"\n",true);'),$text);
+        $text=preg_replace_callback('/\w{75,}/',
+            create_function(
+                '$matches',                                     # nolint
+                'return wordwrap($matches[0],70,"\n",true);'),  # nolint
+            $text);
 
         return nl2br($text);
     }
diff --git a/include/class.mailparse.php b/include/class.mailparse.php
index 88648367615c7141e2c87b4534f3df683d4ef081..f56006fa9c1e35c88d67165302a7797f43f562d7 100644
--- a/include/class.mailparse.php
+++ b/include/class.mailparse.php
@@ -50,8 +50,10 @@ class Mail_Parse {
 
     function splitBodyHeader() {
 
-        if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s",$this->mime_message, $match)) {
-            $this->header=$match[1];
+        if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s",
+                $this->mime_message,
+                $match)) {                                  # nolint
+            $this->header=$match[1];                        # nolint
         }
     }
     /**
diff --git a/include/class.pagenate.php b/include/class.pagenate.php
index b182db517e579a9a7f4921a086e57825cea026a5..baad19b09fc6012fe352b5d7b4da3a251b73ee6f 100644
--- a/include/class.pagenate.php
+++ b/include/class.pagenate.php
@@ -15,37 +15,37 @@
 **********************************************************************/
 class PageNate {
     
-	var $start;
-	var $limit;
-	var $total;
-	var $page;
-	var $pages;
-	
+    var $start;
+    var $limit;
+    var $total;
+    var $page;
+    var $pages;
+    
 
-	function PageNate($total,$page,$limit=20,$url='') {
-		$this->total = intval($total);
-		$this->limit = max($limit, 1 );
-		$this->page  = max($page, 1 );
-		$this->start = max((($page-1)*$this->limit),0);
-		$this->pages = ceil( $this->total / $this->limit );
-		
-		if (($this->limit > $this->total) || ($this->page>ceil($this->total/$this->limit))) {
-			$this->start = 0;
-		}
-		if (($this->limit-1)*$this->start > $this->total) {
-			$this->start -= $this->start % $this->limit;
-		}
-		$this->setURL($url);
-	}
-	function setURL($url='',$vars=''){
-		if($url){
-			if(strpos($url,'?')===false)
-				$url=$url.'?';
-		}else{
-		 $url=THISPAGE.'?';
-		}
-		$this->url=$url.$vars;
-	}
+    function PageNate($total,$page,$limit=20,$url='') {
+        $this->total = intval($total);
+        $this->limit = max($limit, 1 );
+        $this->page  = max($page, 1 );
+        $this->start = max((($page-1)*$this->limit),0);
+        $this->pages = ceil( $this->total / $this->limit );
+        
+        if (($this->limit > $this->total) || ($this->page>ceil($this->total/$this->limit))) {
+            $this->start = 0;
+        }
+        if (($this->limit-1)*$this->start > $this->total) {
+            $this->start -= $this->start % $this->limit;
+        }
+        $this->setURL($url);
+    }
+    function setURL($url='',$vars=''){
+        if($url){
+            if(strpos($url,'?')===false)
+                $url=$url.'?';
+        }else{
+         $url=THISPAGE.'?';
+        }
+        $this->url=$url.$vars;
+    }
 
     function getStart() {
         return $this->start;
@@ -64,34 +64,34 @@ class PageNate {
         return ceil(($this->start+1)/$this->limit);
     }
     
-	function showing() {
-		$html = '';
-		$from= $this->start+1;
-		if ($this->start + $this->limit < $this->total) {
-			$to= $this->start + $this->limit;
-		} else {
-			$to= $this->total;
-		}
+    function showing() {
+        $html = '';
+        $from= $this->start+1;
+        if ($this->start + $this->limit < $this->total) {
+            $to= $this->start + $this->limit;
+        } else {
+            $to= $this->total;
+        }
         $html="&nbsp;Showing&nbsp;&nbsp;";
-		if ($this->total > 0) {
+        if ($this->total > 0) {
             $html .= "$from - $to of " .$this->total;
-		}else{
-			$html .= " 0 ";
-		}
-		return $html;
-	}
+        }else{
+            $html .= " 0 ";
+        }
+        return $html;
+    }
     
-	function getPageLinks() {
-		$html 				= '';
-		$file				=$this->url;
-		$displayed_span 	= 5;
-		$total_pages 		= ceil( $this->total / $this->limit );
-		$this_page 			= ceil( ($this->start+1) / $this->limit );
+    function getPageLinks() {
+        $html                 = '';
+        $file                =$this->url;
+        $displayed_span     = 5;
+        $total_pages         = ceil( $this->total / $this->limit );
+        $this_page             = ceil( ($this->start+1) / $this->limit );
 
         $last=$this_page-1;
         $next=$this_page+1;
         
-		$start_loop 		= floor($this_page-$displayed_span);
+        $start_loop         = floor($this_page-$displayed_span);
         $stop_loop          = ceil($this_page + $displayed_span);
        
         
@@ -107,14 +107,14 @@ class PageNate {
             $html .= "\n<a href=\"$file&p=$lastspan\" ><strong>&laquo;</strong></a>";
         }
         
-		for ($i=$start_loop; $i <= $stop_loop; $i++) {
-			$page = ($i - 1) * $this->limit;
-			if ($i == $this_page) {
-				$html .= "\n<b>[$i]</b>";
-			} else {
-				$html .= "\n<a href=\"$file&p=$i\" ><b>$i</b></a>";
-			}
-		}
+        for ($i=$start_loop; $i <= $stop_loop; $i++) {
+            $page = ($i - 1) * $this->limit;
+            if ($i == $this_page) {
+                $html .= "\n<b>[$i]</b>";
+            } else {
+                $html .= "\n<a href=\"$file&p=$i\" ><b>$i</b></a>";
+            }
+        }
         if($stop_loop<$total_pages){
             $nextspan=($stop_loop+$displayed_span>$total_pages)?$total_pages-$displayed_span:$stop_loop+$displayed_span;
             $html .= "\n<a href=\"$file&p=$nextspan\" ><strong>&raquo;</strong></a>";
@@ -122,8 +122,8 @@ class PageNate {
         
 
         
-		return $html;
-	}
+        return $html;
+    }
 
 }
 ?>
diff --git a/include/class.staff.php b/include/class.staff.php
index ecaaba2adbfb897bbb53b07e44a468eb72374a12..0630a0fe514d391c90af98bd47fe06c7c3871422 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -495,7 +495,7 @@ class Staff {
     }
 
     function login($username, $passwd, &$errors, $strike=true) {
-        global $cfg;
+        global $cfg, $session;
 
 
         if($_SESSION['_staff']['laststrike']) {
@@ -552,7 +552,7 @@ class Staff {
     }
 
     function create($vars, &$errors) {
-        if(($id=self::save(0, $vars, $errors)) && $vars['teams'] && ($self=Staff::lookup($id)))
+        if(($id=self::save(0, $vars, $errors)) && $vars['teams'] && ($staff=Staff::lookup($id)))
             $staff->updateTeams($vars['teams']);
 
         return $id;
diff --git a/include/class.ticket.php b/include/class.ticket.php
index b0f54d2c761461ba987b0e4f91f86ad276d0290e..8eca918fbb101b71f02276b930e62b1bde35a3b1 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -188,7 +188,8 @@ class Ticket{
         if(!strcasecmp($client->getEmail(),$this->getEmail()))
             return true;
 
-        return ($cfg && $cfg->showRelatedTickets() && $client->getTicketId()==$ticket->getExtId());
+        return ($cfg && $cfg->showRelatedTickets() 
+            && $client->getTicketId()==$this->getExtId());
     }
 
     //Getters
@@ -633,7 +634,7 @@ class Ticket{
             .', priority_id='.db_input($priorityId)
             .' WHERE ticket_id='.db_input($this->getId());
 
-        return (db_query($sql) && db_affected_rows($res));
+        return (($res=db_query($sql)) && db_affected_rows($res));
     }
 
     //DeptId can NOT be 0. No orphans please!
@@ -959,7 +960,7 @@ class Ticket{
     }
 
     function onAssign($note, $alert=true) {
-        global $cfg;
+        global $cfg, $thisstaff;
 
         if($this->isClosed()) $this->reopen(); //Assigned tickets must be open - otherwise why assign?
 
@@ -1016,7 +1017,7 @@ class Ticket{
         return true;
     }
 
-    function onOverdue($whine=true) {
+   function onOverdue($whine=true, $comments="") {
         global $cfg;
 
         if($whine && ($sla=$this->getSLA()) && !$sla->alertOnOverdue())
@@ -1026,8 +1027,9 @@ class Ticket{
         if(!$whine || !$cfg->alertONOverdueTicket())
             return true;
 
-        //Get template.
-        if(!($tpl = $dept->getTemplate()))
+        $dept = $this->getDept();
+        //Get department-defined or default template.
+        if(!$dept || !($tpl = $dept->getTemplate()))
             $tpl= $cfg->getDefaultTemplate();
 
         //Email to use!
@@ -1776,7 +1778,7 @@ class Ticket{
             if($cfg->getMaxOpenTickets()>0 && strcasecmp($origin,'staff') 
                     && ($client=Client::lookupByEmail($vars['email']))
                     && ($openTickets=$client->getNumOpenTickets())
-                    && ($opentickets>=$cfg->getMaxOpenTickets()) ) {
+                    && ($openTickets>=$cfg->getMaxOpenTickets()) ) {
 
                 $errors['err']="You've reached the maximum open tickets allowed.";
                 Sys::log(LOG_WARNING, 'Ticket denied -'.$vars['email'], 
@@ -1976,8 +1978,8 @@ class Ticket{
         else
             $vars['message']=$vars['issue'];
 
-        if($var['source'] && !in_array(strtolower($var['source']),array('email','phone','other')))
-            $errors['source']='Invalid source - '.Format::htmlchars($var['source']);
+        if($vars['source'] && !in_array(strtolower($vars['source']),array('email','phone','other')))
+            $errors['source']='Invalid source - '.Format::htmlchars($vars['source']);
 
         if(!($ticket=Ticket::create($vars, $errors, 'staff', false, (!$vars['assignId']))))
             return false;
@@ -1995,7 +1997,7 @@ class Ticket{
             }
         }
         //Post Internal note
-        if($var['assignId'] && $thisstaff->canAssignTickets()) { //Assign ticket to staff or team.
+        if($vars['assignId'] && $thisstaff->canAssignTickets()) { //Assign ticket to staff or team.
             $ticket->assign($vars['assignId'],$vars['note']);
         } elseif($vars['note']) { //Not assigned...save optional note if any
             $ticket->postNote('New Ticket',$vars['note'],false);
diff --git a/include/class.validator.php b/include/class.validator.php
index ea2dc62d0e079513940885b8f1734c4025bb178f..d41bf027fe826c855893146dfee680c578598feb 100644
--- a/include/class.validator.php
+++ b/include/class.validator.php
@@ -147,20 +147,21 @@ class Validator {
 
         $urlregex = "^(https?)\:\/\/";
         // USER AND PASS (optional) 
-        $urlregex .= "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)?"; 
+        $urlregex .= "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)?"; # nolint
         // HOSTNAME OR IP 
-        $urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*";  // http://x = allowed (ex. http://localhost, http://routerlogin) 
+        // http://x = allowed (ex. http://localhost, http://routerlogin) 
+        $urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*";       # nolint
         //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)+";  // http://x.x = minimum 
         //$urlregex .= "([a-z0-9+\$_-]+\.)*[a-z0-9+\$_-]{2,3}";  // http://x.xx(x) = minimum 
         //use only one of the above 
         // PORT (optional) 
         $urlregex .= "(\:[0-9]{2,5})?"; 
         // PATH  (optional) 
-        $urlregex .= "(\/([a-z0-9+\$_-]\.?)+)*\/?"; 
+        $urlregex .= "(\/([a-z0-9+\$_-]\.?)+)*\/?";             # nolint
         // GET Query (optional) 
-        $urlregex .= "(\?[a-z+&\$_.-][a-z0-9;:@/&%=+\$_.-]*)?"; 
+        $urlregex .= "(\?[a-z+&\$_.-][a-z0-9;:@/&%=+\$_.-]*)?"; # nolint
         // ANCHOR (optional) 
-        $urlregex .= "(#[a-z_.-][a-z0-9+\$_.-]*)?\$"; 
+        $urlregex .= "(#[a-z_.-][a-z0-9+\$_.-]*)?\$";           # nolint 
         
         return eregi($urlregex, $url)?true:false; 
     }
diff --git a/include/mysql.php b/include/mysql.php
index 192859af970722c9127e1c57c7596085e98290f5..47f5ab5c8854e7ce4f6cdea47cd5ebd7dccd2fc4 100644
--- a/include/mysql.php
+++ b/include/mysql.php
@@ -46,8 +46,10 @@
     function db_version(){
 
         $version=0;
-        if(preg_match('/(\d{1,2}\.\d{1,2}\.\d{1,2})/', mysql_result(db_query('SELECT VERSION()'),0,0),$matches))
-            $version=$matches[1];
+        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
 
         return $version;
     }
diff --git a/include/pear/Mail/mimeDecode.php b/include/pear/Mail/mimeDecode.php
index b7984d681e38a30e5e546d2846019b93b38f3c33..59b6e1923c32d5ea5a3816e5c14821b1913fd3bf 100644
--- a/include/pear/Mail/mimeDecode.php
+++ b/include/pear/Mail/mimeDecode.php
@@ -721,6 +721,7 @@ class Mail_mimeDecode extends PEAR
         $this->_decode_headers = FALSE;
         $headerlist =$this->_parseHeaders($this->_header);
         $to = "";
+        $header = array();
         if (!$headerlist) {
             return $this->raiseError("Message did not contain headers");
         }
diff --git a/setup/test/lib/phplint.tcl b/setup/test/lib/phplint.tcl
new file mode 100755
index 0000000000000000000000000000000000000000..6b857c75048a26fcd136d9213ece4c5b784bbfbc
--- /dev/null
+++ b/setup/test/lib/phplint.tcl
@@ -0,0 +1,208 @@
+#!/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*\((.*)\).*}
+    while {[gets $fd line] != -1} {
+        incr linenr
+        if {[regexp $fnre $line - ind - - - fa]} {
+            # 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
+            }
+            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
+            #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/lint.php b/setup/test/lint.php
new file mode 100644
index 0000000000000000000000000000000000000000..c25f7acec092a0a17e0dcf295173cc742b480329
--- /dev/null
+++ b/setup/test/lint.php
@@ -0,0 +1,66 @@
+#!/usr/bin/env php
+<?php
+if (php_sapi_name() != 'cli') exit();
+
+function get_osticket_root_path() {
+    # Hop up to the root folder
+    $start = dirname(__file__);
+    for (;;) {
+        if (file_exists($start . '/main.inc.php')) break;
+        $start .= '/..';
+    }
+    return $start;
+}
+
+$root = get_osticket_root_path();
+
+# Check PHP syntax across all php files
+function glob_recursive($pattern, $flags = 0) {
+    $files = glob($pattern, $flags);
+    foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) 
+            as $dir) {
+        $files = array_merge($files, 
+            glob_recursive($dir.'/'.basename($pattern), $flags));
+    }
+    return $files;
+}
+echo "PHP Syntax Errors: ";
+ob_start();
+$scripts=glob_recursive("$root/*/*.php");
+$exit=0;
+$syntax_errors="";
+foreach ($scripts as $s) {
+    system("php -l $s", $exit);
+    $line = ob_get_contents();
+    ob_clean();
+    if ($exit !== 0)
+        $syntax_errors .= $line;
+}
+ob_end_clean();
+
+if (strlen($syntax_errors)) {
+    $syntax_errors=str_replace("$root/", '', $syntax_errors);
+    echo "FAIL\n";
+    echo "-------------------------------------------------------\n";
+    echo "$syntax_errors";
+    exit();
+} else {
+    echo "\n";
+}
+
+# Run phplint across all php files
+echo "Access to unitialized variables: ";
+ob_start();
+# XXX: This won't run well on Windoze
+system("$root/setup/test/lib/phplint.tcl ".implode(" ", $scripts));
+$lint_errors = ob_get_clean();
+
+if (strlen($lint_errors)) {
+    $lint_errors=str_replace("$root/", '', $lint_errors);
+    echo "FAIL\n";
+    echo "-------------------------------------------------------\n";
+    echo "$lint_errors";
+} else {
+    echo "\n";
+}
+?>