From 86da47880a60dbb6f2483f7080e40197cca2b1e4 Mon Sep 17 00:00:00 2001
From: James Kiesel <james.kiesel@gmail.com>
Date: Wed, 30 Dec 2015 12:17:45 +0100
Subject: [PATCH 1/4] Allow +1 via email

---
 lib/email/receiver.rb                  | 19 ++++++++++++-
 spec/components/email/receiver_spec.rb | 39 ++++++++++++++++++++++++++
 spec/fixtures/emails/like.eml          | 37 ++++++++++++++++++++++++
 spec/fixtures/emails/plus_one.eml      | 37 ++++++++++++++++++++++++
 spec/fixtures/emails/too_short.eml     |  2 +-
 5 files changed, 132 insertions(+), 2 deletions(-)
 create mode 100644 spec/fixtures/emails/like.eml
 create mode 100644 spec/fixtures/emails/plus_one.eml

diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb
index 2c0983701dd..9cf07bf908f 100644
--- a/lib/email/receiver.rb
+++ b/lib/email/receiver.rb
@@ -21,6 +21,7 @@ module Email
     class ReplyUserNotFoundError < ProcessingError; end
     class ReplyUserNotMatchingError < ProcessingError; end
     class InactiveUserError < ProcessingError; end
+    class InvalidPostAction < ProcessingError; end
 
     attr_reader :body, :email_log
 
@@ -103,7 +104,11 @@ module Email
         raise ReplyUserNotFoundError    if user.blank?
         raise ReplyUserNotMatchingError if @email_log.user_id != user.id
 
-        create_reply(@email_log)
+        if post_action_type = post_action_for(@body)
+          create_post_action(@email_log, post_action_type)
+        else
+          create_reply(@email_log)
+        end
       end
 
     rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError => e
@@ -239,6 +244,18 @@ module Email
       @body = "[quote=\"#{user_email}\"]\n#{@body}\n[/quote]"
     end
 
+    def create_post_action(email_log, type)
+      PostAction.act(email_log.user, email_log.post, type)
+    rescue PostAction::AlreadyActed => e
+      raise InvalidPostAction.new(e)
+    end
+
+    def post_action_for(body)
+      if ['+1', I18n.t('post_action_types.like.title').downcase].include? body.downcase
+        PostActionType.types[:like]
+      end
+    end
+
     def create_reply(email_log)
       create_post_with_attachments(email_log.user,
                                    raw: @body,
diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb
index baa3037c41c..d5c20be381f 100644
--- a/spec/components/email/receiver_spec.rb
+++ b/spec/components/email/receiver_spec.rb
@@ -340,6 +340,45 @@ This is a link http://example.com"
         expect(Upload.find_by(sha1: upload_sha)).not_to eq(nil)
       end
 
+      describe 'Liking via email' do
+        let!(:reply_key) { '636ca428858779856c226bb145ef4fad' }
+        let(:replied_user_like_params) { { user: replying_user, post: post, post_action_type_id: PostActionType.types[:like] } }
+        let(:replied_user_like) { PostAction.find_by(replied_user_like_params) }
+
+        describe "plus_one.eml" do
+          let!(:email_raw) {
+            fixture_file("emails/plus_one.eml")
+            .gsub("TO", "reply+#{reply_key}@appmail.adventuretime.ooo")
+            .gsub("FROM", replying_user_email)
+          }
+
+          it "adds a user like to the post" do
+            expect { receiver.process }.to change { PostAction.count }.by(1)
+            expect(replied_user_like).to be_present
+          end
+
+          it "does not create a duplicate like" do
+            PostAction.create(replied_user_like_params)
+            before_count = PostAction.count
+            expect { receiver.process }.to raise_error(Email::Receiver::InvalidPostAction)
+            expect(PostAction.count).to eq before_count
+            expect(replied_user_like).to be_present
+          end
+        end
+
+        describe "like.eml" do
+          let!(:email_raw) {
+            fixture_file("emails/like.eml")
+            .gsub("TO", "reply+#{reply_key}@appmail.adventuretime.ooo")
+            .gsub("FROM", replying_user_email)
+          }
+
+          it 'adds a user like to the post' do
+            expect { receiver.process }.to change { PostAction.count }.by(1)
+            expect(replied_user_like).to be_present
+          end
+        end
+      end
     end
 
     # === Failure Conditions ===
diff --git a/spec/fixtures/emails/like.eml b/spec/fixtures/emails/like.eml
new file mode 100644
index 00000000000..3bd9ae0fa7c
--- /dev/null
+++ b/spec/fixtures/emails/like.eml
@@ -0,0 +1,37 @@
+Return-Path: <FROM>
+Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
+Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
+Date: Thu, 13 Jun 2013 17:03:48 -0400
+From: FROM
+To: TO
+Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
+Mime-Version: 1.0
+Content-Type: text/plain;
+ charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+X-Sieve: CMU Sieve 2.2
+X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
+ 13 Jun 2013 14:03:48 -0700 (PDT)
+X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
+
+LIKE
+
+
+On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta
+<reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo> wrote:
+>
+>
+>
+> eviltrout posted in 'Adventure Time Sux' on Discourse Meta:
+>
+> ---
+> hey guys everyone knows adventure time sucks!
+>
+> ---
+> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3
+>
+> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences).
+>
diff --git a/spec/fixtures/emails/plus_one.eml b/spec/fixtures/emails/plus_one.eml
new file mode 100644
index 00000000000..a3255e9699c
--- /dev/null
+++ b/spec/fixtures/emails/plus_one.eml
@@ -0,0 +1,37 @@
+Return-Path: <FROM>
+Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
+Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
+Date: Thu, 13 Jun 2013 17:03:48 -0400
+From: FROM
+To: TO
+Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
+Mime-Version: 1.0
+Content-Type: text/plain;
+ charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+X-Sieve: CMU Sieve 2.2
+X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
+ 13 Jun 2013 14:03:48 -0700 (PDT)
+X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
+
++1
+
+
+On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta
+<reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo> wrote:
+>
+>
+>
+> eviltrout posted in 'Adventure Time Sux' on Discourse Meta:
+>
+> ---
+> hey guys everyone knows adventure time sucks!
+>
+> ---
+> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3
+>
+> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences).
+>
diff --git a/spec/fixtures/emails/too_short.eml b/spec/fixtures/emails/too_short.eml
index 54fed0f98c5..69f59769787 100644
--- a/spec/fixtures/emails/too_short.eml
+++ b/spec/fixtures/emails/too_short.eml
@@ -18,4 +18,4 @@ X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
 X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
 
 
