zammad/zammad

View on GitHub
app/models/channel/filter/identify_sender.rb

Summary

Maintainability
D
2 days
Test Coverage
# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/

module Channel::Filter::IdentifySender

  def self.run(_channel, mail, _transaction_params)

    customer_user_id = mail[ :'x-zammad-ticket-customer_id' ]
    customer_user = nil
    if customer_user_id.present?
      customer_user = User.lookup(id: customer_user_id)
      if customer_user
        Rails.logger.debug { "Took customer form x-zammad-ticket-customer_id header '#{customer_user_id}'." }
      else
        Rails.logger.debug { "Invalid x-zammad-ticket-customer_id header '#{customer_user_id}', no such user - take user from 'from'-header." }
      end
    end

    # check if sender exists in database
    if !customer_user && mail[ :'x-zammad-customer-login' ].present?
      customer_user = User.find_by(login: mail[ :'x-zammad-customer-login' ])
    end
    if !customer_user && mail[ :'x-zammad-customer-email' ].present?
      customer_user = User.find_by(email: mail[ :'x-zammad-customer-email' ])
    end

    # get correct customer
    if !customer_user && Setting.get('postmaster_sender_is_agent_search_for_customer') == true && mail[ :'x-zammad-ticket-create-article-sender' ] == 'Agent'
      # get first recipient and set customer
      begin
        to = :'raw-to'
        if mail[to]&.addrs
          items = mail[to].addrs
          items.each do |item|

            # skip if recipient is system email
            next if EmailAddress.exists?(email: item.address.downcase)

            customer_user = user_create(
              login:     item.address,
              firstname: item.display_name,
              email:     item.address,
            )
            break
          end
        end
      rescue => e
        Rails.logger.error "SenderIsSystemAddress: ##{e.inspect}"
      end
    end

    # take regular from as customer
    if !customer_user
      customer_user = user_create(
        login:     mail[ :'x-zammad-customer-login' ] || mail[ :'x-zammad-customer-email' ] || mail[:from_email],
        firstname: mail[ :'x-zammad-customer-firstname' ] || mail[:from_display_name],
        lastname:  mail[ :'x-zammad-customer-lastname' ],
        email:     mail[ :'x-zammad-customer-email' ] || mail[:from_email],
      )
    end

    create_recipients(mail)
    mail[ :'x-zammad-ticket-customer_id' ] = customer_user.id

    # find session user
    session_user_id = mail[ :'x-zammad-session-user-id' ]
    session_user = nil
    if session_user_id.present?
      session_user = User.lookup(id: session_user_id)
      if session_user
        Rails.logger.debug { "Took session form x-zammad-session-user-id header '#{session_user_id}'." }
      else
        Rails.logger.debug { "Invalid x-zammad-session-user-id header '#{session_user_id}', no such user - take user from 'from'-header." }
      end
    end
    if !session_user
      session_user = user_create(
        login:     mail[:from_email],
        firstname: mail[:from_display_name],
        lastname:  '',
        email:     mail[:from_email],
      )
    end
    if session_user
      mail[ :'x-zammad-session-user-id' ] = session_user.id
    end

    true
  end

  # create to and cc user
  def self.create_recipients(mail)
    max_count = 40
    current_count = 0
    %w[raw-to raw-cc].each do |item|
      next if mail[item.to_sym].blank?

      begin
        items = mail[item.to_sym].addrs
        next if items.blank?

        items.each do |address_data|
          email_address = sanitize_email(address_data.address)
          next if email_address.blank?

          # Skip the creation for system email addresses.
          next if EmailAddress.exists?(email: email_address)

          email_address_validation = EmailAddressValidation.new(email_address)
          next if !email_address_validation.valid?

          user_create(
            firstname: address_data.display_name,
            lastname:  '',
            email:     email_address,
          )
          current_count += 1
          return false if current_count == max_count
        end
      rescue => e
        # parse not parsable fields by mail gem like
        #  - Max Kohl | [example.com] <kohl@example.com>
        #  - Max Kohl <max.kohl <max.kohl@example.com>
        Rails.logger.error e
        Rails.logger.error "try it by my self (#{item}): #{mail[item.to_sym]}"
        recipients = mail[item.to_sym].to_s.split(',')
        recipients.each do |recipient|
          address = nil
          display_name = nil
          if recipient =~ %r{.*<(.+?)>}
            address = $1
          end
          if recipient =~ %r{^(.+?)<(.+?)>}
            display_name = $1
          end

          next if address.blank?

          address = sanitize_email(address)

          email_address_validation = EmailAddressValidation.new(address)
          next if !email_address_validation.valid?

          user_create(
            firstname: display_name,
            lastname:  '',
            email:     address,
          )
          current_count += 1
          return false if current_count == max_count
        end
      end
    end
  end

  def self.user_create(attrs, role_ids = nil)
    populate_attributes!(attrs, role_ids: role_ids)

    if attrs[:login]
      attrs[:login] = EmailHelper::Idn.to_unicode(attrs[:login])
    end

    if (user = User.find_by('email = :email OR login = :email', attrs))
      user.update!(attrs.slice(:firstname)) if user.no_name? && attrs[:firstname].present?
    elsif (user = User.create!(attrs))
      user.update!(updated_by_id: user.id, created_by_id: user.id)
    end

    user
  end

  def self.populate_attributes!(attrs, **extras)
    if attrs[:email].match?(%r{\S\s+\S}) || attrs[:email].match?(%r{^<|>$})
      attrs[:preferences] = { mail_delivery_failed:        true,
                              mail_delivery_failed_reason: 'invalid email',
                              mail_delivery_failed_data:   Time.zone.now }
    end

    attrs.merge!(
      email:         sanitize_email(attrs[:email]),
      firstname:     sanitize_name(attrs[:firstname]),
      lastname:      sanitize_name(attrs[:lastname]),
      password:      '',
      active:        true,
      role_ids:      extras[:role_ids] || Role.signup_role_ids,
      updated_by_id: 1,
      created_by_id: 1
    )
  end

  def self.sanitize_name(string)
    return '' if string.nil?

    string.strip
          .delete('"')
          .delete_prefix("'")
          .delete_suffix("'")
          .gsub(%r{.+?\s\(.+?\)$}, '')
  end

  def self.sanitize_email(string)
    string += '@local' if string.exclude?('@')

    string = string.downcase
          .strip
          .delete('"')
          .delete("'")
          .delete(' ') # see https://github.com/zammad/zammad/issues/2254
          .sub(%r{^<|>$}, '')        # see https://github.com/zammad/zammad/issues/2254
          .sub(%r{\A'(.*)'\z}, '\1') # see https://github.com/zammad/zammad/issues/2154
          .gsub(%r{\s}, '')          # see https://github.com/zammad/zammad/issues/2198
          .delete_suffix('.')

    EmailHelper::Idn.to_unicode(string)
  end

end