diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index 05cf5354c426092df2d32adc814e83b31aac228a..68ff2fbacfb26e92f2e9a27c8958cf9b7e08adbd 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -661,11 +661,10 @@ function refer($tid, $target=null) {
         if (!$ticket->checkStaffPerm($thisstaff, Ticket::PERM_RELEASE) && !$thisstaff->isManager())
             Http::response(403, __('Permission denied'));
 
+        $errors = array();
         if (!$ticket->isAssigned())
             $errors['err'] = __('Ticket is not assigned!');
 
-
-        $errors = array();
         $info = array(':title' => sprintf(__('Ticket #%s: %s'),
                     $ticket->getNumber(),
                     __('Release Confirmation')));
diff --git a/include/class.mailer.php b/include/class.mailer.php
index 017d7fbb0ec99267cf1e51920a4e2dd62a88e13d..5d7a135a354330624c1122f342a57c1ab3ab5c3f 100644
--- a/include/class.mailer.php
+++ b/include/class.mailer.php
@@ -399,28 +399,41 @@ class Mailer {
         // Use general failsafe default initially
         $eol = "\n";
         // MAIL_EOL setting can be defined in `ost-config.php`
-        if (defined('MAIL_EOL') && is_string(MAIL_EOL)) {
+        if (defined('MAIL_EOL') && is_string(MAIL_EOL))
             $eol = MAIL_EOL;
-        }
-
         $mime = new Mail_mime($eol);
-
         // Add recipients
         if (!is_array($recipients) && (!$recipients instanceof MailingList))
             $recipients =  array($recipients);
         foreach ($recipients as $recipient) {
             switch (true) {
+                case $recipient instanceof EmailRecipient:
+                    $addr = sprintf('%s <%s>',
+                            $recipient->getName(),
+                            $recipient->getEmail());
+                    switch ($recipient->getType()) {
+                        case 'to':
+                            $mime->addTo($addr);
+                            break;
+                        case 'cc':
+                            $mime->addCc($addr);
+                            break;
+                        case 'bcc':
+                            $mime->addBcc($addr);
+                            break;
+                    }
+                    break;
                 case $recipient instanceof TicketOwner:
                 case $recipient instanceof Staff:
                     $mime->addTo(sprintf('%s <%s>',
                                 $recipient->getName(),
                                 $recipient->getEmail()));
-                break;
+                    break;
                 case $recipient instanceof Collaborator:
                     $mime->addCc(sprintf('%s <%s>',
                                 $recipient->getName(),
                                 $recipient->getEmail()));
-                break;
+                    break;
                 default:
                     // Assuming email address.
                     $mime->addTo($recipient);
@@ -572,6 +585,7 @@ class Mailer {
         if ($this->getEmail())
             $args = array('-f '.$this->getEmail()->getEmail());
         $mail = mail::factory('mail', $args);
+        $to = $headers['To'];
         $result = $mail->send($to, $headers, $body);
         if(!PEAR::isError($result))
             return $messageId;
diff --git a/include/class.thread.php b/include/class.thread.php
index 177c608a0f66f6a9461ee00e3a01ce8f4815ffa3..66b339d0ac6837b42091eb177e606f1aedf148bc 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -1466,18 +1466,6 @@ implements TemplateVariable {
         return $entry;
     }
 
-    function setReplyFlag($entry, $replyType) {
-      switch ($replyType) {
-        case 'all':
-          return $entry->flags |= ThreadEntry::FLAG_REPLY_ALL;
-          break;
-
-        case 'user':
-          return $entry->flags |= ThreadEntry::FLAG_REPLY_USER;
-          break;
-      }
-    }
-
     //new entry ... we're trusting the caller to check validity of the data.
     static function create($vars=false) {
         global $cfg;
@@ -1513,37 +1501,15 @@ implements TemplateVariable {
             'poster' => $poster,
             'source' => $vars['source'],
             'flags' => $vars['flags'] ?: 0,
-            'recipients' => $vars['recipients'],
         ));
 
         //add recipients to thread entry
-        $recipients = array();
-        $ticket = Thread::objects()->filter(array('id'=>$vars['threadId']))->values_flat('object_id')->first();
-        $ticketUser = Ticket::objects()->filter(array('ticket_id'=>$ticket[0]))->values_flat('user_id')->first();
-
-        //User
-        if ($ticketUser && strcasecmp('none', $vars['reply-to'])) {
-          $uEmail = UserEmailModel::objects()->filter(array('user_id'=>$ticketUser[0]))->values_flat('address')->first();
-          $u = array();
-          $u[$ticketUser[0]] = $uEmail[0];
-          $recipients['to'] = $u;
-        }
+        if ($vars['recipients'])
+            $entry->recipients = json_encode($vars['recipients']);
 
         if (Collaborator::getIdByUserId($vars['userId'], $vars['threadId']))
           $entry->flags |= ThreadEntry::FLAG_COLLABORATOR;
 
-        //add reply type flag
-        self::setReplyFlag($entry, $vars['reply-to']);
-
-        //Cc collaborators
-        if ($vars['ccs'] && !strcasecmp('all', $vars['reply-to'])) {
-          $cc = Collaborator::getCollabList($vars['ccs']);
-          $recipients['cc'] = $cc;
-        }
-
-        if ($vars['reply-to'] != 'none' && $recipients)
-          $entry->recipients = json_encode($recipients);
-
         if ($entry->format == 'html')
             // The current codebase properly balances html
             $entry->flags |= self::FLAG_BALANCED;
@@ -2893,10 +2859,19 @@ implements TemplateVariable {
     }
 
     function addResponse($vars, &$errors) {
-
         $vars['threadId'] = $this->getId();
         $vars['userId'] = 0;
-        $vars['pid'] = $this->getLastMessage()->id;
+        $vars['pid'] = $this->getLastMessage()->getId();
+
+        $vars['flags'] = 0;
+        switch ($vars['reply-to']) {
+            case 'all':
+                $vars['flags'] |= ThreadEntry::FLAG_REPLY_ALL;
+            break;
+            case 'user':
+                $vars['flags'] |= ThreadEntry::FLAG_REPLY_USER;
+            break;
+        }
 
         if (!($resp = ResponseThreadEntry::add($vars, $errors)))
             return $resp;
diff --git a/include/class.ticket.php b/include/class.ticket.php
index f9646b39df8096adb8c07a48c1b8de681c143cb5..bec6136bfee9b6ce72375cc15cca22f2db1b0400 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -841,18 +841,29 @@ implements RestrictedAccess, Threadable, Searchable {
         return $entries;
     }
 
-    // MailingList of recipients  (owner + active collaborators)
-    function getRecipients() {
-        if (!isset($this->recipients)) {
-            $list = new MailingList();
-            $list->add($this->getOwner());
-            if ($collabs = $this->getActiveCollaborators()) {
-                foreach ($collabs as $c)
-                    $list->add($c);
-            }
-            $this->recipients = $list;
+    // MailingList of participants  (owner + collaborators)
+    function getRecipients($who='all', $whitelist=array(), $active=true) {
+        $list = new MailingList();
+        switch (strtolower($who)) {
+            case 'user':
+                $list->addTo($this->getOwner());
+                break;
+            case 'all':
+                $list->addTo($this->getOwner());
+                // Fall-trough
+            case 'collabs':
+                if (($collabs = $active ?  $this->getActiveCollaborators() :
+                    $this->getCollaborators())) {
+                    foreach ($collabs as $c)
+                        if (!$whitelist || in_array($c->getUserId(),
+                                    $whitelist))
+                            $list->addCc($c);
+                }
+                break;
+            default:
+                return null;
         }
-        return $this->recipients;
+        return $list;
     }
 
     function getCollaborators() {
@@ -1295,18 +1306,15 @@ implements RestrictedAccess, Threadable, Searchable {
         return $this->save();
     }
 
-    //Status helper.
-
+    // Ticket Status helper.
     function setStatus($status, $comments='', &$errors=array(), $set_closing_agent=true) {
         global $thisstaff;
 
         if ($thisstaff && !($role=$this->getRole($thisstaff)))
             return false;
 
-        if ($status && is_numeric($status))
-            $status = TicketStatus::lookup($status);
-
-        if (!$status || !$status instanceof TicketStatus)
+        if ((!$status instanceof TicketStatus)
+                && !($status = TicketStatus::lookup($status)))
             return false;
 
         // Double check permissions (when changing status)
@@ -1317,7 +1325,7 @@ implements RestrictedAccess, Threadable, Searchable {
                     return false;
                 break;
             case 'deleted':
-                // XXX: intercept deleted status and do hard delete
+                // XXX: intercept deleted status and do hard delete TODO: soft deletes
                 if ($role->hasPerm(Ticket::PERM_DELETE))
                     return $this->delete($comments);
                 // Agent doesn't have permission to delete  tickets
@@ -2885,18 +2893,6 @@ implements RestrictedAccess, Threadable, Searchable {
     function postReply($vars, &$errors, $alert=true, $claim=true) {
         global $thisstaff, $cfg;
 
-        // Add new collabs if any.
-        $collabs = $this->getCollaborators();
-        if (isset($vars['ccs'])) {
-            foreach ($vars['ccs'] as $uid) {
-                if ($collabs->findFirst(array('user_id' => $uid)))
-                    continue;
-
-                if ($user=User::lookup($uid))
-                    $this->addCollaborator($user, array(), $errors);
-            }
-        }
-
         if (!$vars['poster'] && $thisstaff)
             $vars['poster'] = $thisstaff;
 
@@ -2906,18 +2902,25 @@ implements RestrictedAccess, Threadable, Searchable {
         if (!$vars['ip_address'] && $_SERVER['REMOTE_ADDR'])
             $vars['ip_address'] = $_SERVER['REMOTE_ADDR'];
 
+        // Add new collaboratorss (if any).
+        if (isset($vars['ccs']) && count($vars['ccs']))
+            $this->addCollaborators($vars['ccs']);
+
+        // Get active recipients of the response
+        $recipients = $this->getRecipients($vars['reply-to'], $vars['ccs']);
+        if ($recipients instanceof MailingList)
+            $vars['recipients'] = $recipients->getEmailAddresses();
+
         if (!($response = $this->getThread()->addResponse($vars, $errors)))
             return null;
 
         $dept = $this->getDept();
         $assignee = $this->getStaff();
-        // Set status - if checked.
+        // Set status if new is selected
         if ($vars['reply_status_id']
-            && $vars['reply_status_id'] != $this->getStatusId()
-        ) {
-            $this->setStatus($vars['reply_status_id']);
-        }
-
+                && ($status = TicketStatus::lookup($vars['reply_status_id']))
+                && $status->getId() != $this->getStatusId())
+            $this->setStatus($status);
 
         // Claim on response bypasses the department assignment restrictions
         $claim = ($claim
@@ -2973,8 +2976,9 @@ implements RestrictedAccess, Threadable, Searchable {
         );
 
         if ($email
-            && ($tpl = $dept->getTemplate())
-            && ($msg=$tpl->getReplyMsgTemplate())) {
+                && $recipients
+                && ($tpl = $dept->getTemplate())
+                && ($msg=$tpl->getReplyMsgTemplate())) {
 
             $msg = $this->replaceVars($msg->asArray(),
                 $variables + array('recipient' => $this->getOwner())
@@ -2984,17 +2988,6 @@ implements RestrictedAccess, Threadable, Searchable {
             $attachments = $cfg->emailAttachments() ?
                 $response->getAttachments() : array();
 
-            // Get active recipients
-            $recipients = new MailingList();
-            $recipients->add($this->getOwner());
-            if (!strcasecmp('all', $vars['reply-to'])
-                    &&  ($collabs = $this->getActiveCollaborators())){
-                foreach ($collabs as $c)
-                    if ($vars['ccs'] && in_array($c->getUserId(),
-                                $vars['ccs']))
-                        $recipients->add($c);
-            }
-
             //Send email to recepients
             $email->send($recipients, $msg['subj'], $msg['body'],
                     $attachments, $options);
@@ -4070,35 +4063,30 @@ implements RestrictedAccess, Threadable, Searchable {
         if (!($ticket=self::create($create_vars, $errors, 'staff', false)))
             return false;
 
-        if (isset($vars['ccs']))
-          $ticket->addCollaborators($vars['ccs'], array(), $errors);
-
-        if (strcasecmp('user', $vars['reply-to']))
-            $recipients = $ticket->getRecipients();
-        else
-            $recipients = $ticket->getOwner();
-
         $vars['msgId']=$ticket->getLastMsgId();
 
         // Effective role for the department
         $role = $ticket->getRole($thisstaff);
 
-        $alert = strcasecmp('none', $vars['reply-to']);
+        // Add collaborators (if any)
+        if (isset($vars['ccs']) && count($vars['ccs']))
+          $ticket->addCollaborators($vars['ccs'], array(), $errors);
 
+        $alert = strcasecmp('none', $vars['reply-to']);
         // post response - if any
         $response = null;
-        if($vars['response'] && $role->hasPerm(Ticket::PERM_REPLY)) {
+        if ($vars['response'] && $role->hasPerm(Ticket::PERM_REPLY)) {
             $vars['response'] = $ticket->replaceVars($vars['response']);
             // $vars['cannedatachments'] contains the attachments placed on
             // the response form.
-            $response = $ticket->postReply($vars, $errors, $alert && !$cfg->notifyONNewStaffTicket());
+            $response = $ticket->postReply($vars, $errors, ($alert &&
+                        !$cfg->notifyONNewStaffTicket()));
         }
 
         // Not assigned...save optional note if any
         if (!$ticket->isAssigned() && $vars['note']) {
-            if (!$cfg->isRichTextEnabled()) {
+            if (!$cfg->isRichTextEnabled())
                 $vars['note'] = new TextThreadEntryBody($vars['note']);
-            }
             $ticket->logNote(_S('New Ticket'), $vars['note'], $thisstaff, false);
         }
 
@@ -4108,6 +4096,10 @@ implements RestrictedAccess, Threadable, Searchable {
         ) {
             return $ticket; //No alerts.
         }
+
+        // Notice Recipients
+        $recipients = $ticket->getRecipients($vars['reply-to']);
+
         // Send Notice to user --- if requested AND enabled!!
         if (($tpl=$dept->getTemplate())
             && ($msg=$tpl->getNewTicketNoticeMsgTemplate())
diff --git a/include/class.util.php b/include/class.util.php
index 52035bcb2287a0e204745c504bb8f5c62e2b7f28..962ef6f7a33efd342c3365b7c723d520ef9bc4fc 100644
--- a/include/class.util.php
+++ b/include/class.util.php
@@ -9,6 +9,34 @@ interface EmailContact {
     function getEmail();
 }
 
+
+class EmailRecipient
+implements EmailContact {
+    protected $contact;
+    protected $type;
+
+    function __construct(EmailContact $contact, $type='to') {
+        $this->contact = $contact;
+        $this->type = $type;
+    }
+
+    function getId() {
+        return $this->contact->getId();
+    }
+
+    function getEmail() {
+        return $this->contact->getEmail();
+    }
+
+    function getName() {
+        return $this->contact->getName();
+    }
+
+    function getType() {
+        return $this->type;
+    }
+}
+
 abstract class BaseList
 implements IteratorAggregate, Countable {
     protected $storage = array();
@@ -190,17 +218,43 @@ implements ArrayAccess, Serializable {
 class MailingList extends ListObject
 implements TemplateVariable {
 
-    function add($contact) {
-        if (!$contact instanceof EmailContact)
-            throw new InvalidArgumentException('Email Contact expected');
+    function add($recipient) {
+        if (!$recipient instanceof EmailRecipient)
+            throw new InvalidArgumentException('Email Recipient expected');
 
-        return parent::add($contact);
+        return parent::add($recipient);
+    }
+
+    function addRecipient($contact, $to='to') {
+        return $this->add(new EmailRecipient($contact, $to));
+    }
+
+    function addTo(EmailContact $contact) {
+        return $this->addRecipient($contact, 'to');
+    }
+
+    function addCc(EmailContact $contact) {
+        return $this->addRecipient($contact, 'cc');
+    }
+
+    function addBcc(EmailContact $contact) {
+        return $this->addRecipient($contact, 'bcc');
     }
 
     function __toString() {
         return $this->getNames();
     }
 
+    // Recipients' email addresses
+    function getEmailAddresses() {
+        $list = array();
+        foreach ($this->storage as $u) {
+            $list[$u->getType()][$u->getId()] = sprintf("%s <%s>",
+                    $u->getName(), $u->getEmail());
+        }
+        return $list;
+    }
+
     function getNames() {
         $list = array();
         foreach($this->storage as $user) {
diff --git a/scp/tickets.php b/scp/tickets.php
index 444275482087101137b03c913c6a72957efe1d64..e6f668aeb2b551df1ce9ce2e431c4164c9283de4 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -147,8 +147,7 @@ if($_POST && !$errors):
         case 'reply':
             if (!$role || !$role->hasPerm(Ticket::PERM_REPLY)) {
                 $errors['err'] = __('Action denied. Contact admin for access');
-            }
-            else {
+            } else {
                 $vars = $_POST;
                 $vars['cannedattachments'] = $response_form->getField('attachments')->getClean();
                 $vars['response'] = ThreadEntryBody::clean($vars['response']);
@@ -175,8 +174,9 @@ if($_POST && !$errors):
                 if(!$errors['err'] && Banlist::isBanned($ticket->getEmail()))
                     $errors['err']=__('Email is in banlist. Must be removed to reply.');
             }
+
             $alert =  strcasecmp('none', $_POST['reply-to']);
-            if(!$errors && ($response=$ticket->postReply($vars, $errors,
+            if (!$errors && ($response=$ticket->postReply($vars, $errors,
                             $alert))) {
                 $msg = sprintf(__('%s: Reply posted successfully'),
                         sprintf(__('Ticket #%s'),
@@ -203,7 +203,7 @@ if($_POST && !$errors):
                 if ($ticket)
                     $redirect = 'tickets.php?id='.$ticket->getId();
 
-            } elseif(!$errors['err']) {
+            } elseif (!$errors['err']) {
                 $errors['err']=sprintf('%s %s',
                     __('Unable to post the reply.'),
                     __('Correct any errors below and try again.'));