? .cvsignore
? privatemsg_access_denied.patch
? privatemsg_access_denied2.patch
Index: privatemsg.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.module,v
retrieving revision 1.109
diff -u -p -r1.109 privatemsg.module
--- privatemsg.module	7 Dec 2009 16:28:47 -0000	1.109
+++ privatemsg.module	14 Dec 2009 14:37:25 -0000
@@ -138,10 +138,14 @@ function privatemsg_menu() {
   );
   $items['messages/view/%privatemsg_thread'] = array(
     'title'            => 'Read message',
+    // Set the third argument to TRUE so that we can show access denied instead
+    // of not found.
+    'load arguments'   => array(NULL, NULL, TRUE),
     'page callback'    => 'privatemsg_view',
     'page arguments'   => array(2),
     'file'             => 'privatemsg.pages.inc',
     'access callback'  => 'privatemsg_view_access',
+    'access arguments' => array(2),
     'type'             => MENU_LOCAL_TASK,
     'weight'           => -5,
   );
@@ -247,9 +251,18 @@ function privatemsg_user_access($permiss
  * messages/view/% pages and not to leave tabs artifact on other lower
  * level pages such as the messages/new/%.
  *
+ * @param $thread
+ *   A array containing all information about a specific thread, generated by
+ *   privatemsg_thread_load().
+ *
  * @ingroup api
  */
-function privatemsg_view_access() {
+function privatemsg_view_access($thread) {
+  // Do not allow access to threads without messages.
+  if (empty($thread['messages'])) {
+    // Count all messages, if there
+    return FALSE;
+  }
   if (privatemsg_user_access('read privatemsg') && arg(1) == 'view') {
     return TRUE;
   }
@@ -269,6 +282,10 @@ function privatemsg_view_access() {
  *   the current user.
  * @param $start
  *   Message offset from the start of the thread.
+ * @param $useAccessDenied
+ *   Set to TRUE if the function should forward to the access denied page
+ *   instead of not found. This is used by the menu system because that does
+ *   load arguments before access checks are made. Defaults to FALSE.
  *
  * @return
  *   $thread object, with keys messages, participants, title and user. messages
@@ -280,7 +297,7 @@ function privatemsg_view_access() {
 
  * @ingroup api
  */
-function privatemsg_thread_load($thread_id, $account = NULL, $start = NULL) {
+function privatemsg_thread_load($thread_id, $account = NULL, $start = NULL, $useAccessDenied = FALSE) {
   static $threads = array();
   if ((int)$thread_id > 0) {
     $thread = array('thread_id' => $thread_id);
@@ -380,7 +397,18 @@ function privatemsg_thread_load($thread_
 
       // If there are no messages, don't allow access to the thread.
       if (empty($thread['messages'])) {
-        $thread = FALSE;
+        if ($useAccessDenied) {
+          // Generate new query with read all to see if the thread does exist.
+          $query = _privatemsg_assemble_query('messages', array($thread_id), NULL);
+          $exists = db_result(db_query($query['count']));
+          if (!$exists) {
+            // Thread does not exist, display 404.
+            $thread = FALSE;
+          }
+        }
+        else {
+          $thread = FALSE;
+        }
       }
       else {
         // General data, assume subject is the same for all messages of that thread.
Index: privatemsg.test
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.test,v
retrieving revision 1.4
diff -u -p -r1.4 privatemsg.test
--- privatemsg.test	7 Dec 2009 16:28:47 -0000	1.4
+++ privatemsg.test	14 Dec 2009 14:37:26 -0000
@@ -38,14 +38,34 @@ class PrivatemsgTestCase extends DrupalW
    */
   function testPrivatemsgReadPrivatemsgPermission() {
     $user_no_read_msg = $this->drupalCreateUser(); // set up user with default permissions (meaning: no read privatemsg permission
+    $author     = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
+    $recipient  = $this->drupalCreateUser(array('read privatemsg'));
+    $no_recipient  = $this->drupalCreateUser(array('read privatemsg'));
+
+
+    $subject = $this->randomName(20);
+    $body = $this->randomName(50);
+
+    $response = privatemsg_new_thread(array($recipient), $subject, $body, array('author' => $author));
+
     $this->drupalLogin($user_no_read_msg);
     $this->drupalGet('messages');
     $this->assertResponse(403, t('HTTP Response 403: Access to mailbox was blocked to user without "<em>read privatemsg</em>" permission'));
 
-    $user_read_msg = $this->drupalCreateUser(array('read privatemsg')); // set up user with default permissions (meaning: no read privatemsg permission
-    $this->drupalLogin($user_read_msg);
+    $this->drupalLogin($no_recipient);
     $this->drupalGet('messages');
     $this->assertResponse(200, t('HTTP Response 200: Access to mailbox was authorized to user with "<em>read privatemsg</em>" permission'));
+
+    $this->drupalGet('messages/view/' . $response['message']['thread_id']);
+    $this->assertResponse(403, t('HTTP Response 403: Access to thread is blocked for non-recipients.'));
+
+    $this->drupalLogin($recipient);
+    $this->drupalGet('messages/view/' . $response['message']['thread_id']);
+    $this->assertText($subject, t('Access to thread for recipient allowed.'));
+
+    $this->drupalGet('messages/view/' . $response['message']['thread_id'] + 1);
+    $this->assertResponse(404, t('Non-existing thread lead to HTTP Response 404.'));
+
   }
   /**
    * Test user access to /messages/new