-+1
\ No newline at end of file
+ok

From a559754db37e1255337c2aaec1404b31f044065b Mon Sep 17 00:00:00 2001
From: James Kiesel <james.kiesel@gmail.com>
Date: Wed, 30 Dec 2015 20:04:05 +0100
Subject: [PATCH 2/4] Add post action creator

---
 lib/email/receiver.rb      |  3 ++-
 lib/post_action_creator.rb | 20 ++++++++++++++++++++
 2 files changed, 22 insertions(+), 1 deletion(-)
 create mode 100644 lib/post_action_creator.rb

diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb
index 9cf07bf908f..a8881a31ec6 100644
--- a/lib/email/receiver.rb
+++ b/lib/email/receiver.rb
@@ -1,5 +1,6 @@
 require_dependency 'new_post_manager'
 require_dependency 'email/html_cleaner'
+require_dependency 'post_action_creator'
 
 module Email
 
@@ -245,7 +246,7 @@ module Email
     end
 
     def create_post_action(email_log, type)
-      PostAction.act(email_log.user, email_log.post, type)
+      PostActionCreator.new(email_log.user, email_log.post).perform(type)
     rescue PostAction::AlreadyActed => e
       raise InvalidPostAction.new(e)
     end
diff --git a/lib/post_action_creator.rb b/lib/post_action_creator.rb
new file mode 100644
index 00000000000..3d84b44d9f1
--- /dev/null
+++ b/lib/post_action_creator.rb
@@ -0,0 +1,20 @@
+# creates post actions based on a post and a user
+class PostActionCreator
+
+  def initialize(user, post)
+    @user = user
+    @post = post
+  end
+
+  def perform(action)
+    guardian.ensure_post_can_act!(@post, action, taken_actions: PostAction.counts_for([@post], @user)[@post.id])
+    PostAction.act(@user, @post, action)
+  end
+
+  private
+
+  def guardian
+    @guardian ||= Guardian.new(@user)
+  end
+
+end

From 6ceb1089468893b58497f44252e5245e7b31bfc5 Mon Sep 17 00:00:00 2001
From: James Kiesel <james.kiesel@gmail.com>
Date: Wed, 30 Dec 2015 20:52:36 +0100
Subject: [PATCH 3/4] Add specs for post action guardian

