equal
deleted
inserted
replaced
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 ) |