Compare commits
No commits in common. "48143595bfb022a5f58e3724df7a4201711102fc" and "28948e513fb714bd2598ccc07039393197fed429" have entirely different histories.
48143595bf
...
28948e513f
25 changed files with 108 additions and 87 deletions
|
|
@ -1 +1 @@
|
|||
2.5
|
||||
2.4
|
||||
|
|
|
|||
14
README.md
14
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.
|
||||
|
|
|
|||
11
Rakefile
11
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 <mahlon@martini.nu>',
|
||||
'Michael Granger <ged@faeriemud.org>',
|
||||
'Jeremiah Jordan <jeremiah.m.jordan@gmail.com>'
|
||||
]
|
||||
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|
|
||||
|
|
|
|||
18
USAGE.md
18
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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
* callseq:
|
||||
* 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:
|
||||
* callseq:
|
||||
* 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
20
lib/ezmlm.rb
20
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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+.
|
||||
###
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue