diff --git a/privatemsg_forward/privatemsg_forward.info b/privatemsg_forward/privatemsg_forward.info
new file mode 100644
index 0000000..90eee47
--- /dev/null
+++ b/privatemsg_forward/privatemsg_forward.info
@@ -0,0 +1,7 @@
+name = Privatemsg Forward
+description = Allows a whole thread to be forwarded to another user.
+package = Mail
+core = 7.x
+dependencies[] = privatemsg
+files[]=privatemsg_forward.test
+
diff --git a/privatemsg_forward/privatemsg_forward.module b/privatemsg_forward/privatemsg_forward.module
new file mode 100644
index 0000000..799ad2a
--- /dev/null
+++ b/privatemsg_forward/privatemsg_forward.module
@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * @file
+ * Module file for privatemsg_forward.module.
+ */
+
+/**
+ * Implements hook_permission().
+ */
+function privatemsg_forward_permission() {
+  return array(
+    'forward a privatemsg thread' => array(
+      'title' => t('Forward a message'),
+      'description' => t('Forward a privatemsg thread to one or more users.'),
+    ),
+    'remove from a privatemsg thread' => array(
+      'title' => t('Remove from thread'),
+      'description' => t('Remove from a privatemsg thread after forwarding it.'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function privatemsg_forward_form_privatemsg_new_alter(&$form, &$form_state) {
+  if (isset($form['thread_id'])) {
+    $form['forward'] = array(
+      '#type'               => 'fieldset',
+      '#access'             => privatemsg_user_access('forward a privatemsg thread'),
+      '#collapsible'        => TRUE,
+      '#collapsed'          => TRUE,
+      '#title'              => t('Forward conversation to others'),
+      '#weight'             => 5,
+    );
+
+    $form['forward']['to']  = array(
+      '#type'               => 'textfield',
+      '#title'              => t('To'),
+      '#description'        => t('Separate multiple names with commas.'),
+      '#required'           => FALSE,
+      '#weight'             => -10,
+      '#size'               => 50,
+      '#autocomplete_path'  => 'messages/autocomplete',
+    );
+
+    $form['forward']['remove'] = array(
+      '#type'               => 'checkbox',
+      '#title'              => t('Remove myself as a conversation participant'),
+      '#description'        => t('You will no longer be listed as a participant in this conversation nor will you receive new messages. However, any messages you have previously posted will still appear in the conversation.'),
+      '#default_value'      => FALSE,
+      '#access'             => privatemsg_user_access('remove from a privatemsg thread'),
+    );
+
+    $form['forward']['submit'] = array(
+      '#type'               => 'submit',
+      '#value'              => t('Forward this conversation'),
+      '#weight'             => 15,
+      '#submit'             => array('privatemsg_forward_form_submit', 'privatemsg_forward_form_delete_submit'),
+      '#validate'           => array('privatemsg_forward_form_validate'),
+    );
+
+    // Also add submit and validate function to the global ones so that the
+    // Send message button can be used.
+    $form['#validate'][] = 'privatemsg_forward_form_validate';
+    // Add forward submit callback before default so that the message is sent
+    // to the new recipients too.
+    array_unshift($form['#submit'], 'privatemsg_forward_form_submit');
+    // Add delete submit function at the end so that it also removes the current
+    // user from the new reply.
+    $form['#submit'][] = 'privatemsg_forward_form_delete_submit';
+  }
+}
+
+/**
+ * Validate function for forward form.
+ */
+function privatemsg_forward_form_validate($form, &$form_state) {
+  global $user;
+  // Parse user string.
+  list($new_recipients, $invalid) = _privatemsg_parse_userstring($form_state['values']['to']);
+
+  // Display invalid names.
+  if (!empty($invalid)) {
+    drupal_set_message(t('The following users will not receive this private message: @invalid.', array('@invalid' => implode(', ', $invalid))), 'error');
+  }
+
+  // Remove blocked participants.
+  if (!empty($new_recipients)) {
+    foreach (module_invoke_all('privatemsg_block_message', $user, $new_recipients) as $blocked) {
+      unset($new_recipients[$blocked['recipient']]);
+      drupal_set_message($blocked['message'], 'warning');
+    }
+  }
+
+  $form_state['new_recipients'] = $new_recipients;
+}
+
+/**
+ * Submit function for forward form.
+ */
+function privatemsg_forward_form_submit($form, &$form_state) {
+  global $user;
+  $new_recipients = $form_state['new_recipients'];
+
+  // Don't forward if no valid participants have been entered.
+  if (!empty($new_recipients)) {
+    $thread = privatemsg_thread_load($form_state['values']['thread_id']);
+    $new_names = array();
+    // For every message and every new participant, add them as recipient.
+    foreach ($thread['messages'] as $mid => $message)  {
+      foreach ($new_recipients as $new_recipient) {
+        $new_names[privatemsg_recipient_key($new_recipient)] = privatemsg_recipient_format($new_recipient);
+        privatemsg_message_change_recipient($mid, $new_recipient->recipient, $new_recipient->type);
+      }
+      _privatemsg_handle_recipients($mid, $new_recipients);
+    }
+    drupal_set_message(t('This conversation has been forwarded to !new.', array('!new' => implode(', ', $new_names))));
+  }
+
+  // If the body field is non-empty, add a new reply to the thread.
+  // Only do this if the form has not been submitted with the Send message
+  // button to avoid sending the message twice.
+  if (!empty($form_state['values']['body']['value'])) {
+    // Call validate again to refresh information.
+    privatemsg_new_validate($form, $form_state);
+    // Only explicitly call submit callback if it won't happen automatically.
+    if ($form_state['clicked_button']['#value'] != t('Send message')) {
+      privatemsg_new_submit($form, $form_state);
+    }
+
+  }
+}
+
+/**
+ * Submit callback to remove the current user as recipient of the thread.
+ */
+function privatemsg_forward_form_delete_submit($form, &$form_state) {
+  global $user;
+  if ($form_state['values']['remove']) {
+    db_delete('pm_index')
+      ->condition('thread_id', $form_state['values']['thread_id'])
+      ->condition('recipient', $user->uid)
+      ->condition('type', array('hidden', 'user'))
+      ->execute();
+
+    // Display message and redirect to messages if user has removed himself.
+    $form_state['redirect'] = 'messages';
+    drupal_set_message(t('You have been removed as a participant in the conversation.'));
+  }
+}
diff --git a/privatemsg_forward/privatemsg_forward.test b/privatemsg_forward/privatemsg_forward.test
new file mode 100644
index 0000000..5b53235
--- /dev/null
+++ b/privatemsg_forward/privatemsg_forward.test
@@ -0,0 +1,164 @@
+<?php
+/**
+ * @file
+ * Test file for privatemsg_forward.module
+ */
+
+class PrivatemsgForwardTestCase extends DrupalWebTestCase {
+
+  private $author;
+  private $recipient1;
+  private $recipient2;
+
+  private $firstMessage;
+
+  /**
+   * Implements getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('Privatemsg forward functionality.'),
+      'description' => t('Test forward/remove myself features of the module.'),
+      'group' => t('Privatemsg'),
+    );
+  }
+
+  /**
+   * Implements setUp().
+   */
+  function setUp() {
+    parent::setUp('privatemsg', 'privatemsg_forward');
+  }
+
+  /**
+   * Test forwarding a thread.
+   *
+   * Including with/without sending a message and/or removing from the thread.
+   */
+  function testForward() {
+    $this->author = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
+    $this->recipient1 = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg', 'forward a privatemsg thread', 'remove from a privatemsg thread'));
+    $this->recipient2 = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg', 'forward a privatemsg thread'));
+
+    // Just forward.
+    $this->createThread();
+    $justForward = array('to' => $this->recipient2->name);
+    $this->drupalPost(NULL, $justForward, t('Forward this conversation'));
+
+    // Validate forwarding.
+    $this->assertText($this->firstMessage['body[value]'], t('Thread is still displayed'));
+    $this->goToThread($this->recipient2);
+
+    // Forward and remove myself.
+    $this->createThread();
+    $forwardDelete = array(
+      'to' => $this->recipient2->name,
+      'remove' => TRUE,
+    );
+    $this->drupalPost(NULL, $forwardDelete, t('Forward this conversation'));
+    $this->assertNoText($this->firstMessage['subject'], t('User has been removed from thread'));
+    $this->goToThread($this->recipient2);
+
+    // Check that after sending a response, the thread is still hidden.
+    $reply = array(
+      'body[value]' => $this->randomName(100),
+    );
+    $this->drupalPost(NULL, $reply, t('Send message'));
+    $this->assertText($reply['body[value]'], t('New reply is displayed'));
+    $this->drupalLogin($this->recipient1);
+    $this->drupalGet('messages');
+    $this->assertNoText($this->firstMessage['subject'], t('Forwarded has not recieved the reply'));
+
+    //Forward and reply.
+    $this->createThread();
+    $forwardReply = array(
+      'to' => $this->recipient2->name,
+      'body[value]' => $this->randomName(200),
+    );
+    // It is also possible to use the Send message button when there is a reply.
+    $this->drupalPost(NULL, $forwardReply, t('Send message'));
+
+    $this->assertText($forwardReply['body[value]'], t('New reply is displayed'));
+    $this->goToThread($this->recipient2, $forwardReply['body[value]']);
+
+    // Forward, reply and remove myself.
+    $this->createThread();
+    $forwardReplyDelete = array(
+      'to' => $this->recipient2->name,
+      'remove' => TRUE,
+      'body[value]' => $this->randomName(200),
+    );
+    $this->drupalPost(NULL, $forwardReplyDelete, t('Forward this conversation'));
+
+    // Additional forward because thread name is displayed as a info message.
+    $this->assertNoText($this->firstMessage['subject'], t('User has been removed from thread'));
+    $this->goToThread($this->recipient2, $forwardReplyDelete['body[value]']);
+
+    // Verify that the second recipient can see the forward fieldset but not
+    // the remove myself button.
+    $this->assertText(t('Forward conversation to others'));
+    $this->assertNoText(t('Remove myself as a conversation participant'));
+
+    // Remove only.
+    $this->createThread();
+    $removeOnly = array(
+      'remove' => TRUE,
+    );
+    $this->drupalPost(NULL, $removeOnly, t('Forward this conversation'));
+    $this->assertText(t('You have been removed as a participant in the conversation.'), t('Confirmation message displayed.'));
+    $this->assertNoText($this->firstMessage['subject'], t('User has been removed from thread'));
+
+    // Remove and reply.
+    $this->createThread();
+    $removeReply = array(
+      'remove' => TRUE,
+      'body[value]'   => $this->randomName(100),
+    );
+    $this->drupalPost(NULL, $removeReply, t('Forward this conversation'));
+    $this->assertText(t('You have been removed as a participant in the conversation.'), t('Confirmation message displayed.'));
+    $this->assertNoText($this->firstMessage['subject'], t('User has been removed from thread'));
+    $this->goToThread($this->author, $removeReply['body[value]']);
+
+    // Verify that the author can't see the forward fieldset.
+    $this->assertNoText(t('Forward conversation to others'));
+  }
+
+  /**
+   * Create the first message of a thread.
+   */
+  function createThread() {
+    $this->firstMessage = array(
+      'recipient' => $this->recipient1->name,
+      'subject' => $this->randomName(20),
+      'body[value]' => $this->randomName(100),
+    );
+
+    // Login author.
+    $this->drupalLogin($this->author);
+
+    // Submit the message.
+    $this->drupalPost('messages/new', $this->firstMessage, t('Send message'));
+    $this->assertText(t('A message has been sent to @recipients.', array('@recipients' => $this->recipient1->name)), 'Message sent confirmation displayed.');
+
+    // Login as recipient.
+    $this->drupalLogin($this->recipient1);
+    $this->drupalGet('messages');
+    $this->clickLink($this->firstMessage['subject']);
+  }
+
+  /**
+   * Go to the currently active thread and check if the user can see it.
+   */
+  function goToThread($user, $reply = NULL) {
+    $this->drupalLogin($user);
+    $this->drupalGet('messages');
+    $this->clickLink($this->firstMessage['subject']);
+    $this->assertText($this->firstMessage['body[value]'], t('Thread is displayed for user @username', array('@username' => $user->name)));
+
+    // Check additional message if passed.
+    if ($reply) {
+      $this->assertText($reply, t('Reply is displayed.'));
+    }
+  }
+}
+
