diff --git a/include/class.mailer.php b/include/class.mailer.php
index 49b15ec285a0f84176cc5f235389e2dc970f51f2..ee22db849f18ff121bad2c9d84313fc3f088de5b 100644
--- a/include/class.mailer.php
+++ b/include/class.mailer.php
@@ -150,14 +150,27 @@ class Mailer {
         $thread = $entry ? $entry->getThread()
             : (isset($options['thread']) && $options['thread'] instanceof Thread
                 ? $options['thread'] : false);
+
+        switch (true) {
+        case $recipient instanceof Staff:
+            $utype = 'S';
+            break;
+        case $recipient instanceof TicketOwner:
+            $utype = 'U';
+            break;
+        case $recipient instanceof Collaborator:
+            $utype = 'C';
+            break;
+        default:
+            $utype = $options['utype'] ?: '?';
+        }
+
+
         $tag = pack('VVVa',
             $recipient instanceof EmailContact ? $recipient->getUserId() : 0,
             $entry ? $entry->getId() : 0,
             $thread ? $thread->getId() : 0,
-            ($recipient instanceof Staff ? 'S'
-                : ($recipient instanceof TicketOwner ? 'U'
-                : ($recipient instanceof Collaborator ? 'C'
-                : '?')))
+            $utype ?: '?'
         );
         // Sign the tag with the system secret salt
         $tag .= substr(hash_hmac('sha1', $tag.$rand.$sysid, SECRET_SALT, true), -5);
diff --git a/include/class.mailparse.php b/include/class.mailparse.php
index f530ac50bdafdfaa6f9bedf1c74c99c9096b4b49..1efd5a8b15fa48c88a9936172451840cffe72015 100644
--- a/include/class.mailparse.php
+++ b/include/class.mailparse.php
@@ -208,14 +208,14 @@ class Mail_Parse {
         if (!($header = $this->struct->headers['from']))
             return null;
 
-        return Mail_Parse::parseAddressList($header);
+        return Mail_Parse::parseAddressList($header, $this->charset);
     }
 
     function getDeliveredToAddressList() {
         if (!($header = $this->struct->headers['delivered-to']))
             return null;
 
-        return Mail_Parse::parseAddressList($header);
+        return Mail_Parse::parseAddressList($header, $this->charset);
     }
 
     function getToAddressList(){
@@ -223,7 +223,7 @@ class Mail_Parse {
         $tolist = array();
         if ($header = $this->struct->headers['to'])
             $tolist = array_merge($tolist,
-                Mail_Parse::parseAddressList($header));
+                Mail_Parse::parseAddressList($header, $this->charset));
         return $tolist ? $tolist : null;
     }
 
@@ -231,14 +231,14 @@ class Mail_Parse {
         if (!($header = @$this->struct->headers['cc']))
             return null;
 
-        return Mail_Parse::parseAddressList($header);
+        return Mail_Parse::parseAddressList($header, $this->charset);
     }
 
     function getBccAddressList(){
         if (!($header = @$this->struct->headers['bcc']))
             return null;
 
-        return Mail_Parse::parseAddressList($header);
+        return Mail_Parse::parseAddressList($header, $this->charset);
     }
 
     function getMessageId(){
@@ -258,7 +258,7 @@ class Mail_Parse {
         if (!($header = @$this->struct->headers['reply-to']))
             return null;
 
-        return Mail_Parse::parseAddressList($header);
+        return Mail_Parse::parseAddressList($header, $this->charset);
     }
 
     function isBounceNotice() {
@@ -543,7 +543,7 @@ class Mail_Parse {
     	return 0;
     }
 
-    function parseAddressList($address){
+    function parseAddressList($address, $charset='UTF-8'){
         if (!$address)
             return array();
 
@@ -558,11 +558,11 @@ class Mail_Parse {
 
         // Decode name and mailbox
         foreach ($parsed as $p) {
-            $p->personal = Format::mimedecode($p->personal, $this->charset);
+            $p->personal = Format::mimedecode($p->personal, $charset);
             // Some mail clients may send ISO-8859-1 strings without proper encoding.
             // Also, handle the more sane case where the mailbox is properly encoded
             // against RFC2047
-            $p->mailbox = Format::mimedecode($p->mailbox, $this->charset);
+            $p->mailbox = Format::mimedecode($p->mailbox, $charset);
         }
 
         return $parsed;
diff --git a/include/class.task.php b/include/class.task.php
index a9e07584e03a06209ad730b5a366177922355225..d86a52ef766d2a89ede2b410a50878116501b0bb 100644
--- a/include/class.task.php
+++ b/include/class.task.php
@@ -1078,7 +1078,7 @@ class Task extends TaskModel implements RestrictedAccess, Threadable {
 
         // Who posted the entry?
         $skip = array();
-        if ($entry instanceof Message) {
+        if ($entry instanceof MessageThreadEntry) {
             $poster = $entry->getUser();
             // Skip the person who sent in the message
             $skip[$entry->getUserId()] = 1;
diff --git a/include/class.thread.php b/include/class.thread.php
index c1fc3f2a5d7fe5a19e7b441305d0d97a1e789ca8..68d1d69358e1feb9db91de5ca05423fbd3dd9cbb 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -348,99 +348,100 @@ class Thread extends VerySimpleModel {
             $vars['attachments'] = $mailinfo['attachments'];
 
         $body = $mailinfo['message'];
-        $poster = $mailinfo['email'];
-
-        // Disambiguate if the user happens also to be a staff member of the
-        // system. The current ticket owner should _always_ post messages
-        // instead of notes or responses
-        if ($mailinfo['userId'] || (
-            $object instanceof Ticket
-            && strcasecmp($mailinfo['email'], $object->getEmail()) == 0
-        )) {
-            $vars['message'] = $body;
-            $vars['userId'] = $mailinfo['userId'] ?: $object->getUserId();
 
-            if ($object instanceof Threadable)
-                return $object->postThreadEntry('M', $vars);
-            elseif ($this instanceof ObjectThread)
-                $this->addMessage($vars, $errors);
-            else
-                throw new Exception('Cannot continue discussion with abstract thread');
-        }
-        // Consider collaborator role (disambiguate staff members as
-        // collaborators). Normally, the block above should match based
-        // on the Referenced message-id header
-        elseif ($object instanceof Ticket
-            && ($E = UserEmail::lookup($mailinfo['email']))
-            && ($C = Collaborator::lookup(array(
-                'ticketId' => $object->getId(), 'userId' => $E->user_id
-            )))
-        ) {
-            $vars['userId'] = $mailinfo['userId'] ?: $C->getUserId();
-            $vars['message'] = $body;
+        // Attempt to determine the user posting the entry and the
+        // corresponding entry type by the information determined by the
+        // mail parser (via the In-Reply-To header)
+        switch ($mailinfo['userClass']) {
+        case 'C': # Thread collaborator
             $vars['flags'] = ThreadEntry::FLAG_COLLABORATOR;
+        case 'U': # Ticket owner
+            $vars['thread-type'] = 'M';
+            $vars['userId'] = $mailinfo['userId'];
+            break;
 
-            if ($object instanceof Threadable)
-                return $object->postThreadEntry('M', $vars);
-            elseif ($this instanceof ObjectThread)
-                $this->addMessage($vars, $errors);
-            else
-                throw new Exception('Cannot continue discussion with abstract thread');
-        }
-        // Accept internal note from staff members' replies
-        elseif ($mailinfo['staffId']
-                || ($mailinfo['staffId'] = Staff::getIdByEmail($mailinfo['email']))) {
+        case 'A': # System administrator
+        case 'S': # Staff member (agent)
+            $vars['thread-type'] = 'N';
             $vars['staffId'] = $mailinfo['staffId'];
-            $vars['poster'] = Staff::lookup($mailinfo['staffId']);
-            $vars['note'] = $body;
+            if ($vars['staffId'])
+                $vars['poster'] = Staff::lookup($mailinfo['staffId']);
+            break;
 
-            if ($object instanceof Threadable)
-                return $object->postThreadEntry('N', $vars);
-            elseif ($this instanceof ObjectThread)
-                return $this->addNote($vars, $errors);
-            else
-                throw new Exception('Cannot continue discussion with abstract thread');
-        }
-        elseif (Email::getIdByEmail($mailinfo['email'])) {
+        // The user type was not identified by the mail parsing system. It
+        // is likely that the In-Reply-To and References headers were not
+        // properly brokered by the user's mail client. Use the old logic to
+        // determine the post type.
+        default:
+            // Disambiguate if the user happens also to be a staff member of
+            // the system. The current ticket owner should _always_ post
+            // messages instead of notes or responses
+            if ($object instanceof Ticket
+                && strcasecmp($mailinfo['email'], $object->getEmail()) == 0
+            ) {
+                $vars['thread-type'] = 'M';
+                $vars['userId'] = $object->getUserId();
+            }
+            // Consider collaborator role (disambiguate staff members as
+            // collaborators). Normally, the block above should match based
+            // on the Referenced message-id header
+            elseif ($C = $this->collaborators->filter(array(
+                'user__emails__address' => $mailinfo['email']
+            ))->first()) {
+                $vars['thread-type'] = 'M';
+                // XXX: There's no way that mailinfo[userId] would be set
+                $vars['userId'] = $mailinfo['userId'] ?: $C->getUserId();
+                $vars['flags'] = ThreadEntry::FLAG_COLLABORATOR;
+            }
             // Don't process the email -- it came FROM this system
-            return true;
-        }
-        // Support the mail parsing system declaring a thread-type
-        elseif (isset($mailinfo['thread-type'])) {
-            switch ($mailinfo['thread-type']) {
-            case 'N':
-                $vars['note'] = $body;
-                $vars['poster'] = $poster;
-                if ($object instanceof Threadable)
-                    return $object->postThreadEntry('N', $vars);
-                elseif ($this instanceof ObjectThread)
-                    return $this->addNote($vars, $errors);
-                else
-                    throw new Exception('Cannot continue discussion with abstract thread');
+            elseif (Email::getIdByEmail($mailinfo['email'])) {
+                return false;
             }
         }
+
+        // Ensure we record the name of the person posting
+        $vars['poster'] = $vars['poster']
+            ?: $mailinfo['name'] ?: $mailinfo['email'];
+
         // TODO: Consider security constraints
-        else {
+        if (!$vars['thread-type']) {
             //XXX: Are we potentially leaking the email address to
             // collaborators?
             // Try not to destroy the format of the body
-            $header = sprintf("Received From: %s <%s>\n\n", $mailinfo['name'],
-                $mailinfo['email']);
+            $header = sprintf(
+                _S('Received From: %1$s <%2$s>') . "\n\n",
+                $mailinfo['name'], $mailinfo['email']);
             if ($body instanceof HtmlThreadEntryBody)
                 $header = nl2br(Format::htmlchars($header));
             // Add the banner to the top of the message
             if ($body instanceof ThreadEntryBody)
                 $body->prepend($header);
-            $vars['message'] = $body;
             $vars['userId'] = 0; //Unknown user! //XXX: Assume ticket owner?
-            $vars['origin'] = 'Email';
+            $vars['thread-type'] = 'M';
+        }
+
+        switch ($vars['thread-type']) {
+        case 'M':
+            $vars['message'] = $body;
+
             if ($object instanceof Threadable)
                 return $object->postThreadEntry('M', $vars);
             elseif ($this instanceof ObjectThread)
                 return $this->addMessage($vars, $errors);
-            else
-                throw new Exception('Cannot continue discussion with abstract thread');
+            break;
+
+        case 'N':
+            $vars['note'] = $body;
+
+            if ($object instanceof Threadable)
+                return $object->postThreadEntry('N', $vars);
+            elseif ($this instanceof ObjectThread)
+                return $this->addNote($vars, $errors);
+            break;
         }
+
+        throw new Exception('Unable to continue thread via email.');
+
         // Currently impossible, but indicate that this thread object could
         // not append the incoming email.
         return false;
@@ -1178,6 +1179,12 @@ implements TemplateVariable {
                 elseif (@$mid_info['staffId']) {
                     $mailinfo['staffId'] = $mid_info['staffId'];
                 }
+
+                // Capture the user type
+                if (@$mid_info['userClass'])
+                    $mailinfo['userClass'] = $mid_info['userClass'];
+
+
                 // ThreadEntry was positively identified
                 return $t;
             }
diff --git a/include/class.ticket.php b/include/class.ticket.php
index c93b9856ad462f002da8b07e85d832b91958d356..55e3f9fbbb1da0e82d2aecf0df3258ad6e4d96e3 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -1275,21 +1275,16 @@ implements RestrictedAccess, Threadable {
             if ($message instanceof ThreadEntry && $message->isAutoReply())
                 $sentlist[] = $this->getEmail();
 
-            // Alert admin??
-            if ($cfg->alertAdminONNewTicket()) {
-                $alert = $this->replaceVars($msg, array('recipient' => 'Admin'));
-                $email->sendAlert($cfg->getAdminEmail(), $alert['subj'], $alert['body'], null, $options);
-                $sentlist[]=$cfg->getAdminEmail();
-            }
-
             // Only alerts dept members if the ticket is NOT assigned.
             if ($cfg->alertDeptMembersONNewTicket() && !$this->isAssigned()) {
                 if ($members = $dept->getMembersForAlerts()->all())
                     $recipients = array_merge($recipients, $members);
             }
 
-            if ($cfg->alertDeptManagerONNewTicket() && $dept && ($manager=$dept->getManager()))
+            if ($cfg->alertDeptManagerONNewTicket() && $dept &&
+                    ($manager=$dept->getManager())) {
                 $recipients[] = $manager;
+            }
 
             // Account manager
             if ($cfg->alertAcctManagerONNewMessage()
@@ -1313,6 +1308,16 @@ implements RestrictedAccess, Threadable {
                 $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
                 $sentlist[] = $staff->getEmail();
             }
+
+            // Alert admin ONLY if not already a staff??
+            if ($cfg->alertAdminONNewTicket()
+                    && !in_array($cfg->getAdminEmail(), $sentlist)) {
+                $options += array('utype'=>'A');
+                $alert = $this->replaceVars($msg, array('recipient' => 'Admin'));
+                $email->sendAlert($cfg->getAdminEmail(), $alert['subj'],
+                        $alert['body'], null, $options);
+            }
+
         }
         return true;
     }
@@ -1388,7 +1393,7 @@ implements RestrictedAccess, Threadable {
         }
         // Who posted the entry?
         $skip = array();
-        if ($entry instanceof Message) {
+        if ($entry instanceof MessageThreadEntry) {
             $poster = $entry->getUser();
             // Skip the person who sent in the message
             $skip[$entry->getUserId()] = 1;
@@ -2478,7 +2483,10 @@ implements RestrictedAccess, Threadable {
         global $cfg, $thisstaff;
 
         //Who is posting the note - staff or system?
-        $vars['staffId'] = 0;
+        if ($vars['staffId'] && !$poster)
+            $poster = Staff::lookup($vars['staffId']);
+
+        $vars['staffId'] = $vars['staffId'] ?: 0;
         if ($poster && is_object($poster)) {
             $vars['staffId'] = $poster->getId();
             $vars['poster'] = $poster->getName();