diff --git a/include/api.tickets.php b/include/api.tickets.php
index 47d978a498aac405381d4f5659f2b8072d092da6..6371daa632ff5240b0bee90cb3bed69c0ac56384 100644
--- a/include/api.tickets.php
+++ b/include/api.tickets.php
@@ -149,6 +149,10 @@ class TicketApiController extends ApiController {
             $data = $this->getEmailRequest();
 
         if (($thread = ThreadEntry::lookupByEmailHeaders($data))
+                && ($t=$thread->getTicket())
+                && ($data['staffId']
+                    || !$t->isClosed()
+                    || $t->isReopenable())
                 && $thread->postEmail($data)) {
             return $thread->getTicket();
         }
diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php
index 45758c75c052057061c354e77e2c5faef639e4ff..4e6546549f44d7ad520670bb01af8e946534aaea 100644
--- a/include/class.mailfetch.php
+++ b/include/class.mailfetch.php
@@ -721,6 +721,10 @@ class MailFetcher {
 
         $seen = false;
         if (($thread = ThreadEntry::lookupByEmailHeaders($vars, $seen))
+                && ($t=$thread->getTicket())
+                && ($vars['staffId']
+                    || !$t->isClosed()
+                    || $t->isReopenable())
                 && ($message = $thread->postEmail($vars))) {
             if (!$message instanceof ThreadEntry)
                 // Email has been processed previously
diff --git a/include/class.ticket.php b/include/class.ticket.php
index d2b9a6dec7a00053bdc2d5f63d12e109f8ee28bd..0dd0759524db699f4645db80604be81dfb0c4195 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -142,6 +142,10 @@ class Ticket {
         return ($this->getReopenDate());
     }
 
+    function isReopenable() {
+        return $this->getStatus()->isReopenable();
+    }
+
     function isClosed() {
          return $this->hasState('closed');
     }
@@ -939,11 +943,19 @@ class Ticket {
         return (db_query($sql) && db_affected_rows());
     }
 
-    //set status to open on a closed ticket.
-    function reopen($isanswered=0) {
+    function reopen() {
         global $cfg;
 
-        return $this->setStatus($cfg->getDefaultTicketStatusId());
+        if (!$this->isClosed())
+            return false;
+
+        // Set status to open based on current closed status settings
+        // If the closed status doesn't have configured "reopen" status then use the
+        // the default ticket status.
+        if (!($status=$this->getStatus()->getReopenStatus()))
+            $status = $cfg->getDefaultTicketStatusId();
+
+        return $status ? $this->setStatus($status, 'Reopened') : false;
     }
 
     function onNewTicket($message, $autorespond=true, $alertstaff=true) {
@@ -1149,8 +1161,9 @@ class Ticket {
             }
         }
 
-        // Reopen if NOT open
-        if(!$this->isOpen()) $this->reopen();
+        // Reopen if closed AND reopenable
+        if ($this->isClosed() && $this->isReopenable())
+            $this->reopen();
 
        /**********   double check auto-response  ************/
         if (!($user = $message->getUser()))
diff --git a/include/client/view.inc.php b/include/client/view.inc.php
index 454cc9e7d80e8977d32803417dc927d8167ef0e7..1867ac467ab9b3ac50496c6f00c63c0abad3d150 100644
--- a/include/client/view.inc.php
+++ b/include/client/view.inc.php
@@ -4,6 +4,10 @@ if(!defined('OSTCLIENTINC') || !$thisclient || !$ticket || !$ticket->checkUserAc
 $info=($_POST && $errors)?Format::htmlchars($_POST):array();
 
 $dept = $ticket->getDept();
+
+if ($ticket->isClosed() && !$ticket->isReopenable())
+    $warn = __('This ticket is marked as closed and cannot be reopened.');
+
 //Making sure we don't leak out internal dept names
 if(!$dept || !$dept->isPublic())
     $dept = $cfg->getDefaultDept();
@@ -146,6 +150,10 @@ if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) {
 <?php }elseif($warn) { ?>
     <div id="msg_warning"><?php echo $warn; ?></div>
 <?php } ?>
+
+<?php
+
+if (!$ticket->isClosed() || $ticket->isReopenable()) { ?>
 <form id="reply" action="tickets.php?id=<?php echo $ticket->getId(); ?>#reply" name="reply" method="post" enctype="multipart/form-data">
     <?php csrf_token(); ?>
     <h2><?php echo __('Post a Reply');?></h2>
@@ -183,3 +191,5 @@ if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) {
         <input type="button" value="<?php echo __('Cancel');?>" onClick="history.go(-1)">
     </p>
 </form>
+<?php
+} ?>
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index ffaaa8a120b2292720b6bdba85ce95059fa634a3..b0dc9ee4aa4ee6cc4f91271c3be2a46adbc5c2d4 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -22,15 +22,27 @@ $lock  = $ticket->getLock();  //Ticket lock obj
 $id    = $ticket->getId();    //Ticket ID.
 
 //Useful warnings and errors the user might want to know!
-if($ticket->isAssigned() && (
-            ($staff && $staff->getId()!=$thisstaff->getId())
-         || ($team && !$team->hasMember($thisstaff))
+if ($ticket->isClosed() && !$ticket->isReopenable())
+    $warn = sprintf(
+            __('Current ticket status (%s) does not allow the end user to reply.'),
+            $ticket->getStatus());
+elseif ($ticket->isAssigned()
+        && (($staff && $staff->getId()!=$thisstaff->getId())
+            || ($team && !$team->hasMember($thisstaff))
         ))
-    $warn.='&nbsp;&nbsp;<span class="Icon assignedTicket">'.sprintf(__('Ticket is assigned to %s'),implode('/', $ticket->getAssignees())).'</span>';
-if(!$errors['err'] && ($lock && $lock->getStaffId()!=$thisstaff->getId()))
-    $errors['err']=sprintf(__('This ticket is currently locked by %s'),$lock->getStaffName());
-if(!$errors['err'] && ($emailBanned=TicketFilter::isBanned($ticket->getEmail())))
-    $errors['err']=__('Email is in banlist! Must be removed before any reply/response');
+    $warn.= sprintf('&nbsp;&nbsp;<span class="Icon assignedTicket">%</span>',
+            sprintf(__('Ticket is assigned to %s'),
+                implode('/', $ticket->getAssignees())
+                ));
+
+if (!$errors['err']) {
+
+    if ($lock && $lock->getStaffId()!=$thisstaff->getId())
+        $errors['err'] = sprintf(__('This ticket is currently locked by %s'),
+                $lock->getStaffName());
+    elseif (($emailBanned=TicketFilter::isBanned($ticket->getEmail())))
+        $errors['err'] = __('Email is in banlist! Must be removed before any reply/response');
+}
 
 $unbannable=($emailBanned) ? BanList::includes($ticket->getEmail()) : false;