Compare commits

...

5 Commits

Author SHA1 Message Date
Arthur POULET 99c1495d3f
Add auto-registration mailinglists 2022-11-24 21:57:57 +01:00
Arthur POULET ea02fe40ca
Rewrite some params 2022-11-24 21:46:58 +01:00
Arthur POULET f9a0648264
Improve files dependencies 2022-11-24 21:03:56 +01:00
Arthur POULET 43accadaf3
Fix the subscribe email 2022-11-24 20:49:07 +01:00
Arthur POULET 87002a9571
Fix some coding style 2022-11-24 20:47:51 +01:00
12 changed files with 83 additions and 56 deletions

View File

@ -3,6 +3,8 @@
$LOAD_PATH << File.join(Dir.pwd, "lib")
require "app"
require "protocols"
require "models"
require "pry"
# mailinglist1 = Mailinglist.build(name: "FreeML", suffix: "free", strategy: "free").save

View File

@ -5,6 +5,8 @@ $LOAD_PATH << File.join(Dir.pwd, "lib")
require "app"
require "optparse"
require "uuid"
require "protocols"
require "models"
options = {
name: UUID.generate,
@ -21,8 +23,17 @@ OptionParser.new do |opts|
opts.on("-n=NAME", "--name=NAME", "Define the name of the option") do |v|
options[:name] = v
end
opts.on("-e=EMAIL", "--email=EMAIL", "Initialize the list with some emails, separated with ,") do |email|
options[:emails] ||= []
options[:emails] << email
end
end.parse!
options[:suffix] = options[:name].gsub(/[^a-zA-Z0-9]+/, '-')
mailinglist = Mailinglist.build(name: options[:name], suffix: options[:suffix], strategy: options[:strategy]).save
pp mailinglist
options[:emails].each do |email|
pp Email.register!(name: email, email: email, mailinglist: mailinglist)
end

View File

@ -3,12 +3,12 @@
$LOAD_PATH << File.join(Dir.pwd, "lib")
require "app"
require "protocols"
require "models"
mailinglist0 = Mailinglist.build(name: "AutoReg", suffix: "autoreg", strategy: "autoregister").save
mailinglist1 = Mailinglist.build(name: "FreeML", suffix: "free", strategy: "free").save
mailinglist2 = Mailinglist.build(name: "ValidatedML", suffix: "validated", strategy: "validated").save
mailinglist3 = Mailinglist.build(name: "ClosedML", suffix: "closed", strategy: "closed").save
pp mailinglist1, mailinglist2, mailinglist3
# email1 = Email.register!(name: "AP", email: "arthur.poulet.hunk@sceptique.eu", mailinglist: mailinglist)
# email2 = Email.register!(name: "AP2", email: "arthur.poulet.hunk2@sceptique.eu", mailinglist: mailinglist)
# pp email1, email2
pp mailinglist0, mailinglist1, mailinglist2, mailinglist3

View File

@ -2,6 +2,8 @@
$LOAD_PATH << File.join(Dir.pwd, "lib")
require "app"
require "protocols"
require "models"
require "distributor"
Signal.trap("SIGINT") do

View File

@ -2,7 +2,10 @@
$LOAD_PATH << File.join(Dir.pwd, "lib")
require "app"
require "protocols"
require "models"
require "sinatra"
set :views, File.expand_path(File.join(settings.root + "/../lib/web/views"))
require "web"

View File

@ -8,5 +8,3 @@ require "sequel"
$db = Sequel.connect(ENV["DB_URL"])
require_relative "logger"
require_relative "protocols"
require_relative "models" rescue nil

View File

@ -36,12 +36,13 @@ class Distributor
$logger.info "incoming email from #{mail.from} | #{mail.subject}"
list = Mailinglist.search_mail(mail)
if list
# TODO: create list from mail ?
subject, subject_attributes = mail.subject.split(",", 2)
attributes = Attributes.parse(subject: subject_attributes, body: mail.body)
handler = @handlers[subject] || @handlers[:default]
$logger.info "#{handler.class}#handle on #{list.email} for #{mail.from}"
handler.handle(list:, to: mail, attributes:)
handler.handle(list: list, mail: mail, attributes: attributes)
else
$logger.warn "list #{mail.to} do not exist (asked by #{mail.from})"
end
mail.seen!(imap_client: @imap_client)
end

View File

@ -4,31 +4,31 @@ class Distributor
class SetPermissions < Action
SET_PERMISSIONS_TEMPLATE = Actions.template("set_permissions.success")
def handle(list:, to:, attributes:)
def handle(list:, mail:, attributes:)
return if attributes["user-email"].nil? # drop missing param
return if attributes["permissions"].nil? # drop missing param
modo = Email.first(mailinglist: list, email: to.from)
modo = Email.first(mailinglist: list, email: mail.from)
if !modo&.modo? && !modo&.op?
$logger.warn "SECU <#{to.from}> failed to set-permissions <#{list.email}> modo"
$logger.warn "SECU <#{mail.from}> failed to set-permissions <#{list.email}> modo"
return nil
end
user_email = attributes["user-email"]
user = Email.first(mailinglist: list, email: user_email)
if user.nil?
$logger.warn "SECU <#{to.from}> failed to set-permissions on non-existing email <#{user_email}>"
$logger.warn "SECU <#{mail.from}> failed to set-permissions on non-existing email <#{user_email}>"
return nil
end
if user.op? && !modo.op?
$logger.warn "SECU <#{to.from}> failed to set-permissions on op email <#{user_email}>"
$logger.warn "SECU <#{mail.from}> failed to set-permissions on op email <#{user_email}>"
return nil
end
permissions = attributes["permissions"].to_i
if Email::Permissions.op?(permissions) && !modo.op?
$logger.warn "SECU <#{to.from}> failed to set op permissions on email <#{user_email}>"
$logger.warn "SECU <#{mail.from}> failed to set op permissions on email <#{user_email}>"
return nil
end

View File

@ -7,81 +7,86 @@ class Distributor
WAIT_USER_TEMPLATE = Actions.template("subscribe.wait_user")
WAIT_MODO_TEMPLATE = Actions.template("subscribe.wait_modo")
def handle(list:, to:, attributes:)
def handle(list:, mail:, attributes:)
register =
begin
Email.register!(mailinglist: list, name: to.from_name, email: to.from).save
Email.register!(mailinglist: list, name: mail.from_name, email: mail.from).save
rescue StandardError => e
$logger.error e.message
nil
end
if register
if !register.reader?
handle_wait_validation(list:, to:, register:)
handle_wait_validation(list: list, mail: mail, register: register)
else
handle_subscribed(list:, to:, register:)
handle_subscribed(list: list, mail: mail, register: register)
end
else
handle_403(list:, to:)
handle_403(list: list, mail: mail)
end
end
def handle_wait_validation(list:, to:, register:)
def handle_wait_validation(list:, mail:, register:)
$logger.debug "Subscribe#handle_wait_validation on #{list.email} for #{to.from}"
body = WAIT_USER_TEMPLATE.result binding
@distributor.distribute(to.to_response(list:, to:, body:))
@distributor.distribute(mail.to_response(list: list, mail: mail, body: body))
modo = list.enabled_modos.first
body = WAIT_MODO_TEMPLATE.result binding
@distributor.distribute(
Protocols::Mail.build(
subject: "ML #{list.name} requires validaton for #{to.from}", list:, to: modo.email, body:,
subject: "ML #{list.name} requires validaton for #{mail.from}", list: list, to: modo.email, body: body,
),
)
end
def handle_subscribed(list:, to:, register:)
$logger.debug "Subscribe#handle_subscribed on #{list.email} for #{to.from}"
def handle_subscribed(list:, mail:, register:)
$logger.debug "Subscribe#handle_subscribed on #{list.email} for #{mail.from}"
$logger.debug register.inspect
body = SUCCESS_TEMPLATE.result binding
@distributor.distribute(to.to_response(list:, to:, body:))
@distributor.distribute(mail.to_response(list: list, mail: mail, body: body))
end
def handle_403(list:, to:)
$logger.debug "Subscribe#handle_403 on #{list.email} for #{to.from}"
def handle_403(list:, mail:)
$logger.debug "Subscribe#handle_403 on #{list.email} for #{mail.from}"
body = FORBIDDEN_TEMPLATE.result binding
@distributor.distribute(to.to_response(list:, to:, body:))
@distributor.distribute(mail.to_response(list: list, mail: mail, body: body))
end
end
class Unsubscribe < Action
SUCCESS_TEMPLATE = Actions.template("unsubscribe.success")
def handle(list:, to:, attributes:)
Email.unregister!(mailinglist: list, email: to.from)
def handle(list:, mail:, attributes:)
Email.unregister!(mailinglist: list, email: mail.from)
body = SUCCESS_TEMPLATE.result binding
@distributor.distribute(to.to_response(list:, to:, body:))
@distributor.distribute(mail.to_response(list: list, mail: mail, body: body))
end
end
class Help < Action
HELP_TEMPLATE = Actions.template("help")
def handle(list:, to:, attributes:)
def handle(list:, mail:, attributes:)
body = HELP_TEMPLATE.result binding
@distributor.distribute(to.to_response(list:, to:, body:))
@distributor.distribute(mail.to_response(list: list, mail: mail, body: body))
end
end
# This distribute the mail among the readers
class Distribute < Action
def handle(list:, to:, attributes:)
if !list
$logger.warn "invalid email writer for #{mail.from} on #{mail.to}"
return nil
def handle(list:, mail:, attributes:)
if !list.enabled_writers.find { _1.email == mail.from }
if list.registration?("autoregister")
Email.register!(mailinglist: list, name: mail.from_name, email: mail.from).save
else
$logger.warn "invalid email writer for #{mail.from} on #{mail.to}"
return nil
end
end
list.enabled_readers.each do |reader|
to_distrib = to.to_redistribute(list:, dest: reader)
to_distrib = mail.to_redistribute(list: list, dest: reader)
@distributor.distribute(to_distrib)
end
end

View File

@ -1,3 +1,3 @@
You have subscribed to <%= list.name %> <<%= list.email %>>
Your current permissions are <%= register.permissions.to_s(2).rjust(8, "0") %>
Your current permissions are <%= register.permissions_words.join(', ') %>

View File

@ -6,8 +6,8 @@ class Mailinglist < Sequel::Model($db)
HOST = ENV["MAILINGLIST_HOST"]
STRATEGIES = {
registration: %w[free validated closed],
moderate: %w[freewrite restrictedwrite],
registration: %w[autoregister free validated closed],
moderation: %w[freewrite restrictedwrite],
}.freeze
one_to_many :emails
@ -23,7 +23,7 @@ class Mailinglist < Sequel::Model($db)
end
def self.build(name:, aliasing: nil, suffix: nil, strategy: "closed")
email = "#{BASE_USER}"
email = BASE_USER.dup
email << SUFFIX_SEPARATOR << suffix if suffix
aliasing = UUID.generate if !suffix && !aliasing
email << aliasing if aliasing
@ -48,27 +48,35 @@ class Mailinglist < Sequel::Model($db)
end
def registration
strategy.split("|").select { STRATEGIES[:registration].include(_1) }
strategy.split("|").filter { STRATEGIES[:registration].include?(_1) }
end
def registration?(find)
registration.include?(find)
end
def moderation
strategy.split("|").select { STRATEGIES[:registration].include(_1) }
strategy.split("|").filter { STRATEGIES[:moderation].include?(_1) }
end
def moderation?(find)
moderation.include?(find)
end
def enabled_readers
emails.filter{ _1.permissions & Email::Permissions::READ != 0 }
emails.filter { _1.permissions & Email::Permissions::READ != 0 }
end
def enabled_writers
emails.filter{ _1.permissions & Email::Permissions::WRITE != 0 }
emails.filter { _1.permissions & Email::Permissions::WRITE != 0 }
end
def enabled_admins
emails.filter{ _1.permissions & Email::Permissions::ADMIN != 0 }
emails.filter { _1.permissions & Email::Permissions::ADMIN != 0 }
end
def enabled_modos
emails.filter{ _1.permissions & Email::Permissions::MODO != 0 }
emails.filter { _1.permissions & Email::Permissions::MODO != 0 }
end
def actions_emails
@ -100,9 +108,7 @@ class Mailinglist < Sequel::Model($db)
end
def set_permissions_email(*permissions_symbols, user_email: nil, permissions: nil)
if permissions.nil?
permissions = Email::Permissions.from_symbols(*permissions_symbols)
end
permissions = Email::Permissions.from_symbols(*permissions_symbols) if permissions.nil?
user_part = user_email ? ",user-email=#{user_email}" : ""
permissions_part = ",permissions=#{permissions}"
"#{email}?subject=set-permissions#{user_part}#{permissions_part}"
@ -112,5 +118,4 @@ class Mailinglist < Sequel::Model($db)
"#{email}?subject=list-users"
end
end

View File

@ -78,7 +78,7 @@ class Protocols::Mail
["List-Post", "<mailto:#{list.post_email}>"],
["List-Owner", "<mailto:#{list.owner_email}>"],
["In-Reply-To", @message_id],
["Precedence", "list"],
%w[Precedence list],
# ["Precedence", "bulk"],
["Message-Id", "<#{UUID.generate}@#{ENV['SENDER_HOST']}>"],
["X-Sequence", (@seq + 1).to_s],
@ -124,8 +124,8 @@ class Protocols::Mail
# Create a response email for an existing one.
#
# @param list [Mailinglist]
# @param to [Protocols::Mail]
def to_response(list:, to:, body:)
# @param mail [Protocols::Mail]
def to_response(list:, mail:, body:)
new = clone
new.replace_headers!(
["User-Agent", USER_AGENT],
@ -139,7 +139,7 @@ class Protocols::Mail
["Message-Id", "<#{UUID.generate}@#{ENV['SENDER_HOST']}>"],
)
new.to = to.from
new.to = mail.from
new.from = list.email
new.body = "#{body}#{list.signature}"
new