<commit_before>require 'mail'
require 'mail/message'
require 'gpgme'

require 'mail/gpg/version'
require 'mail/gpg/missing_keys_error'
require 'mail/gpg/version_part'
require 'mail/gpg/decrypted_part'
require 'mail/gpg/encrypted_part'
require 'mail/gpg/inline_decrypted_message'
require 'mail/gpg/gpgme_helper'
require 'mail/gpg/message_patch'
require 'mail/gpg/rails'
require 'mail/gpg/signed_part'
require 'mail/gpg/mime_signed_message'
require 'mail/gpg/inline_signed_message'

module Mail
  module Gpg
    # options are:
    # :sign: sign message using the sender's private key
    # :sign_as: sign using this key (give the corresponding email address or key fingerprint)
    # :passphrase: passphrase for the signing key
    # :keys: A hash mapping recipient email addresses to public keys or public
    # key ids. Imports any keys given here that are not already part of the
    # local keychain before sending the mail.
    # :always_trust: send encrypted mail to untrusted receivers, true by default
    def self.encrypt(cleartext_mail, options = {})
      construct_mail(cleartext_mail, options) do
        receivers = []
        receivers += cleartext_mail.to if cleartext_mail.to
        receivers += cleartext_mail.cc if cleartext_mail.cc
        receivers += cleartext_mail.bcc if cleartext_mail.bcc

        if options[:sign_as]
          options[:sign] = true
          options[:signers] = options.delete(:sign_as)
        elsif options[:sign]
          options[:signers] = cleartext_mail.from
        end

        add_part VersionPart.new
        add_part EncryptedPart.new(cleartext_mail,
                                   options.merge({recipients: receivers}))
        content_type "multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=#{boundary}"
        body.preamble = options[:preamble] || "This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)"
      end
    end

    def self.sign(cleartext_mail, options = {})
      options[:sign_as] ||= cleartext_mail.from
      construct_mail(cleartext_mail, options) do
        to_be_signed = SignedPart.build(cleartext_mail)
        add_part to_be_signed
        add_part to_be_signed.sign(options)

        content_type "multipart/signed; micalg=pgp-sha1; protocol=\"application/pgp-signature\"; boundary=#{boundary}"
        body.preamble = options[:preamble] || "This is an OpenPGP/MIME signed message (RFC 4880 and 3156)"
      end
    end

    # options are:
    # :verify: decrypt and verify
    def self.decrypt(encrypted_mail, options = {})
      if encrypted_mime?(encrypted_mail)
        decrypt_pgp_mime(encrypted_mail, options)
      elsif encrypted_inline?(encrypted_mail)
        decrypt_pgp_inline(encrypted_mail, options)
      else
        raise EncodingError, "Unsupported encryption format '#{encrypted_mail.content_type}'"
      end
    end

    def self.signature_valid?(signed_mail, options = {})
      if signed_mime?(signed_mail)
        signature_valid_pgp_mime?(signed_mail, options)
      elsif signed_inline?(signed_mail)
        signature_valid_inline?(signed_mail, options)
      else
        raise EncodingError, "Unsupported signature format '#{signed_mail.content_type}'"
      end
    end

    # true if a mail is encrypted
    def self.encrypted?(mail)
      return true if encrypted_mime?(mail)
      return true if encrypted_inline?(mail)
      false
    end

    # true if a mail is signed.
    #
    # throws EncodingError if called on an encrypted mail (so only call this method if encrypted? is false)
    def self.signed?(mail)
      return true if signed_mime?(mail)
      return true if signed_inline?(mail)
      if encrypted?(mail)
        raise EncodingError, 'Unable to determine signature on an encrypted mail, use :verify option on decrypt()'
      end
      false
    end

    STANDARD_HEADERS = %w(from to cc bcc reply_to subject in_reply_to return_path message_id)
    MORE_HEADERS = %w(Auto-Submitted List-Help List-Id List-Owner List-Post List-Unsubscribe OpenPGP References)

    private

    def self.construct_mail(cleartext_mail, options, &block)
      Mail.new do
        self.perform_deliveries = cleartext_mail.perform_deliveries
        STANDARD_HEADERS.each do |field|
          if h = cleartext_mail.header[field]
            self.header[field] = h.value
          end
        end
        if cleartext_mail.message_id
          header['Message-ID'] = cleartext_mail['Message-ID'].value
        end
        cleartext_mail.header.fields.each do |field|
          if MORE_HEADERS.include?(field.name) or field.name =~ /^X-/
            header[field.name] = field.value
          end
        end
        instance_eval &block
      end
    end

    # decrypts PGP/MIME (RFC 3156, section 4) encrypted mail
    def self.decrypt_pgp_mime(encrypted_mail, options)
      # MUST containt exactly two body parts
      if encrypted_mail.parts.length != 2
        raise EncodingError, "RFC 3136 mandates exactly two body parts, found '#{encrypted_mail.parts.length}'"
      end
      if !VersionPart.isVersionPart? encrypted_mail.parts[0]
        raise EncodingError, "RFC 3136 first part not a valid version part '#{encrypted_mail.parts[0]}'"
      end
      decrypted = DecryptedPart.new(encrypted_mail.parts[1], options)
      Mail.new(decrypted) do
        %w(from to cc bcc subject reply_to in_reply_to).each do |field|
          send field, encrypted_mail.send(field)
        end<commit_msg>use regexp for matching List-* headers<commit_after>require 'mail'
require 'mail/message'
require 'gpgme'

