class ASF::Mail
Public Class Methods
Convert dom, list to form used in mail_list_autosub.yml This changed in March 2022 from dom-list format
# File lib/whimsy/asf/mail.rb, line 231 def self._autosubid(dom, list) return "#{list}@#{dom}" end
# File lib/whimsy/asf/mail.rb, line 52 def self._cannot_sub self._load_auto() @auto[:disallowed] + @auto[:intrinsic] end
# File lib/whimsy/asf/mail.rb, line 57 def self._cannot_unsub self._load_auto() @auto[:intrinsic] end
# File lib/whimsy/asf/mail.rb, line 67 def self._chairs_allowed self._load_auto() @auto[:chairs] end
# File lib/whimsy/asf/mail.rb, line 62 def self._committers_allowed self._load_auto() @auto[:committers] end
list of mailing lists that aren’t actively seeking new subscribers
# File lib/whimsy/asf/mail.rb, line 47 def self._deprecated self._load_auto() @auto[:deprecated] end
Load the auto-subscription file
# File lib/whimsy/asf/mail.rb, line 268 def self._load_auto apmail_bin = File.join(ASF::Config[:puppet_data], 'apmail_bin') # Loaded by puppet auto_file = File.join(apmail_bin, 'mail_list_autosub.yml') auto_mtime = File.mtime(auto_file) # fetch this up front in case file updated during loading if not @auto or auto_mtime != @auto_mtime @auto = YAML.load_file(auto_file) @auto_mtime = auto_mtime end end
Load the flags file
# File lib/whimsy/asf/mail.rb, line 279 def self._load_flags # flags for each mailing list @list_flags ||= File.join(ASF::Config[:subscriptions], 'list-flags') if not @flags or File.mtime(@list_flags) != @flags_mtime lists = [] File.open(@list_flags).each do |line| if line.match(/(?:^F:-([a-zA-Z]{26}) )?(\S+) (\S+)/) flags, dom, list = $1, $2, $3 next if list =~ /^infra-[a-z]$/ or (dom == 'incubator' and list == 'infra-dev') lists << [dom, list, flags || ''] else raise "Unexpected flags: #{line}" end end @flags = lists @flags_hash = lists.map{|d,l,f| ["#{l}@#{d}",f]}.to_h @flags_mtime = File.mtime(@list_flags) end end
# File lib/whimsy/asf/mail.rb, line 72 def self._members_allowed self._load_auto() @auto[:members] + @auto[:chairs] end
Convert dom, list to form currently used in subreq.py
# File lib/whimsy/asf/mail.rb, line 222 def self.archivelistid(dom, list) return "apachecon-#{list}" if dom == 'apachecon.com' return list if dom == 'apache.org' dom.sub('.apache.org', '-') + list end
which lists are available for automatic moderation via Whimsy? Params: ldap_pmcs: list of (P)PMC mail_list names to which the user belongs (as owner) lid_only: return lid instead of [dom,list,lid] Return: an array of entries: lid or [dom,list,lid]
# File lib/whimsy/asf/mail.rb, line 156 def self.canmod(ldap_pmcs, lidonly = true) allowed = [] parse_flags do |dom, list, _| autoid = _autosubid(dom, list) next if self._deprecated.include? autoid next if self._cannot_sub.include? autoid lid = archivelistid(dom, list) if ldap_pmcs.include? dom.sub('.apache.org', '') if lidonly allowed << lid else allowed << [dom, list, lid] end end end allowed end
Can a list be read by the person with the specified attributes? Note: this is different from cansub - not all readable lists can be self-subscribed
Params: listid: list@domain member: true if member pmc_chair: true if pmc_chair ldap_pmcs: list of (P)PMC mail_list names to which the user belongs Return true if person is allowed to read the list nil if the list is not known otherwise true
# File lib/whimsy/asf/mail.rb, line 95 def self.canread(listid, member=false, pmc_chair=false, ldap_pmcs=[]) flags = getflags(listid) return nil if flags.nil? # Not a known list return true unless isModSub?(flags) # subscription not needed return true if self._cannot_unsub.include? listid # must be system maintained, so assume OK return true if self._committers_allowed().include?(listid) return true if member # They can read anything return true if pmc_chair and self._chairs_allowed.include? listid return true if ldap_pmcs and ldap_pmcs.include? listid.split('@')[-1].sub('.apache.org', '') false end
which lists are available for subscription via Whimsy? Params: member: true if member pmc_chair: true if pmc_chair ldap_pmcs: list of (P)PMC mail_list names to which the user belongs lid_only: return lid instead of [dom,list,lid] Return: an array of entries: lid or [dom,list,lid]
# File lib/whimsy/asf/mail.rb, line 114 def self.cansub(member, pmc_chair, ldap_pmcs, lidonly = true) allowed = [] parse_flags do |dom, list, f| autoid = _autosubid(dom, list) next if self._deprecated.include? autoid next if self._cannot_sub.include? autoid lid = archivelistid(dom, list) cansub = false modsub = isModSub?(f) if not modsub # subs not moderated; allow all cansub = true elsif self._committers_allowed().include?(autoid) # always allowed cansub = true else # subs are moderated if member if list == 'private' or self._members_allowed.include?(autoid) cansub = true end elsif ldap_pmcs and list == 'private' and ldap_pmcs.include? dom.sub('.apache.org', '') cansub = true end if pmc_chair and self._chairs_allowed.include? autoid cansub = true end end if cansub if lidonly allowed << lid else allowed << [dom, list, lid] end end end allowed end
common configuration for sending mail; loads :sendmail
configuration from ~/.whimsy
if available; otherwise default to disable openssl verification as that is what it required in order to work on the infrastructure provided whimsy-vm.
# File lib/whimsy/asf/mail.rb, line 179 def self.configure # fetch overrides sendmail = ASF::Config.get(:sendmail) if sendmail # convert string keys to symbols options = Hash[sendmail.map {|key, value| [key.to_sym, value]}] # extract delivery method method = options.delete(:delivery_method).to_sym else # provide defaults that work on whimsy-vm* infrastructure. Since # procmail is configured with a self-signed certificate, verification # isn't a possibility method = :smtp options = {openssl_verify_mode: 'none'} end ::Mail.defaults do delivery_method method, options end end
get flags for list@domain; nil if not found
# File lib/whimsy/asf/mail.rb, line 316 def self.getflags(listid) self._load_flags() @flags_hash[listid] end
Do the flags indicate subscription moderation?
# File lib/whimsy/asf/mail.rb, line 322 def self.isModSub?(flags) flags.include? 's' end
return a Hash containing complete list of all known emails, and the ASF::Person
that is associated with that email.
# File lib/whimsy/asf/mail.rb, line 8 def self.list begin return @list.to_h if @list rescue NoMethodError, WeakRef::RefError end list = {} # load info from LDAP people = ASF::Person.preload(['mail', 'asf-altEmail']) people.each do |person| (person.mail + person.alt_email).each do |mail| list[mail.downcase] = person end end # load all member emails in one pass ASF::Member.each do |id, text| Member.emails(text).each do |mail| list[mail.downcase] ||= Person.find(id) end end # load all ICLA emails in one pass ASF::ICLA.each do |icla| person = Person.find(icla.id) icla.emails.each do |email| list[email.downcase] ||= person end next if icla.noId? list["#{icla.id.downcase}@apache.org"] ||= person end @list = WeakRef.new(list) list end
Convert list@host.apache.org to host-list (listkey) style
# File lib/whimsy/asf/mail.rb, line 216 def self.listdom2listkey(listdom) list, dom = listdom.split('@') self.archivelistid(dom, list) end
parse the flags F:-aBcdeFgHiJklMnOpqrSTUVWXYz domain list Input: filter = RE to match against the flags, e.g. /s/ for subsmod Output: yields: domain, list, flags
# File lib/whimsy/asf/mail.rb, line 306 def self.parse_flags(filter=nil) self._load_flags() @flags.each do |d, l, f| next if filter and f !~ filter yield [d, l, f] end end
List of .qmail files that could clash with user ids (See: INFRA-14566)
# File lib/whimsy/asf/mail.rb, line 203 def self.qmail_ids return [] unless File.exist? '/srv/subscriptions/qmail.ids' File.read('/srv/subscriptions/qmail.ids').split end
Is the id used by qmail? See also ASF::ICLA.taken?
# File lib/whimsy/asf/mail.rb, line 211 def self.taken?(id) self.qmail_ids.include? id end
Canonicalise an email address, removing aliases and ignored punctuation and downcasing the name if safe to do so
Currently only handles aliases for @gmail.com and @googlemail.com
All domains are converted to lower-case
The case of the name part is preserved since some providers may be case-sensitive Almost all providers ignore case in names, however that is not guaranteed
# File lib/whimsy/asf/mail.rb, line 244 def self.to_canonical(email) parts = email.split('@') if parts.length == 2 name, dom = parts return email if name.empty? || dom.empty? dom.downcase! dom = 'gmail.com' if dom == 'googlemail.com' # same mailbox if dom == 'gmail.com' return name.sub(/\+.*/, '').gsub('.', '').downcase + '@' + dom else # Effectively the same: dom = 'apache.org' if dom == 'minotaur.apache.org' # only downcase the domain (done above) return name + '@' + dom end end # Invalid; return input rather than failing return email end
Is a list available for unsubscription via Whimsy? Params: listid (a@b) Return: true or false
# File lib/whimsy/asf/mail.rb, line 80 def self.unsubbable?(listid) !self._cannot_unsub.include? listid end