--- mail_handler.rb.orig	2023-02-15 23:25:49.023531264 +0100
+++ mail_handler.rb	2023-02-16 22:06:33.017071239 +0100
@@ -67,7 +67,7 @@
     %w(project status tracker category priority assigned_to fixed_version).each do |option|
       options[:issue][option.to_sym] = env[option] if env[option]
     end
-    %w(allow_override unknown_user no_permission_check no_account_notice no_notification default_group project_from_subaddress).each do |option|
+    %w(allow_override unknown_user no_permission_check no_account_notice no_notification default_group project_from_subaddress ext_ref reprefix).each do |option|
       options[option.to_sym] = env[option] if env[option]
     end
     if env['private']
@@ -178,6 +178,40 @@
   end
 
   def dispatch_to_default
+    extref_name = handler_options[:ext_ref]
+    extref = get_keyword(extref_name, {}, false) if extref_name
+    if extref_name && extref
+      Rails.logger.warn "MailHandlerExt: found ext ref #{extref_name} = #{extref}"
+      begin
+        query = IssueQuery.new(:name => '_',
+                               :filters => { 'status_id' => {:operator => "o", :values => [""]}})
+        cf = query.issue_custom_fields.visible.where(:is_filter => true).where(:name => extref_name).first
+        if cf
+          query.add_filter "cf_#{cf.id}", '=', [extref]
+        end
+        issues=query.issues
+        issue_id = nil
+        if issues.count > 10
+          Rails.logger.error "MailHandler: found #{issues.count} open issues with #{extref_name}: #{extref}. Too much. Ignoring external reference."
+        elsif issues.count > 1
+          Rails.logger.warn "MailHandler: found #{issues.count} open issues with #{extref_name}: #{extref}:"
+          Rails.logger.warn "MailHandler: issues: #{issues.map{ |i| i.id.to_s }.join(", ")}"
+          issue_id = issues.first.id
+        elsif issues.count == 1
+          issue_id = issues.first.id
+        else
+          Rails.logger.warn "MailHandler: no open issues found with #{extref_name}: #{extref}:"
+        end
+        #Rails.logger.error "MailHandler: query #{query}"
+        #Rails.logger.error "MailHandler: query.statement #{query.statement}"
+        if !issue_id.nil?
+          Rails.logger.warn "MailHandler: ext ref maps mail to issue #{issue_id}"
+          return receive_issue_reply(issue_id)
+        end
+      rescue Exception => e
+        Rails.logger.error "MailHandlerExt: error quer ext ref #{extref}: #{e.message}"
+      end
+    end
     receive_issue
   end
 
@@ -208,6 +242,7 @@
       issue.subject = "(#{ll(Setting.default_language, :text_no_subject)})"
     end
     issue.description = cleaned_up_text_body
+    issue.description << "\n\n-- \n" << headers_as_text
     issue.start_date ||= User.current.today if Setting.default_issue_start_date_to_creation_date?
     if handler_options[:issue][:is_private] == '1'
       issue.is_private = true
@@ -221,6 +256,29 @@
     issue
   end
 
+  def headers_as_text
+    # headers_txt must not be frozen
+    headers_txt = +''
+    headers_txt << header_as_text('from')
+    headers_txt << header_as_text('to')
+    headers_txt << header_as_text('cc')
+    return headers_txt
+  end
+
+  def header_as_text(h)
+    value = email.header[h]
+    # ret must not be frozen
+    ret = +''
+    if value
+      ret << h << ': ' << value.to_s << "\n"
+    end
+    return ret
+  rescue Exception => e
+    ret = +''
+    ret << h << ": error reading value\n"
+    return ret
+  end
+
   # Adds a note to an existing issue
   def receive_issue_reply(issue_id, from_journal=nil)
     issue = Issue.find_by(:id => issue_id)
@@ -251,6 +309,7 @@
     issue.safe_attributes = issue_attributes_from_keywords(issue)
     issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
     journal.notes = cleaned_up_text_body
+    journal.notes << "\n\n-- \n" << header_as_text('subject') << headers_as_text
 
     # add To and Cc as watchers before saving so the watchers can reply to Redmine
     add_watchers(issue)
@@ -334,6 +393,12 @@
         return false
       end
     end
+
+    if attachment.filename.to_s =~ /\.(bmp|gif|jpg|jpe|jpeg|png)$/i && attachment.body.decoded.size < 20000
+      logger.info "MailHandler: ignoring attachment #{attachment.filename} based on size"
+      return false
+    end
+
     true
   end
 
@@ -352,30 +417,35 @@
     end
   end
 
-  def get_keyword(attr, options={})
+  def get_keyword(attr, options={}, remove_line=true)
     @keywords ||= {}
     if @keywords.has_key?(attr)
       @keywords[attr]
     else
-      @keywords[attr] = begin
+      value = begin
         override =
           if options.key?(:override)
             options[:override]
           else
             (handler_options[:allow_override] & [attr.to_s.downcase.gsub(/\s+/, '_'), 'all']).present?
           end
-        if override && (v = extract_keyword!(cleaned_up_text_body, attr, options[:format]))
+        if override && (v = extract_keyword!(cleaned_up_text_body, attr, options[:format], remove_line))
           v
         elsif !handler_options[:issue][attr].blank?
           handler_options[:issue][attr]
         end
       end
+      if remove_line
+        #Rails.logger.warn "MailHandler: reading and removed attr #{attr.to_s}: '#{value}'"
+        @keywords[attr] = value
+      end
+      value
     end
   end
 
   # Destructively extracts the value for +attr+ in +text+
   # Returns nil if no matching keyword found
-  def extract_keyword!(text, attr, format=nil)
+  def extract_keyword!(text, attr, format=nil, remove_line=true)
     keys = [attr.to_s.humanize]
     if attr.is_a?(Symbol)
       if user && user.language.present?
@@ -389,10 +459,14 @@
     keys.collect! {|k| Regexp.escape(k)}
     format ||= '.+'
     keyword = nil
-    regexp = /^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i
+    prefix = handler_options[:reprefix]
+    prefix ||= ''
+    regexp = /^#{prefix}(#{keys.join('|')})[ \t]*:[ \t]*(?<kval>#{format})\s*$/i
     if m = text.match(regexp)
-      keyword = m[2].strip
-      text.sub!(regexp, '')
+      keyword = m[:kval].strip
+      if remove_line
+        text.sub!(regexp, '')
+      end
     end
     keyword
   end
@@ -449,6 +523,7 @@
       'due_date' => get_keyword(:due_date, :format => '\d{4}-\d{2}-\d{2}'),
       'estimated_hours' => get_keyword(:estimated_hours),
       'done_ratio' => get_keyword(:done_ratio, :format => '(\d|10)?0'),
+      'subject' => get_keyword(:subject),
       'is_private' => get_keyword_bool(:is_private),
       'parent_issue_id' => get_keyword(:parent_issue)
     }.delete_if {|k, v| v.blank?}
