##// END OF EJS Templates
Accept email from anonymous users with an empty from address. #5604...
Eric Davis -
r3669:d48eb2de47c3
parent child
Show More
@@ -0,0 +1,17
1 Return-Path: <john.doe@somenet.foo>
2 Received: from osiris ([127.0.0.1])
3 by OSIRIS
4 with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
5 Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
6 To: <redmine@somenet.foo>
7 Subject: Ticket by unknown user
8 Date: Sun, 22 Jun 2008 12:28:07 +0200
9 MIME-Version: 1.0
10 Content-Type: text/plain;
11 format=flowed;
12 charset="iso-8859-1";
13 reply-type=original
14 Content-Transfer-Encoding: 7bit
15
16 This is a ticket submitted by an unknown user.
17
@@ -1,336 +1,336
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class MailHandler < ActionMailer::Base
19 19 include ActionView::Helpers::SanitizeHelper
20 20
21 21 class UnauthorizedAction < StandardError; end
22 22 class MissingInformation < StandardError; end
23 23
24 24 attr_reader :email, :user
25 25
26 26 def self.receive(email, options={})
27 27 @@handler_options = options.dup
28 28
29 29 @@handler_options[:issue] ||= {}
30 30
31 31 @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip) if @@handler_options[:allow_override].is_a?(String)
32 32 @@handler_options[:allow_override] ||= []
33 33 # Project needs to be overridable if not specified
34 34 @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
35 35 # Status overridable by default
36 36 @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
37 37
38 38 @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1' ? true : false)
39 39 super email
40 40 end
41 41
42 42 # Processes incoming emails
43 43 # Returns the created object (eg. an issue, a message) or false
44 44 def receive(email)
45 45 @email = email
46 46 sender_email = email.from.to_a.first.to_s.strip
47 47 # Ignore emails received from the application emission address to avoid hell cycles
48 48 if sender_email.downcase == Setting.mail_from.to_s.strip.downcase
49 49 logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]" if logger && logger.info
50 50 return false
51 51 end
52 @user = User.find_by_mail(sender_email)
52 @user = User.find_by_mail(sender_email) if sender_email.present?
53 53 if @user && !@user.active?
54 54 logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]" if logger && logger.info
55 55 return false
56 56 end
57 57 if @user.nil?
58 58 # Email was submitted by an unknown user
59 59 case @@handler_options[:unknown_user]
60 60 when 'accept'
61 61 @user = User.anonymous
62 62 when 'create'
63 63 @user = MailHandler.create_user_from_email(email)
64 64 if @user
65 65 logger.info "MailHandler: [#{@user.login}] account created" if logger && logger.info
66 66 Mailer.deliver_account_information(@user, @user.password)
67 67 else
68 68 logger.error "MailHandler: could not create account for [#{sender_email}]" if logger && logger.error
69 69 return false
70 70 end
71 71 else
72 72 # Default behaviour, emails from unknown users are ignored
73 73 logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]" if logger && logger.info
74 74 return false
75 75 end
76 76 end
77 77 User.current = @user
78 78 dispatch
79 79 end
80 80
81 81 private
82 82
83 83 MESSAGE_ID_RE = %r{^<redmine\.([a-z0-9_]+)\-(\d+)\.\d+@}
84 84 ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]}
85 85 MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]}
86 86
87 87 def dispatch
88 88 headers = [email.in_reply_to, email.references].flatten.compact
89 89 if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
90 90 klass, object_id = $1, $2.to_i
91 91 method_name = "receive_#{klass}_reply"
92 92 if self.class.private_instance_methods.collect(&:to_s).include?(method_name)
93 93 send method_name, object_id
94 94 else
95 95 # ignoring it
96 96 end
97 97 elsif m = email.subject.match(ISSUE_REPLY_SUBJECT_RE)
98 98 receive_issue_reply(m[1].to_i)
99 99 elsif m = email.subject.match(MESSAGE_REPLY_SUBJECT_RE)
100 100 receive_message_reply(m[1].to_i)
101 101 else
102 102 receive_issue
103 103 end
104 104 rescue ActiveRecord::RecordInvalid => e
105 105 # TODO: send a email to the user
106 106 logger.error e.message if logger
107 107 false
108 108 rescue MissingInformation => e
109 109 logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
110 110 false
111 111 rescue UnauthorizedAction => e
112 112 logger.error "MailHandler: unauthorized attempt from #{user}" if logger
113 113 false
114 114 end
115 115
116 116 # Creates a new issue
117 117 def receive_issue
118 118 project = target_project
119 119 tracker = (get_keyword(:tracker) && project.trackers.find_by_name(get_keyword(:tracker))) || project.trackers.find(:first)
120 120 category = (get_keyword(:category) && project.issue_categories.find_by_name(get_keyword(:category)))
121 121 priority = (get_keyword(:priority) && IssuePriority.find_by_name(get_keyword(:priority)))
122 122 status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
123 123 assigned_to = (get_keyword(:assigned_to, :override => true) && find_user_from_keyword(get_keyword(:assigned_to, :override => true)))
124 124 due_date = get_keyword(:due_date, :override => true)
125 125 start_date = get_keyword(:start_date, :override => true)
126 126
127 127 # check permission
128 128 unless @@handler_options[:no_permission_check]
129 129 raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
130 130 end
131 131
132 132 issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority, :due_date => due_date, :start_date => start_date, :assigned_to => assigned_to)
133 133 # check workflow
134 134 if status && issue.new_statuses_allowed_to(user).include?(status)
135 135 issue.status = status
136 136 end
137 137 issue.subject = email.subject.chomp
138 138 if issue.subject.blank?
139 139 issue.subject = '(no subject)'
140 140 end
141 141 # custom fields
142 142 issue.custom_field_values = issue.available_custom_fields.inject({}) do |h, c|
143 143 if value = get_keyword(c.name, :override => true)
144 144 h[c.id] = value
145 145 end
146 146 h
147 147 end
148 148 issue.description = cleaned_up_text_body
149 149 # add To and Cc as watchers before saving so the watchers can reply to Redmine
150 150 add_watchers(issue)
151 151 issue.save!
152 152 add_attachments(issue)
153 153 logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
154 154 issue
155 155 end
156 156
157 157 def target_project
158 158 # TODO: other ways to specify project:
159 159 # * parse the email To field
160 160 # * specific project (eg. Setting.mail_handler_target_project)
161 161 target = Project.find_by_identifier(get_keyword(:project))
162 162 raise MissingInformation.new('Unable to determine target project') if target.nil?
163 163 target
164 164 end
165 165
166 166 # Adds a note to an existing issue
167 167 def receive_issue_reply(issue_id)
168 168 status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
169 169 due_date = get_keyword(:due_date, :override => true)
170 170 start_date = get_keyword(:start_date, :override => true)
171 171 assigned_to = (get_keyword(:assigned_to, :override => true) && find_user_from_keyword(get_keyword(:assigned_to, :override => true)))
172 172
173 173 issue = Issue.find_by_id(issue_id)
174 174 return unless issue
175 175 # check permission
176 176 unless @@handler_options[:no_permission_check]
177 177 raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project)
178 178 raise UnauthorizedAction unless status.nil? || user.allowed_to?(:edit_issues, issue.project)
179 179 end
180 180
181 181 # add the note
182 182 journal = issue.init_journal(user, cleaned_up_text_body)
183 183 add_attachments(issue)
184 184 # check workflow
185 185 if status && issue.new_statuses_allowed_to(user).include?(status)
186 186 issue.status = status
187 187 end
188 188 issue.start_date = start_date if start_date
189 189 issue.due_date = due_date if due_date
190 190 issue.assigned_to = assigned_to if assigned_to
191 191
192 192 issue.save!
193 193 logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
194 194 journal
195 195 end
196 196
197 197 # Reply will be added to the issue
198 198 def receive_journal_reply(journal_id)
199 199 journal = Journal.find_by_id(journal_id)
200 200 if journal && journal.journalized_type == 'Issue'
201 201 receive_issue_reply(journal.journalized_id)
202 202 end
203 203 end
204 204
205 205 # Receives a reply to a forum message
206 206 def receive_message_reply(message_id)
207 207 message = Message.find_by_id(message_id)
208 208 if message
209 209 message = message.root
210 210
211 211 unless @@handler_options[:no_permission_check]
212 212 raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project)
213 213 end
214 214
215 215 if !message.locked?
216 216 reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip,
217 217 :content => cleaned_up_text_body)
218 218 reply.author = user
219 219 reply.board = message.board
220 220 message.children << reply
221 221 add_attachments(reply)
222 222 reply
223 223 else
224 224 logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic" if logger && logger.info
225 225 end
226 226 end
227 227 end
228 228
229 229 def add_attachments(obj)
230 230 if email.has_attachments?
231 231 email.attachments.each do |attachment|
232 232 Attachment.create(:container => obj,
233 233 :file => attachment,
234 234 :author => user,
235 235 :content_type => attachment.content_type)
236 236 end
237 237 end
238 238 end
239 239
240 240 # Adds To and Cc as watchers of the given object if the sender has the
241 241 # appropriate permission
242 242 def add_watchers(obj)
243 243 if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project)
244 244 addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase}
245 245 unless addresses.empty?
246 246 watchers = User.active.find(:all, :conditions => ['LOWER(mail) IN (?)', addresses])
247 247 watchers.each {|w| obj.add_watcher(w)}
248 248 end
249 249 end
250 250 end
251 251
252 252 def get_keyword(attr, options={})
253 253 @keywords ||= {}
254 254 if @keywords.has_key?(attr)
255 255 @keywords[attr]
256 256 else
257 257 @keywords[attr] = begin
258 258 if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && plain_text_body.gsub!(/^#{attr.to_s.humanize}[ \t]*:[ \t]*(.+)\s*$/i, '')
259 259 $1.strip
260 260 elsif !@@handler_options[:issue][attr].blank?
261 261 @@handler_options[:issue][attr]
262 262 end
263 263 end
264 264 end
265 265 end
266 266
267 267 # Returns the text/plain part of the email
268 268 # If not found (eg. HTML-only email), returns the body with tags removed
269 269 def plain_text_body
270 270 return @plain_text_body unless @plain_text_body.nil?
271 271 parts = @email.parts.collect {|c| (c.respond_to?(:parts) && !c.parts.empty?) ? c.parts : c}.flatten
272 272 if parts.empty?
273 273 parts << @email
274 274 end
275 275 plain_text_part = parts.detect {|p| p.content_type == 'text/plain'}
276 276 if plain_text_part.nil?
277 277 # no text/plain part found, assuming html-only email
278 278 # strip html tags and remove doctype directive
279 279 @plain_text_body = strip_tags(@email.body.to_s)
280 280 @plain_text_body.gsub! %r{^<!DOCTYPE .*$}, ''
281 281 else
282 282 @plain_text_body = plain_text_part.body.to_s
283 283 end
284 284 @plain_text_body.strip!
285 285 @plain_text_body
286 286 end
287 287
288 288 def cleaned_up_text_body
289 289 cleanup_body(plain_text_body)
290 290 end
291 291
292 292 def self.full_sanitizer
293 293 @full_sanitizer ||= HTML::FullSanitizer.new
294 294 end
295 295
296 296 # Creates a user account for the +email+ sender
297 297 def self.create_user_from_email(email)
298 298 addr = email.from_addrs.to_a.first
299 299 if addr && !addr.spec.blank?
300 300 user = User.new
301 301 user.mail = addr.spec
302 302
303 303 names = addr.name.blank? ? addr.spec.gsub(/@.*$/, '').split('.') : addr.name.split
304 304 user.firstname = names.shift
305 305 user.lastname = names.join(' ')
306 306 user.lastname = '-' if user.lastname.blank?
307 307
308 308 user.login = user.mail
309 309 user.password = ActiveSupport::SecureRandom.hex(5)
310 310 user.language = Setting.default_language
311 311 user.save ? user : nil
312 312 end
313 313 end
314 314
315 315 private
316 316
317 317 # Removes the email body of text after the truncation configurations.
318 318 def cleanup_body(body)
319 319 delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
320 320 unless delimiters.empty?
321 321 regex = Regexp.new("^(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
322 322 body = body.gsub(regex, '')
323 323 end
324 324 body.strip
325 325 end
326 326
327 327 def find_user_from_keyword(keyword)
328 328 user ||= User.find_by_mail(keyword)
329 329 user ||= User.find_by_login(keyword)
330 330 if user.nil? && keyword.match(/ /)
331 331 firstname, lastname = *(keyword.split) # "First Last Throwaway"
332 332 user ||= User.find_by_firstname_and_lastname(firstname, lastname)
333 333 end
334 334 user
335 335 end
336 336 end
@@ -1,356 +1,365
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2009 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 require File.dirname(__FILE__) + '/../test_helper'
21 21
22 22 class MailHandlerTest < ActiveSupport::TestCase
23 23 fixtures :users, :projects,
24 24 :enabled_modules,
25 25 :roles,
26 26 :members,
27 27 :member_roles,
28 28 :issues,
29 29 :issue_statuses,
30 30 :workflows,
31 31 :trackers,
32 32 :projects_trackers,
33 33 :enumerations,
34 34 :issue_categories,
35 35 :custom_fields,
36 36 :custom_fields_trackers,
37 37 :boards,
38 38 :messages
39 39
40 40 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
41 41
42 42 def setup
43 43 ActionMailer::Base.deliveries.clear
44 44 end
45 45
46 46 def test_add_issue
47 47 ActionMailer::Base.deliveries.clear
48 48 # This email contains: 'Project: onlinestore'
49 49 issue = submit_email('ticket_on_given_project.eml')
50 50 assert issue.is_a?(Issue)
51 51 assert !issue.new_record?
52 52 issue.reload
53 53 assert_equal 'New ticket on a given project', issue.subject
54 54 assert_equal User.find_by_login('jsmith'), issue.author
55 55 assert_equal Project.find(2), issue.project
56 56 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
57 57 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
58 58 assert_equal '2010-01-01', issue.start_date.to_s
59 59 assert_equal '2010-12-31', issue.due_date.to_s
60 60 assert_equal User.find_by_login('jsmith'), issue.assigned_to
61 61 # keywords should be removed from the email body
62 62 assert !issue.description.match(/^Project:/i)
63 63 assert !issue.description.match(/^Status:/i)
64 64 # Email notification should be sent
65 65 mail = ActionMailer::Base.deliveries.last
66 66 assert_not_nil mail
67 67 assert mail.subject.include?('New ticket on a given project')
68 68 end
69 69
70 70 def test_add_issue_with_status
71 71 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
72 72 issue = submit_email('ticket_on_given_project.eml')
73 73 assert issue.is_a?(Issue)
74 74 assert !issue.new_record?
75 75 issue.reload
76 76 assert_equal Project.find(2), issue.project
77 77 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
78 78 end
79 79
80 80 def test_add_issue_with_attributes_override
81 81 issue = submit_email('ticket_with_attributes.eml', :allow_override => 'tracker,category,priority')
82 82 assert issue.is_a?(Issue)
83 83 assert !issue.new_record?
84 84 issue.reload
85 85 assert_equal 'New ticket on a given project', issue.subject
86 86 assert_equal User.find_by_login('jsmith'), issue.author
87 87 assert_equal Project.find(2), issue.project
88 88 assert_equal 'Feature request', issue.tracker.to_s
89 89 assert_equal 'Stock management', issue.category.to_s
90 90 assert_equal 'Urgent', issue.priority.to_s
91 91 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
92 92 end
93 93
94 94 def test_add_issue_with_partial_attributes_override
95 95 issue = submit_email('ticket_with_attributes.eml', :issue => {:priority => 'High'}, :allow_override => ['tracker'])
96 96 assert issue.is_a?(Issue)
97 97 assert !issue.new_record?
98 98 issue.reload
99 99 assert_equal 'New ticket on a given project', issue.subject
100 100 assert_equal User.find_by_login('jsmith'), issue.author
101 101 assert_equal Project.find(2), issue.project
102 102 assert_equal 'Feature request', issue.tracker.to_s
103 103 assert_nil issue.category
104 104 assert_equal 'High', issue.priority.to_s
105 105 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
106 106 end
107 107
108 108 def test_add_issue_with_spaces_between_attribute_and_separator
109 109 issue = submit_email('ticket_with_spaces_between_attribute_and_separator.eml', :allow_override => 'tracker,category,priority')
110 110 assert issue.is_a?(Issue)
111 111 assert !issue.new_record?
112 112 issue.reload
113 113 assert_equal 'New ticket on a given project', issue.subject
114 114 assert_equal User.find_by_login('jsmith'), issue.author
115 115 assert_equal Project.find(2), issue.project
116 116 assert_equal 'Feature request', issue.tracker.to_s
117 117 assert_equal 'Stock management', issue.category.to_s
118 118 assert_equal 'Urgent', issue.priority.to_s
119 119 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
120 120 end
121 121
122 122
123 123 def test_add_issue_with_attachment_to_specific_project
124 124 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
125 125 assert issue.is_a?(Issue)
126 126 assert !issue.new_record?
127 127 issue.reload
128 128 assert_equal 'Ticket created by email with attachment', issue.subject
129 129 assert_equal User.find_by_login('jsmith'), issue.author
130 130 assert_equal Project.find(2), issue.project
131 131 assert_equal 'This is a new ticket with attachments', issue.description
132 132 # Attachment properties
133 133 assert_equal 1, issue.attachments.size
134 134 assert_equal 'Paella.jpg', issue.attachments.first.filename
135 135 assert_equal 'image/jpeg', issue.attachments.first.content_type
136 136 assert_equal 10790, issue.attachments.first.filesize
137 137 end
138 138
139 139 def test_add_issue_with_custom_fields
140 140 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
141 141 assert issue.is_a?(Issue)
142 142 assert !issue.new_record?
143 143 issue.reload
144 144 assert_equal 'New ticket with custom field values', issue.subject
145 145 assert_equal 'Value for a custom field', issue.custom_value_for(CustomField.find_by_name('Searchable field')).value
146 146 assert !issue.description.match(/^searchable field:/i)
147 147 end
148 148
149 149 def test_add_issue_with_cc
150 150 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
151 151 assert issue.is_a?(Issue)
152 152 assert !issue.new_record?
153 153 issue.reload
154 154 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
155 155 assert_equal 1, issue.watchers.size
156 156 end
157 157
158 158 def test_add_issue_by_unknown_user
159 159 assert_no_difference 'User.count' do
160 160 assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'})
161 161 end
162 162 end
163 163
164 164 def test_add_issue_by_anonymous_user
165 165 Role.anonymous.add_permission!(:add_issues)
166 166 assert_no_difference 'User.count' do
167 167 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'accept')
168 168 assert issue.is_a?(Issue)
169 169 assert issue.author.anonymous?
170 170 end
171 171 end
172 172
173 def test_add_issue_by_anonymous_user_with_no_from_address
174 Role.anonymous.add_permission!(:add_issues)
175 assert_no_difference 'User.count' do
176 issue = submit_email('ticket_by_empty_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'accept')
177 assert issue.is_a?(Issue)
178 assert issue.author.anonymous?
179 end
180 end
181
173 182 def test_add_issue_by_anonymous_user_on_private_project
174 183 Role.anonymous.add_permission!(:add_issues)
175 184 assert_no_difference 'User.count' do
176 185 assert_no_difference 'Issue.count' do
177 186 assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'onlinestore'}, :unknown_user => 'accept')
178 187 end
179 188 end
180 189 end
181 190
182 191 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
183 192 assert_no_difference 'User.count' do
184 193 assert_difference 'Issue.count' do
185 194 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'onlinestore'}, :no_permission_check => '1', :unknown_user => 'accept')
186 195 assert issue.is_a?(Issue)
187 196 assert issue.author.anonymous?
188 197 assert !issue.project.is_public?
189 198 end
190 199 end
191 200 end
192 201
193 202 def test_add_issue_by_created_user
194 203 Setting.default_language = 'en'
195 204 assert_difference 'User.count' do
196 205 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create')
197 206 assert issue.is_a?(Issue)
198 207 assert issue.author.active?
199 208 assert_equal 'john.doe@somenet.foo', issue.author.mail
200 209 assert_equal 'John', issue.author.firstname
201 210 assert_equal 'Doe', issue.author.lastname
202 211
203 212 # account information
204 213 email = ActionMailer::Base.deliveries.first
205 214 assert_not_nil email
206 215 assert email.subject.include?('account activation')
207 216 login = email.body.match(/\* Login: (.*)$/)[1]
208 217 password = email.body.match(/\* Password: (.*)$/)[1]
209 218 assert_equal issue.author, User.try_to_login(login, password)
210 219 end
211 220 end
212 221
213 222 def test_add_issue_without_from_header
214 223 Role.anonymous.add_permission!(:add_issues)
215 224 assert_equal false, submit_email('ticket_without_from_header.eml')
216 225 end
217 226
218 227 def test_add_issue_with_japanese_keywords
219 228 tracker = Tracker.create!(:name => 'ι–‹η™Ί')
220 229 Project.find(1).trackers << tracker
221 230 issue = submit_email('japanese_keywords_iso_2022_jp.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'tracker')
222 231 assert_kind_of Issue, issue
223 232 assert_equal tracker, issue.tracker
224 233 end
225 234
226 235 def test_should_ignore_emails_from_emission_address
227 236 Role.anonymous.add_permission!(:add_issues)
228 237 assert_no_difference 'User.count' do
229 238 assert_equal false, submit_email('ticket_from_emission_address.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create')
230 239 end
231 240 end
232 241
233 242 def test_add_issue_should_send_email_notification
234 243 ActionMailer::Base.deliveries.clear
235 244 # This email contains: 'Project: onlinestore'
236 245 issue = submit_email('ticket_on_given_project.eml')
237 246 assert issue.is_a?(Issue)
238 247 assert_equal 1, ActionMailer::Base.deliveries.size
239 248 end
240 249
241 250 def test_add_issue_note
242 251 journal = submit_email('ticket_reply.eml')
243 252 assert journal.is_a?(Journal)
244 253 assert_equal User.find_by_login('jsmith'), journal.user
245 254 assert_equal Issue.find(2), journal.journalized
246 255 assert_match /This is reply/, journal.notes
247 256 end
248 257
249 258 def test_add_issue_note_with_attribute_changes
250 259 # This email contains: 'Status: Resolved'
251 260 journal = submit_email('ticket_reply_with_status.eml')
252 261 assert journal.is_a?(Journal)
253 262 issue = Issue.find(journal.issue.id)
254 263 assert_equal User.find_by_login('jsmith'), journal.user
255 264 assert_equal Issue.find(2), journal.journalized
256 265 assert_match /This is reply/, journal.notes
257 266 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
258 267 assert_equal '2010-01-01', issue.start_date.to_s
259 268 assert_equal '2010-12-31', issue.due_date.to_s
260 269 assert_equal User.find_by_login('jsmith'), issue.assigned_to
261 270 end
262 271
263 272 def test_add_issue_note_should_send_email_notification
264 273 ActionMailer::Base.deliveries.clear
265 274 journal = submit_email('ticket_reply.eml')
266 275 assert journal.is_a?(Journal)
267 276 assert_equal 1, ActionMailer::Base.deliveries.size
268 277 end
269 278
270 279 def test_reply_to_a_message
271 280 m = submit_email('message_reply.eml')
272 281 assert m.is_a?(Message)
273 282 assert !m.new_record?
274 283 m.reload
275 284 assert_equal 'Reply via email', m.subject
276 285 # The email replies to message #2 which is part of the thread of message #1
277 286 assert_equal Message.find(1), m.parent
278 287 end
279 288
280 289 def test_reply_to_a_message_by_subject
281 290 m = submit_email('message_reply_by_subject.eml')
282 291 assert m.is_a?(Message)
283 292 assert !m.new_record?
284 293 m.reload
285 294 assert_equal 'Reply to the first post', m.subject
286 295 assert_equal Message.find(1), m.parent
287 296 end
288 297
289 298 def test_should_strip_tags_of_html_only_emails
290 299 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
291 300 assert issue.is_a?(Issue)
292 301 assert !issue.new_record?
293 302 issue.reload
294 303 assert_equal 'HTML email', issue.subject
295 304 assert_equal 'This is a html-only email.', issue.description
296 305 end
297 306
298 307 context "truncate emails based on the Setting" do
299 308 context "with no setting" do
300 309 setup do
301 310 Setting.mail_handler_body_delimiters = ''
302 311 end
303 312
304 313 should "add the entire email into the issue" do
305 314 issue = submit_email('ticket_on_given_project.eml')
306 315 assert_issue_created(issue)
307 316 assert issue.description.include?('---')
308 317 assert issue.description.include?('This paragraph is after the delimiter')
309 318 end
310 319 end
311 320
312 321 context "with a single string" do
313 322 setup do
314 323 Setting.mail_handler_body_delimiters = '---'
315 324 end
316 325
317 326 should "truncate the email at the delimiter for the issue" do
318 327 issue = submit_email('ticket_on_given_project.eml')
319 328 assert_issue_created(issue)
320 329 assert issue.description.include?('This paragraph is before delimiters')
321 330 assert issue.description.include?('--- This line starts with a delimiter')
322 331 assert !issue.description.match(/^---$/)
323 332 assert !issue.description.include?('This paragraph is after the delimiter')
324 333 end
325 334 end
326 335
327 336 context "with multiple strings" do
328 337 setup do
329 338 Setting.mail_handler_body_delimiters = "---\nBREAK"
330 339 end
331 340
332 341 should "truncate the email at the first delimiter found (BREAK)" do
333 342 issue = submit_email('ticket_on_given_project.eml')
334 343 assert_issue_created(issue)
335 344 assert issue.description.include?('This paragraph is before delimiters')
336 345 assert !issue.description.include?('BREAK')
337 346 assert !issue.description.include?('This paragraph is between delimiters')
338 347 assert !issue.description.match(/^---$/)
339 348 assert !issue.description.include?('This paragraph is after the delimiter')
340 349 end
341 350 end
342 351 end
343 352
344 353 private
345 354
346 355 def submit_email(filename, options={})
347 356 raw = IO.read(File.join(FIXTURES_PATH, filename))
348 357 MailHandler.receive(raw, options)
349 358 end
350 359
351 360 def assert_issue_created(issue)
352 361 assert issue.is_a?(Issue)
353 362 assert !issue.new_record?
354 363 issue.reload
355 364 end
356 365 end
General Comments 0
You need to be logged in to leave comments. Login now