require 'mail/gpg/version'
require 'mail/gpg/missing_keys_error'
require 'mail/gpg/version_part'
require 'mail/gpg/decrypted_part'
require 'mail/gpg/encrypted_part'
require 'mail/gpg/inline_decrypted_message'
require 'mail/gpg/gpgme_helper'
require 'mail/gpg/message_patch'
require 'mail/gpg/rails'
require 'mail/gpg/signed_part'
require 'mail/gpg/mime_signed_message'
require 'mail/gpg/inline_signed_message'

module Mail
  module Gpg
    # options are:
    # :sign: sign message using the sender's private key
    # :sign_as: sign using this key (give the corresponding email address or key fingerprint)
    # :passphrase: passphrase for the signing key
    # :keys: A hash mapping recipient email addresses to public keys or public
    # key ids. Imports any keys given here that are not already part of the
    # local keychain before sending the mail.
    # :always_trust: send encrypted mail to untrusted receivers, true by default
    def self.encrypt(cleartext_mail, options = {})
      construct_mail(cleartext_mail, options) do
        receivers = []
        receivers += cleartext_mail.to if cleartext_mail.to
        receivers += cleartext_mail.cc if cleartext_mail.cc
        receivers += cleartext_mail.bcc if cleartext_mail.bcc

        if options[:sign_as]
          options[:sign] = true
          options[:signers] = options.delete(:sign_as)
        elsif options[:sign]
          options[:signers] = cleartext_mail.from
        end

        add_part VersionPart.new
        add_part EncryptedPart.new(cleartext_mail,
                                   options.merge({recipients: receivers}))
        content_type "multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=#{boundary}"
        body.preamble = options[:preamble] || "This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)"
      end
    end

    def self.sign(cleartext_mail, options = {})
      options[:sign_as] ||= cleartext_mail.from
      construct_mail(cleartext_mail, options) do
        to_be_signed = SignedPart.build(cleartext_mail)
        add_part to_be_signed
        add_part to_be_signed.sign(options)

        content_type "multipart/signed; micalg=pgp-sha1; protocol=\"application/pgp-signature\"; boundary=#{boundary}"
        body.preamble = options[:preamble] || "This is an OpenPGP/MIME signed message (RFC 4880 and 3156)"
      end
    end

    # options are:
    # :verify: decrypt and verify
    def self.decrypt(encrypted_mail, options = {})
      if encrypted_mime?(encrypted_mail)
        decrypt_pgp_mime(encrypted_mail, options)
      elsif encrypted_inline?(encrypted_mail)
        decrypt_pgp_inline(encrypted_mail, options)
      else
        raise EncodingError, "Unsupported encryption format '#{encrypted_mail.content_type}'"
      end
    end

    def self.signature_valid?(signed_mail, options = {})
      if signed_mime?(signed_mail)
        signature_valid_pgp_mime?(signed_mail, options)
      elsif signed_inline?(signed_mail)
        signature_valid_inline?(signed_mail, options)
      else
        raise EncodingError, "Unsupported signature format '#{signed_mail.content_type}'"
      end
    end

    # true if a mail is encrypted
    def self.encrypted?(mail)
      return true if encrypted_mime?(mail)
      return true if encrypted_inline?(mail)
      false
    end

    # true if a mail is signed.
    #
    # throws EncodingError if called on an encrypted mail (so only call this method if encrypted? is false)
    def self.signed?(mail)
      return true if signed_mime?(mail)
      return true if signed_inline?(mail)
      if encrypted?(mail)
        raise EncodingError, 'Unable to determine signature on an encrypted mail, use :verify option on decrypt()'
      end
      false
    end

    STANDARD_HEADERS = %w(from to cc bcc reply_to subject in_reply_to return_path message_id)
    MORE_HEADERS = %w(Auto-Submitted OpenPGP References)

    private

    def self.construct_mail(cleartext_mail, options, &block)
      Mail.new do
        self.perform_deliveries = cleartext_mail.perform_deliveries
        STANDARD_HEADERS.each do |field|
          if h = cleartext_mail.header[field]
            self.header[field] = h.value
          end
        end
        if cleartext_mail.message_id
          header['Message-ID'] = cleartext_mail['Message-ID'].value
        end
        cleartext_mail.header.fields.each do |field|
          if MORE_HEADERS.include?(field.name) or field.name =~ /^(List|X)-/
            header[field.name] = field.value
          end
        end
        instance_eval &block
      end
    end

    # decrypts PGP/MIME (RFC 3156, section 4) encrypted mail
    def self.decrypt_pgp_mime(encrypted_mail, options)
      # MUST containt exactly two body parts
      if encrypted_mail.parts.length != 2
        raise EncodingError, "RFC 3136 mandates exactly two body parts, found '#{encrypted_mail.parts.length}'"
      end
      if !VersionPart.isVersionPart? encrypted_mail.parts[0]
        raise EncodingError, "RFC 3136 first part not a valid version part '#{encrypted_mail.parts[0]}'"
      end
      decrypted = DecryptedPart.new(encrypted_mail.parts[1], options)
      Mail.new(decrypted) do
        %w(from to cc bcc subject reply_to in_reply_to).each do |field|
          send field, encrypted_mail.send(field)
        end
{'fraction_non_alphanumeric': 0.08511422254974207, 'fraction_numerical': 0.006263817243920413, 'mean_word_length': 3.0375604313871327, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6367933', 'n_tokens_mistral': 3364, 'n_tokens_neox': 3300, 'n_words': 867}