---
 lib/email/receiver.rb                       |  2 +-
 lib/post_action_creator.rb                  |  2 +-
 spec/components/email/receiver_spec.rb      |  8 ++++++++
 spec/components/post_action_creator_spec.rb | 22 +++++++++++++++++++++
 4 files changed, 32 insertions(+), 2 deletions(-)
 create mode 100644 spec/components/post_action_creator_spec.rb

diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb
index a8881a31ec6..bb2ad455ac0 100644
--- a/lib/email/receiver.rb
+++ b/lib/email/receiver.rb
@@ -247,7 +247,7 @@ module Email
 
     def create_post_action(email_log, type)
       PostActionCreator.new(email_log.user, email_log.post).perform(type)
-    rescue PostAction::AlreadyActed => e
+    rescue Discourse::InvalidAccess, PostAction::AlreadyActed => e
       raise InvalidPostAction.new(e)
     end
 
diff --git a/lib/post_action_creator.rb b/lib/post_action_creator.rb
index 3d84b44d9f1..e995db1a298 100644
--- a/lib/post_action_creator.rb
+++ b/lib/post_action_creator.rb
@@ -7,7 +7,7 @@ class PostActionCreator
   end
 
   def perform(action)
-    guardian.ensure_post_can_act!(@post, action, taken_actions: PostAction.counts_for([@post], @user)[@post.id])
+    guardian.ensure_post_can_act!(@post, action, taken_actions: PostAction.counts_for([@post].compact, @user)[@post.try(:id)])
     PostAction.act(@user, @post, action)
   end
 
diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb
index d5c20be381f..796ce440d4d 100644
--- a/spec/components/email/receiver_spec.rb
+++ b/spec/components/email/receiver_spec.rb
@@ -364,6 +364,14 @@ This is a link http://example.com"
             expect(PostAction.count).to eq before_count
             expect(replied_user_like).to be_present
           end
+
+          it "does not allow unauthorized happiness" do
+            post.trash!
+            before_count = PostAction.count
+            expect { receiver.process }.to raise_error(Email::Receiver::InvalidPostAction)
+            expect(PostAction.count).to eq before_count
+            expect(replied_user_like).to_not be_present
+          end
         end
 
         describe "like.eml" do
diff --git a/spec/components/post_action_creator_spec.rb b/spec/components/post_action_creator_spec.rb
new file mode 100644
index 00000000000..0348aec6b82
--- /dev/null
+++ b/spec/components/post_action_creator_spec.rb
@@ -0,0 +1,22 @@
+require 'rails_helper'
+require 'post_action_creator'
+
+describe PostCreator do
+  let(:user) { Fabricate(:user) }
+  let(:post) { Fabricate(:post) }
+  let(:group) { Fabricate(:group) }
+  let(:like_type_id) { PostActionType.types[:like] }
+
+
+  describe 'perform' do
+    it 'creates a post action' do
+      expect { PostActionCreator.new(user, post).perform(like_type_id) }.to change { PostAction.count }.by(1)
+      expect(PostAction.find_by(user: user, post: post, post_action_type_id: like_type_id)).to be_present
+    end
+
+    it 'does not create an invalid post action' do
+      expect { PostActionCreator.new(user, nil).perform(like_type_id) }.to raise_error(Discourse::InvalidAccess)
+    end
+  end
+
+end

From b94c53c71cf422d32cdf8dc5166222137a15a0b7 Mon Sep 17 00:00:00 2001
From: James Kiesel <james.kiesel@gmail.com>
Date: Wed, 30 Dec 2015 20:54:51 +0100
Subject: [PATCH 4/4] cleanup post action creator

---
 spec/components/post_action_creator_spec.rb | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/spec/components/post_action_creator_spec.rb b/spec/components/post_action_creator_spec.rb
index 0348aec6b82..3b577fa4043 100644
--- a/spec/components/post_action_creator_spec.rb
+++ b/spec/components/post_action_creator_spec.rb
@@ -1,10 +1,9 @@
 require 'rails_helper'
 require 'post_action_creator'
 
-describe PostCreator do
+describe PostActionCreator do
   let(:user) { Fabricate(:user) }
   let(:post) { Fabricate(:post) }
-  let(:group) { Fabricate(:group) }
   let(:like_type_id) { PostActionType.types[:like] }