lib/ezmlm/list.rb
changeset 16 e135ccae6783
parent 15 a38e6916504c
child 17 23c7f5c8ee39
equal deleted inserted replaced
15:a38e6916504c 16:e135ccae6783
    17 
    17 
    18 ### A Ruby interface to an ezmlm-idx mailing list directory
    18 ### A Ruby interface to an ezmlm-idx mailing list directory
    19 ###
    19 ###
    20 class Ezmlm::List
    20 class Ezmlm::List
    21 
    21 
    22 	# Quick address space detection, to (hopefully)
       
    23 	# match the overflow size on this machine.
       
    24 	ADDRESS_SPACE = [ 'i' ].pack( 'p' ).size * 8
       
    25 
       
    26 	# Valid subdirectories/sections for subscriptions.
    22 	# Valid subdirectories/sections for subscriptions.
    27 	SUBSCRIPTION_DIRS = %w[ deny mod digest allow ]
    23 	SUBSCRIPTION_DIRS = %w[ deny mod digest allow ]
    28 
    24 
    29 
    25 
    30 	### Create a new Ezmlm::List object for the specified +listdir+, which should be
    26 	### Create a new Ezmlm::List object for the specified +listdir+, which should be
    73 
    69 
    74 	### Returns +true+ if +address+ is a subscriber to this list.
    70 	### Returns +true+ if +address+ is a subscriber to this list.
    75 	###
    71 	###
    76 	def include?( addr, section: nil )
    72 	def include?( addr, section: nil )
    77 		addr.downcase!
    73 		addr.downcase!
    78 		file = self.subscription_dir( section ) + self.hashchar( addr )
    74 		file = self.subscription_dir( section ) + Ezmlm::Hash.subscriber( addr )
    79 		return false unless file.exist?
    75 		return false unless file.exist?
    80 		return file.read.scan( /T([^\0]+)\0/ ).flatten.include?( addr )
    76 		return file.read.scan( /T([^\0]+)\0/ ).flatten.include?( addr )
    81 	end
    77 	end
    82 	alias_method :is_subscriber?, :include?
    78 	alias_method :is_subscriber?, :include?
    83 
    79 
    95 	def subscribe( *addr, section: nil )
    91 	def subscribe( *addr, section: nil )
    96 		addr.each do |address|
    92 		addr.each do |address|
    97 			next unless address.index( '@' )
    93 			next unless address.index( '@' )
    98 			address.downcase!
    94 			address.downcase!
    99 
    95 
   100 			file = self.subscription_dir( section ) + self.hashchar( address )
    96 			file = self.subscription_dir( section ) + Ezmlm::Hash.subscriber( address )
   101 			self.with_safety do
    97 			self.with_safety do
   102 				if file.exist?
    98 				if file.exist?
   103 					addresses = file.read.scan( /T([^\0]+)\0/ ).flatten
    99 					addresses = file.read.scan( /T([^\0]+)\0/ ).flatten
   104 					addresses << address
   100 					addresses << address
   105 					file.open( 'w' ) do |f|
   101 					file.open( 'w' ) do |f|
   121 	###
   117 	###
   122 	def unsubscribe( *addr, section: nil )
   118 	def unsubscribe( *addr, section: nil )
   123 		addr.each do |address|
   119 		addr.each do |address|
   124 			address.downcase!
   120 			address.downcase!
   125 
   121 
   126 			file = self.subscription_dir( section ) + self.hashchar( address )
   122 			file = self.subscription_dir( section ) + Ezmlm::Hash.subscriber( address )
   127 			self.with_safety do
   123 			self.with_safety do
   128 				next unless file.exist?
   124 				next unless file.exist?
   129 				addresses = file.read.scan( /T([^\0]+)\0/ ).flatten
   125 				addresses = file.read.scan( /T([^\0]+)\0/ ).flatten
   130 				addresses = addresses - [ address ]
   126 				addresses = addresses - [ address ]
   131 
   127 
   678 		raise "Archiving is not enabled." unless self.archived?
   674 		raise "Archiving is not enabled." unless self.archived?
   679 		return Ezmlm::List::Thread.new( self, thread_id )
   675 		return Ezmlm::List::Thread.new( self, thread_id )
   680 	end
   676 	end
   681 
   677 
   682 
   678 
   683 	### Return an Author object for the given +author_id+.
   679 	### Return an Author object for the given +author_id+, which
       
   680 	### could also be an email address.
   684 	###
   681 	###
   685 	def author( author_id )
   682 	def author( author_id )
   686 		raise "Archiving is not enabled." unless self.archived?
   683 		raise "Archiving is not enabled." unless self.archived?
       
   684 		author_id = Ezmlm::Hash.address(author_id) if author_id.index( '@' )
   687 		return Ezmlm::List::Author.new( self, author_id )
   685 		return Ezmlm::List::Author.new( self, author_id )
   688 	end
   686 	end
   689 
   687 
   690 
   688 
   691 	### Parse all thread indexes into a single array that can be used
   689 	### Parse all thread indexes into a single array that can be used
   726 
   724 
   727 
   725 
   728 	#########
   726 	#########
   729 	protected
   727 	protected
   730 	#########
   728 	#########
   731 
       
   732 	### Hash an email address, using the ezmlm algorithm for
       
   733 	### fast user lookups.  Returns the hashed integer.
       
   734 	###
       
   735 	### Older ezmlm didn't lowercase addresses, anything within the last
       
   736 	### decade did.  We're not going to worry about compatibility there.
       
   737 	###
       
   738 	### See: subhash.c in the ezmlm source.
       
   739 	###
       
   740 	def subhash( addr )
       
   741 		h = 5381
       
   742 		over = 2 ** ADDRESS_SPACE
       
   743 
       
   744 		addr = 'T' + addr.downcase
       
   745 		addr.each_char do |c|
       
   746 			h = ( h + ( h << 5 ) ) ^ c.ord
       
   747 			h = h % over if h > over # emulate integer overflow
       
   748 		end
       
   749 		return h % 53
       
   750 	end
       
   751 
       
   752 
       
   753 	### Given an email address, return the ascii hash prefix.
       
   754 	###
       
   755 	def hashchar( addr )
       
   756 		return ( self.subhash(addr) + 64 ).chr
       
   757 	end
       
   758 
       
   759 
   729 
   760 	### Just return the contents of the provided +file+, rooted
   730 	### Just return the contents of the provided +file+, rooted
   761 	### in the list directory.
   731 	### in the list directory.
   762 	###
   732 	###
   763 	def read( file )
   733 	def read( file )