diff --git a/.rubocop.yml b/.rubocop.yml index d8578271..a1b80d8f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,7 +8,9 @@ Style/WordArray: EnforcedStyle: brackets Layout/LineLength: - Enabled: false + # We want to reduce this back down to 120 but there are a fair number of offences + # of this which need addressing individually and carefully. + Max: 200 Metrics/AbcSize: Enabled: false diff --git a/app/controllers/servers_controller.rb b/app/controllers/servers_controller.rb index 6f8cfe8b..1058b53b 100644 --- a/app/controllers/servers_controller.rb +++ b/app/controllers/servers_controller.rb @@ -43,7 +43,12 @@ def create def update extra_params = [:spam_threshold, :spam_failure_threshold, :postmaster_address] - extra_params += [:send_limit, :allow_sender, :privacy_mode, :log_smtp_data, :outbound_spam_threshold, :message_retention_days, :raw_message_retention_days, :raw_message_retention_size] if current_user.admin? + + if current_user.admin? + extra_params += [:send_limit, :allow_sender, :privacy_mode, :log_smtp_data, :outbound_spam_threshold, + :message_retention_days, :raw_message_retention_days, :raw_message_retention_size] + end + if @server.update(safe_params(*extra_params)) redirect_to_with_json organization_server_path(organization, @server), notice: "Server settings have been updated" else diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index 2448871c..45d55bad 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -71,7 +71,9 @@ def update if @user.save if email_changed - redirect_to_with_json verify_path(return_to: settings_path), notice: "Your settings have been updated successfully. As you've changed, your e-mail address you'll need to verify it before you can continue." + redirect_to_with_json verify_path(return_to: settings_path), + notice: "Your settings have been updated successfully. As you've changed, your e-mail " \ + "address you'll need to verify it before you can continue." else redirect_to_with_json settings_path, notice: "Your settings have been updated successfully." end diff --git a/app/jobs/unqueue_message_job.rb b/app/jobs/unqueue_message_job.rb index 8eca51cd..6a1faf3e 100644 --- a/app/jobs/unqueue_message_job.rb +++ b/app/jobs/unqueue_message_job.rb @@ -2,6 +2,7 @@ class UnqueueMessageJob < Postal::Job + # rubocop:disable Layout/LineLength def perform if original_message = QueuedMessage.find_by_id(params["id"]) if original_message.acquire_lock @@ -430,7 +431,12 @@ def perform Sentry.capture_exception(e, extra: { job_id: self.id, server_id: queued_message.server_id, message_id: queued_message.message_id }) end if queued_message.message - queued_message.message.create_delivery("Error", details: "An internal error occurred while sending this message. This message will be retried automatically. If this persists, contact support for assistance.", output: "#{e.class}: #{e.message}", log_id: "J-#{self.id}") + queued_message.message.create_delivery("Error", + details: "An internal error occurred while sending " \ + "this message. This message will be retried " \ + "automatically.", + output: "#{e.class}: #{e.message}", log_id: "J-#{self.id}" + ) end end end @@ -448,6 +454,7 @@ def perform nil end end + # rubocop:enable Layout/LineLength private diff --git a/app/models/server.rb b/app/models/server.rb index 762ed60f..7a61605d 100644 --- a/app/models/server.rb +++ b/app/models/server.rb @@ -220,6 +220,10 @@ def stats } end + # Return the domain which can be used to authenticate emails sent from the given e-mail address. + # + # @param address [String] an e-mail address + # @return [Domain, nil] the domain to use for authentication def authenticated_domain_for_address(address) return nil if address.blank? @@ -230,14 +234,24 @@ def authenticated_domain_for_address(address) uname, = uname.split("+", 2) - # Check the server's domain - if domain = Domain.verified.order(owner_type: :desc).where("(owner_type = 'Organization' AND owner_id = ?) OR (owner_type = 'Server' AND owner_id = ?)", organization_id, id).where(name: domain_name).first - return domain - end + # Find a verified domain which directly matches the domain name for the given address. + domain = Domain.verified + .order(owner_type: :desc) + .where("(owner_type = 'Organization' AND owner_id = ?) OR " \ + "(owner_type = 'Server' AND owner_id = ?)", organization_id, id) + .where(name: domain_name) + .first + + # If there is a matching domain, return it + return domain if domain - return unless any_domain = domains.verified.where(use_for_any: true).order(:name).first + # Otherwise, we need to look to see if there is a domain configured which can be used as the authenticated + # domain for any domain. This will look for domains directly within the server and return that. + any_domain = domains.verified.where(use_for_any: true).order(:name).first + return any_domain if any_domain - any_domain + # Return nil if we can't find anything suitable + nil end def find_authenticated_domain_from_headers(headers) diff --git a/lib/postal/countries.rb b/lib/postal/countries.rb deleted file mode 100644 index d7db9e42..00000000 --- a/lib/postal/countries.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module Postal - module Countries - - NAMES = ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia, Plurinational State of", "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", "Christmas Island", "Cocos (keeling) Islands", "Colombia", "Comoros", "Congo", "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote D'ivoire", "Croatia", "Cuba", "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (malvinas)", "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", "Guinea-bissau", "Guyana", "Haiti", "Heard Island and Mcdonald Islands", "Holy See (Vatican City State)", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq", "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan", "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, the Former Yugoslav Republic of", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of", "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "Saint Barthelemy", "Saint Helena, Ascension and Tristan Da Cunha", "Saint Kitts and Nevis", "Saint Lucia", "Saint Martin", "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", "Taiwan", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-leste", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela, Bolivarian Republic of", "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara", "Yemen", "Zambia", "Zimbabwe"] - - end -end diff --git a/lib/postal/dkim_header.rb b/lib/postal/dkim_header.rb index 20ab04af..eb685eda 100644 --- a/lib/postal/dkim_header.rb +++ b/lib/postal/dkim_header.rb @@ -34,7 +34,12 @@ def header_names def normalized_headers [].tap do |new_headers| - headers.select { |h| h.match(/^(from|sender|reply-to|subject|date|message-id|to|cc|mime-version|content-type|content-transfer-encoding|resent-to|resent-cc|resent-from|resent-sender|resent-message-id|in-reply-to|references|list-id|list-help|list-owner|list-unsubscribe|list-subscribe|list-post):/i) }.each do |h| + headers.select { |h| h.match(%r{ + ^( + from|sender|reply-to|subject|date|message-id|to|cc|mime-version|content-type|content-transfer-encoding| + resent-to|resent-cc|resent-from|resent-sender|resent-message-id|in-reply-to|references|list-id|list-help| + list-owner|list-unsubscribe|list-subscribe|list-post + ):}ix) }.each do |h| new_headers << normalize_header(h) end end diff --git a/lib/postal/tracking_middleware.rb b/lib/postal/tracking_middleware.rb index 3156b727..5cf4f271 100644 --- a/lib/postal/tracking_middleware.rb +++ b/lib/postal/tracking_middleware.rb @@ -87,8 +87,22 @@ def dispatch_redirect_request(request, server_token, link_token) time = Time.now.to_f if link["message_id"] message_db.update(:messages, { clicked: time }, where: { id: link["message_id"] }) - message_db.insert(:clicks, { message_id: link["message_id"], link_id: link["id"], ip_address: request.ip, user_agent: request.user_agent, timestamp: time }) - SendWebhookJob.queue(:main, server_id: message_db.server_id, event: "MessageLinkClicked", payload: { _message: link["message_id"], url: link["url"], token: link["token"], ip_address: request.ip, user_agent: request.user_agent }) + message_db.insert(:clicks, { + message_id: link["message_id"], + link_id: link["id"], + ip_address: request.ip, + user_agent: request.user_agent, + timestamp: time }) + SendWebhookJob.queue(:main, + server_id: message_db.server_id, + event: "MessageLinkClicked", + payload: { + _message: link["message_id"], + url: link["url"], + token: link["token"], + ip_address: request.ip, + user_agent: request.user_agent + }) end [307, { "Location" => link["url"] }, ["Redirected to: #{link['url']}"]] diff --git a/script/queue_size.rb b/script/queue_size.rb index aa0f0d23..89616a56 100755 --- a/script/queue_size.rb +++ b/script/queue_size.rb @@ -4,6 +4,12 @@ require_relative "../lib/postal/config" require "mysql2" -client = Mysql2::Client.new(host: Postal.config.main_db.host, username: Postal.config.main_db.username, password: Postal.config.main_db.password, port: Postal.config.main_db.port, database: Postal.config.main_db.database) -result = client.query("SELECT COUNT(id) as size FROM `queued_messages` WHERE retry_after IS NULL OR retry_after <= ADDTIME(UTC_TIMESTAMP(), '30') AND locked_at IS NULL") +client = Mysql2::Client.new( + host: Postal.config.main_db.host, + username: Postal.config.main_db.username, + password: Postal.config.main_db.password, + port: Postal.config.main_db.port, + database: Postal.config.main_db.database) +result = client.query("SELECT COUNT(id) as size FROM `queued_messages` WHERE retry_after IS NULL OR " \ + "retry_after <= ADDTIME(UTC_TIMESTAMP(), '30') AND locked_at IS NULL") puts result.to_a.first["size"]