From 5f76287b184022ead3a25b636e4669b31ec5d1cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9gis=20Hanol?= <regis@hanol.fr>
Date: Mon, 16 May 2016 21:45:34 +0200
Subject: [PATCH] FEATURE: cap number of staged users (defaults to 10) created
 per incoming email

---
 config/locales/server.en.yml           | 2 ++
 config/site_settings.yml               | 1 +
 lib/email/receiver.rb                  | 7 +++++++
 spec/components/email/receiver_spec.rb | 6 ++++++
 4 files changed, 16 insertions(+)

diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index c400b5b19..43fb97d36 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -51,6 +51,7 @@ en:
     incoming:
       default_subject: "Incoming email from %{email}"
       show_trimmed_content: "Show trimmed content"
+      maximum_staged_user_per_email_reached: "Reached maximum number of staged users created per email."
       errors:
         empty_email_error: "Happens when the raw mail we received was blank."
         no_message_id_error: "Happens when the mail has no 'Message-Id' header."
@@ -1173,6 +1174,7 @@ en:
     delete_email_logs_after_days: "Delete email logs after (N) days. 0 to keep indefinitely"
     max_emails_per_day_per_user: "Maximum number of emails to send users per day. 0 to disable the limit"
     enable_staged_users: "Automatically create staged users when processing incoming emails."
+    maximum_staged_users_per_email: "Maximum number of staged users created when processing an incoming email."
     auto_generated_whitelist: "List of email addresses that won't be checked for auto-generated content."
     block_auto_generated_emails: "Block incoming emails identified as being auto generated."
     bounce_score_threshold: "Max score before we will stop emailing a user. Soft bounce adds 1, hard bounce adds 2, score reset 30 days after last bounce."
diff --git a/config/site_settings.yml b/config/site_settings.yml
index 9a4cbf07e..6a73beaa8 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -567,6 +567,7 @@ email:
     min: 0
   max_emails_per_day_per_user: 100
   enable_staged_users: true
+  maximum_staged_users_per_email: 10
   auto_generated_whitelist:
     default: ''
     type: list
diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb
index 135fe0be0..4debd83f3 100644
--- a/lib/email/receiver.rb
+++ b/lib/email/receiver.rb
@@ -31,6 +31,7 @@ module Email
 
     def initialize(mail_string)
       raise EmptyEmailError if mail_string.blank?
+      @staged_users_created = 0
       @raw_email = mail_string
       @mail = Mail.new(@raw_email)
       @message_id = @mail.message_id.presence || Digest::MD5.hexdigest(mail_string)
@@ -283,6 +284,7 @@ module Email
               name: display_name.presence || User.suggest_name(email),
               staged: true
             )
+            @staged_users_created += 1
           end
         rescue
           user = nil
@@ -477,6 +479,11 @@ module Email
                   topic.topic_allowed_users.create!(user_id: user.id)
                   topic.add_small_action(sender, "invited_user", user.username)
                 end
+                # cap number of staged users created per email
+                if @staged_users_created > SiteSetting.maximum_staged_users_per_email
+                  topic.add_moderator_post(sender, I18n.t("emails.incoming.maximum_staged_user_per_email_reached"))
+                  return
+                end
               end
             rescue ActiveRecord::RecordInvalid
               # don't care if user already allowed
diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb
index f8497dc28..0cff20991 100644
--- a/spec/components/email/receiver_spec.rb
+++ b/spec/components/email/receiver_spec.rb
@@ -325,6 +325,12 @@ describe Email::Receiver do
       expect(emails).to include("someone@else.com", "discourse@bar.com", "wat@bar.com")
     end
 
+    it "cap the number of staged users created per email" do
+      SiteSetting.maximum_staged_users_per_email = 1
+      expect { process(:cc) }.to change(Topic, :count)
+      expect(Topic.last.ordered_posts[-1].post_type).to eq(Post.types[:moderator_action])
+    end
+
     it "associates email replies using both 'In-Reply-To' and 'References' headers" do
       expect { process(:email_reply_1) }.to change(Topic, :count)