diff --git a/.ruby-version b/.ruby-version index 95e3ba8..6b4950e 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.5 +2.4 diff --git a/README.md b/README.md index 38a91a5..80523ea 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,7 @@ - # Ruby-Ezmlm - * home: - * http://code.martini.nu/ruby-ezmlm - * mirrors: - * https://github.com/mahlonsmith/ruby-ezmlm - * https://hg.sr.ht/~mahlon/ruby-ezmlm - * Ezmlm: - * http://untroubled.org/ezmlm/ +code +: https://bitbucket.org/mahlon/Ruby-Ezmlm ## Authors @@ -24,6 +18,8 @@ manager for use with the Qmail MTA, and the messages contained therein. (The -idx provides an extended feature set over the original ezmlm environment.) +http://untroubled.org/ezmlm/ + This was tested against ezmlm-idx 7.2.2. *Strong recommendation*: Create your lists with archiving (-a) and @@ -50,7 +46,7 @@ be a generic interface for parsing and browsing list content. ## Limitations -This library doesn't create new lists from scratch. Use `ezmlm-make`. +This library doesn't create new lists from scratch. Use ezmlm-make. This library is designed to only work with lists stored on disk (the default), not the SQL backends. diff --git a/Rakefile b/Rakefile index 537c19e..da4a692 100644 --- a/Rakefile +++ b/Rakefile @@ -23,7 +23,6 @@ $version = ( LIBDIR + "#{PROJECT}.rb" ).read.split(/\n/). select{|line| line =~ /VERSION =/}.first.match(/([\d|.]+)/)[1] task :default => [ :spec, :docs, :package ] -task :spec => [ :compile ] ######################################################################## @@ -34,8 +33,12 @@ require 'rubygems' require 'rubygems/package_task' spec = Gem::Specification.new do |s| s.email = 'mahlon@martini.nu' - s.homepage = 'https://bitbucket.org/mahlon/ruby-ezmlm' - s.authors = [ 'Mahlon E. Smith', 'Michael Granger', 'Jeremiah Jordan' ] + s.homepage = 'https://bitbucket.org/mahlon/Ruby-Ezmlm' + s.authors = [ + 'Mahlon E. Smith ', + 'Michael Granger ', + 'Jeremiah Jordan ' + ] s.platform = Gem::Platform::RUBY s.summary = "Interact with Ezmlm-IDX mailing lists." s.name = PROJECT @@ -81,7 +84,7 @@ begin rdoc.rdoc_dir = 'docs' rdoc.main = "README.rdoc" # rdoc.options = [ '-f', 'fivefish' ] - rdoc.rdoc_files = [ 'lib', *FileList['ext/**.c'], *FileList['*.rdoc'], *FileList['*.md'] ] + rdoc.rdoc_files = [ 'lib', *FileList['ext/*/*.c'], *FileList['*.rdoc'] ] end RDoc::Task.new do |rdoc| diff --git a/USAGE.md b/USAGE.md index 6fc0059..5b5c9f7 100644 --- a/USAGE.md +++ b/USAGE.md @@ -10,14 +10,14 @@ Examples -------- -### Print the list address for all lists in a directory: +*Print the list address for all lists in a directory*: Ezmlm.each_list( '/lists' ) do |list| puts list.address end -### Check if I'm subscribed to a list, and if so, unsubscribe: +*Check if I'm subscribed to a list, and if so, unsubscribe*: (You don't really have to check first, subscribe and unsubscribe are idempotent.) @@ -31,14 +31,14 @@ idempotent.) puts "The list now has %d subscribers!" % [ list.subscribers.size ] -### Iterate over the subscriber list: +*Iterate over the subscriber list*: list.subscribers.each do |subscriber| # ... end -### Make the list moderated, and add a moderator: +*Make the list moderated, and add a moderator*: list.moderated = true list.add_moderator( 'mahlon@martini.nu' ) @@ -48,7 +48,7 @@ All other list behavior tunables operate in a similar fashion, see RDoc for details. -### Archiving! +*Archiving!* All of the archival pieces take advantage of Ezmlm-IDX extensions. If you want to use these features, you'll want to enable archiving @@ -67,12 +67,12 @@ rebuild the necessary files - afterwards, they are kept up to date automatically. -### How many messages are in the archive?: +*How many messages are in the archive?*: list.message_count #=> 123 -### Fetch message number 100 from the archive: +*Fetch message number 100 from the archive*: message = list.message( 100 ) or abort "No such message." @@ -96,7 +96,7 @@ Mikel Lindsaar (https://github.com/mikel/mail). See its documentation for specifics. -### Iterate over messages in a specific thread: +*Iterate over messages in a specific thread*: Messages know what thread they belong to. Once you have a thread object from a message, it is an enumerable. Iterate or sort on it using @@ -110,7 +110,7 @@ Threads are also aware of who participated in the conversation, via the 'authors' and 'each_author' methods. -### Iterate over messages from a specific author: +*Iterate over messages from a specific author:* Messages know who authored them. Once you have an author object from a message, it is an enumerable. Iterate or sort on it using standard Ruby diff --git a/ext/ezmlm/hash/hash.c b/ext/ezmlm/hash/hash.c index 3a31b2a..ff7cd94 100644 --- a/ext/ezmlm/hash/hash.c +++ b/ext/ezmlm/hash/hash.c @@ -13,7 +13,6 @@ * */ -static void surf(unsigned int out[8],const unsigned int in[12],const unsigned int seed[32]) { unsigned int t[12]; unsigned int x; unsigned int sum = 0; @@ -33,7 +32,6 @@ void surf(unsigned int out[8],const unsigned int in[12],const unsigned int seed[ } } -static void surfpcs_init(surfpcs *s,const unsigned int k[32]) { int i; @@ -43,7 +41,6 @@ void surfpcs_init(surfpcs *s,const unsigned int k[32]) s->todo = 0; } -static void surfpcs_add(surfpcs *s,const char *x,unsigned int n) { int i; @@ -62,7 +59,6 @@ void surfpcs_add(surfpcs *s,const char *x,unsigned int n) } } -static void surfpcs_addlc(surfpcs *s,const char *x,unsigned int n) /* modified from surfpcs_add by case-independence and skipping ' ' & '\t' */ { @@ -88,7 +84,6 @@ void surfpcs_addlc(surfpcs *s,const char *x,unsigned int n) } } -static void surfpcs_out(surfpcs *s,unsigned char h[32]) { int i; @@ -100,7 +95,6 @@ void surfpcs_out(surfpcs *s,unsigned char h[32]) for (i = 0;i < 32;++i) h[i] = outdata[end[i]]; } -static void makehash(const char *indata,unsigned int inlen,char *hash) /* makes hash[COOKIE=20] from stralloc *indata, ignoring case and */ /* SPACE/TAB */ @@ -118,7 +112,6 @@ void makehash(const char *indata,unsigned int inlen,char *hash) hash[i] = 'a' + (h[i] & 15); } -static unsigned int subhashb(const char *s,long len) { unsigned long h; @@ -128,7 +121,6 @@ unsigned int subhashb(const char *s,long len) return h % 53; } -static unsigned int subhashs(const char *s) { return subhashb(s,strlen(s)); @@ -140,7 +132,7 @@ unsigned int subhashs(const char *s) /* - * call-seq: + * call­seq: * Ezmlm::Hash.address( email ) -> String * * Call the Surf hashing function on an +email+ address, returning @@ -149,7 +141,7 @@ unsigned int subhashs(const char *s) * the '<' character.) * */ -static VALUE +VALUE address( VALUE klass, VALUE email ) { char hash[20]; char *input; @@ -166,14 +158,14 @@ address( VALUE klass, VALUE email ) { /* - * call-seq: + * call­seq: * Ezmlm::Hash.subscriber( address ) -> String * * Call the subscriber hashing function on an email +address+, returning * the index character referring to the file containing subscriber presence. * */ -static VALUE +VALUE subscriber( VALUE klass, VALUE email ) { unsigned int prefix; diff --git a/ext/ezmlm/hash/hash.h b/ext/ezmlm/hash/hash.h index 9200428..9b4c15e 100644 --- a/ext/ezmlm/hash/hash.h +++ b/ext/ezmlm/hash/hash.h @@ -27,19 +27,19 @@ static const unsigned int littleendian[8] = { #define data ((unsigned char *) s->in) #define outdata ((unsigned char *) s->out) -static void surf( unsigned int out[8], const unsigned int in[12], const unsigned int seed[32] ); -static void surfpcs_init( surfpcs *s, const unsigned int k[32] ); -static void surfpcs_add( surfpcs *s, const char *x,unsigned int n ); -static void surfpcs_addlc( surfpcs *s, const char *x,unsigned int n ); -static void surfpcs_out( surfpcs *s, unsigned char h[32] ); +extern void surf( unsigned int out[8], const unsigned int in[12], const unsigned int seed[32] ); +extern void surfpcs_init( surfpcs *s, const unsigned int k[32] ); +extern void surfpcs_add( surfpcs *s, const char *x,unsigned int n ); +extern void surfpcs_addlc( surfpcs *s, const char *x,unsigned int n ); +extern void surfpcs_out( surfpcs *s, unsigned char h[32] ); #endif #ifndef SUBHASH_H #define SUBHASH_H -static unsigned int subhashs(const char *s); -static unsigned int subhashb(const char *s,long len); +unsigned int subhashs(const char *s); +unsigned int subhashb(const char *s,long len); #define subhashsa(SA) subhashb((SA)->s,(SA)->len) #endif diff --git a/lib/ezmlm.rb b/lib/ezmlm.rb index 1c2a911..9500728 100644 --- a/lib/ezmlm.rb +++ b/lib/ezmlm.rb @@ -1,6 +1,5 @@ # vim: set nosta noet ts=4 sw=4: -require 'pathname' # A Ruby interface to the ezmlm-idx mailing list system. # @@ -10,11 +9,24 @@ require 'pathname' # puts "\"%s\" <%s>" % [ list.name, list.address ] # end # +# +# == Version +# +# $Id$ +# +#--- +# +# Please see the file LICENSE in the base directory for licensing details. +# + +require 'pathname' + + +### Toplevel namespace module module Ezmlm - # $Id$ # Package version - VERSION = '1.1.2' + VERSION = '1.0.0' # Suck in the components. # @@ -35,7 +47,7 @@ module Ezmlm def find_directories( listsdir ) listsdir = Pathname.new( listsdir ) return Pathname.glob( listsdir + '*' ).sort.select do |entry| - entry.directory? && ( entry + 'ezmlmrc' ).exist? + entry.directory? && ( entry + 'mailinglist' ).exist? end end diff --git a/lib/ezmlm/list.rb b/lib/ezmlm/list.rb index fc9814e..d88eec9 100644 --- a/lib/ezmlm/list.rb +++ b/lib/ezmlm/list.rb @@ -1,19 +1,27 @@ #!/usr/bin/ruby # vim: set nosta noet ts=4 sw=4: + +# A Ruby interface to a single Ezmlm-idx mailing list directory. +# +# list = Ezmlm::List.new( '/path/to/listdir' ) +# +# +# == Version +# +# $Id$ +# +#--- + require 'pathname' require 'time' require 'etc' require 'ezmlm' unless defined?( Ezmlm ) -# A Ruby interface to a single Ezmlm-idx mailing list directory. -# -# list = Ezmlm::List.new( '/path/to/listdir' ) -# -#--- +### A Ruby interface to an ezmlm-idx mailing list directory +### class Ezmlm::List - # $Id: list.rb,v a89d91d4b157 2017/06/23 17:54:26 mahlon $ # Valid subdirectories/sections for subscriptions. SUBSCRIPTION_DIRS = %w[ deny mod digest allow ] @@ -24,7 +32,7 @@ class Ezmlm::List ### def initialize( listdir ) listdir = Pathname.new( listdir ) unless listdir.is_a?( Pathname ) - unless listdir.directory? && ( listdir + 'ezmlmrc' ).exist? + unless listdir.directory? && ( listdir + 'mailinglist' ).exist? raise ArgumentError, "%p doesn't appear to be an ezmlm-idx list." % [ listdir.to_s ] end @listdir = listdir @@ -69,7 +77,7 @@ class Ezmlm::List ### Returns +true+ if +address+ is a subscriber to this list. ### def include?( addr, section: nil ) - addr = addr.downcase + addr.downcase! file = self.subscription_dir( section ) + Ezmlm::Hash.subscriber( addr ) return false unless file.exist? return file.read.scan( /T([^\0]+)\0/ ).flatten.include?( addr ) @@ -90,7 +98,7 @@ class Ezmlm::List def subscribe( *addr, section: nil ) addr.each do |address| next unless address.index( '@' ) - address = address.downcase + address.downcase! file = self.subscription_dir( section ) + Ezmlm::Hash.subscriber( address ) self.with_safety do @@ -116,7 +124,7 @@ class Ezmlm::List ### def unsubscribe( *addr, section: nil ) addr.each do |address| - address = address.downcase + address.downcase! file = self.subscription_dir( section ) + Ezmlm::Hash.subscriber( address ) self.with_safety do @@ -663,16 +671,6 @@ class Ezmlm::List end - ### Return a Time object for the last activity on the list, or nil - ### if archiving is disabled or there are no posts. - ### - def last_activity - file = self.listdir + 'archnum' - return unless file.exist? - return file.stat.mtime - end - - ### Parse all thread indexes into a single array that can be used ### as a lookup table. ### @@ -823,11 +821,13 @@ class Ezmlm::List ### def with_safety( &block ) home = self.homedir - home.chmod( home.stat.mode | 01000 ) # enable sticky + mode = home.stat.mode + + home.chmod( mode | 01000 ) # enable sticky yield ensure - home.chmod( home.stat.mode & ~01000 ) # disable sticky + home.chmod( mode ) end end # class Ezmlm::List diff --git a/lib/ezmlm/list/author.rb b/lib/ezmlm/list/author.rb index d3257d2..561e517 100644 --- a/lib/ezmlm/list/author.rb +++ b/lib/ezmlm/list/author.rb @@ -1,9 +1,6 @@ #!/usr/bin/ruby # vim: set nosta noet ts=4 sw=4: -require 'pathname' -require 'ezmlm' unless defined?( Ezmlm ) - # A collection of messages authored from a unique user. # @@ -14,9 +11,20 @@ require 'ezmlm' unless defined?( Ezmlm ) # author.name #=> "Help - navigate on interface?" # author.first.date.to_s #=> "2017-05-07T14:55:05-07:00" # +# +# == Version +# +# $Id$ +# #--- + +require 'pathname' +require 'ezmlm' unless defined?( Ezmlm ) + + +### A collection of messages for a specific author. +### class Ezmlm::List::Author - # $Id$ include Enumerable ### Instantiate a new list of messages given diff --git a/lib/ezmlm/list/message.rb b/lib/ezmlm/list/message.rb index cee6abf..77e2d6e 100644 --- a/lib/ezmlm/list/message.rb +++ b/lib/ezmlm/list/message.rb @@ -1,9 +1,6 @@ #!/usr/bin/ruby # vim: set nosta noet ts=4 sw=4: -require 'pathname' -require 'ezmlm' unless defined?( Ezmlm ) -require 'mail' # An individual list message. # @@ -15,9 +12,20 @@ require 'mail' # This class passes all heavy lifting to the Mail::Message library. # Please see it for specifics on usage. # +# == Version +# +# $Id$ +# #--- + +require 'pathname' +require 'ezmlm' unless defined?( Ezmlm ) +require 'mail' + + +### A Ruby interface to an individual list message. +### class Ezmlm::List::Message - # $Id$ ### Instantiate a new messag from a +list+ and a +message_number+. ### diff --git a/lib/ezmlm/list/thread.rb b/lib/ezmlm/list/thread.rb index 79576ad..e154a61 100644 --- a/lib/ezmlm/list/thread.rb +++ b/lib/ezmlm/list/thread.rb @@ -1,9 +1,6 @@ #!/usr/bin/ruby # vim: set nosta noet ts=4 sw=4: -require 'pathname' -require 'ezmlm' unless defined?( Ezmlm ) - # A collection of messages for a specific archive thread. # @@ -11,9 +8,20 @@ require 'ezmlm' unless defined?( Ezmlm ) # thread.subject #=> "Help - navigate on interface?" # thread.first.date.to_s #=> "2017-05-07T14:55:05-07:00" # +# +# == Version +# +# $Id$ +# #--- + +require 'pathname' +require 'ezmlm' unless defined?( Ezmlm ) + + +### A collection of messages for a specific archive thread. +### class Ezmlm::List::Thread - # $Id$ include Enumerable ### Instantiate a new thread of messages given diff --git a/spec/data/testlist/allow/subscribers/.placeholder b/spec/data/testlist/allow/subscribers/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/data/testlist/bounce/.placeholder b/spec/data/testlist/bounce/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/data/testlist/deny/subscribers/.placeholder b/spec/data/testlist/deny/subscribers/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/data/testlist/digest/bounce/.placeholder b/spec/data/testlist/digest/bounce/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/data/testlist/digest/subscribers/.placeholder b/spec/data/testlist/digest/subscribers/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/data/testlist/mod/accepted/.placeholder b/spec/data/testlist/mod/accepted/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/data/testlist/mod/pending/.placeholder b/spec/data/testlist/mod/pending/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/data/testlist/mod/rejected/.placeholder b/spec/data/testlist/mod/rejected/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/data/testlist/mod/subscribers/.placeholder b/spec/data/testlist/mod/subscribers/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/data/testlist/mod/unconfirmed/.placeholder b/spec/data/testlist/mod/unconfirmed/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/data/testlist/subscribers/.placeholder b/spec/data/testlist/subscribers/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/data/testlist/text/.placeholder b/spec/data/testlist/text/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/spec/ezmlm/list_spec.rb b/spec/ezmlm/list_spec.rb index 3f0d4b4..08d3619 100644 --- a/spec/ezmlm/list_spec.rb +++ b/spec/ezmlm/list_spec.rb @@ -236,10 +236,6 @@ describe Ezmlm::List do expect( list.digest_count ).to eq( 10 ) end - it 'knows the date of the most recent posting' do - expect( list.last_activity ).to be_a( Time ) - end - it 'can set a new digest message count' do list.digest_count = 25 expect( list.digest_count ).to eq( 25 ) diff --git a/spec/spec_helpers.rb b/spec/spec_helpers.rb index 13db8b7..6fe76e4 100644 --- a/spec/spec_helpers.rb +++ b/spec/spec_helpers.rb @@ -3,7 +3,6 @@ require 'simplecov' if ENV['COVERAGE'] require 'rspec' require 'fileutils' -require 'tmpdir' require_relative '../lib/ezmlm' @@ -31,8 +30,7 @@ module SpecHelpers ### Create a copy of a fresh listdir into /tmp. ### def make_listdir - dirname = "%s/%s.%d.%0.4f" % [ - Dir.tmpdir, + dirname = "/tmp/%s.%d.%0.4f" % [ 'ezmlm_list', Process.pid, (Time.now.to_f % 3600),