ezmlm 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/ext/ezmlm/hash/extconf.rb +5 -0
- data/ext/ezmlm/hash/hash.c +193 -0
- data/ext/ezmlm/hash/hash.h +46 -0
- data/lib/ezmlm.rb +65 -0
- data/lib/ezmlm/list.rb +834 -0
- data/lib/ezmlm/list/author.rb +119 -0
- data/lib/ezmlm/list/message.rb +121 -0
- data/lib/ezmlm/list/thread.rb +117 -0
- metadata +86 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
|
4
|
+
|
5
|
+
# A collection of messages authored from a unique user.
|
6
|
+
#
|
7
|
+
# Note that Ezmlm uses the "real name" part of an address
|
8
|
+
# to identify an author.
|
9
|
+
#
|
10
|
+
# author = Ezmlm::List::Author.new( list, 'acgcbmbmeapgpfckcdol' )
|
11
|
+
# author.name #=> "Help - navigate on interface?"
|
12
|
+
# author.first.date.to_s #=> "2017-05-07T14:55:05-07:00"
|
13
|
+
#
|
14
|
+
#
|
15
|
+
# == Version
|
16
|
+
#
|
17
|
+
# $Id: author.rb,v 23c7f5c8ee39 2017/05/16 20:58:34 mahlon $
|
18
|
+
#
|
19
|
+
#---
|
20
|
+
|
21
|
+
require 'pathname'
|
22
|
+
require 'ezmlm' unless defined?( Ezmlm )
|
23
|
+
|
24
|
+
|
25
|
+
### A collection of messages for a specific author.
|
26
|
+
###
|
27
|
+
class Ezmlm::List::Author
|
28
|
+
include Enumerable
|
29
|
+
|
30
|
+
### Instantiate a new list of messages given
|
31
|
+
### a +list+ and an +author_id+.
|
32
|
+
###
|
33
|
+
def initialize( list, author_id )
|
34
|
+
raise ArgumentError, "Unknown list object." unless list.respond_to?( :listdir )
|
35
|
+
raise ArgumentError, "Malformed Author ID." unless author_id =~ /^\w{20}$/
|
36
|
+
raise "Archiving is not enabled." unless list.archived?
|
37
|
+
|
38
|
+
@list = list
|
39
|
+
@id = author_id
|
40
|
+
@messages = nil
|
41
|
+
|
42
|
+
self.load_index
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# The list object this message is stored in.
|
47
|
+
attr_reader :list
|
48
|
+
|
49
|
+
# The author's identifier.
|
50
|
+
attr_reader :id
|
51
|
+
|
52
|
+
# The author's name.
|
53
|
+
attr_reader :name
|
54
|
+
|
55
|
+
# An array of messages this author has sent.
|
56
|
+
attr_reader :messages
|
57
|
+
|
58
|
+
# An array of threads this author has participated in.
|
59
|
+
attr_reader :threads
|
60
|
+
|
61
|
+
|
62
|
+
### Enumerable API: Lazy load each message ID as a
|
63
|
+
### Ezmlm::List::Message, yielding it to the block.
|
64
|
+
###
|
65
|
+
def each
|
66
|
+
self.load_index # refresh for any updates since object was created
|
67
|
+
self.messages.each do |id|
|
68
|
+
yield Ezmlm::List::Message.new( self.list, id )
|
69
|
+
end
|
70
|
+
end
|
71
|
+
alias_method :each_message, :each
|
72
|
+
|
73
|
+
|
74
|
+
### Lazy load each thread ID as a Ezmlm::List::Thread, yielding it to the block.
|
75
|
+
###
|
76
|
+
def each_thread
|
77
|
+
self.load_index # refresh for any updates since object was created
|
78
|
+
self.threads.each do |id|
|
79
|
+
yield Ezmlm::List::Thread.new( self.list, id )
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
#########
|
85
|
+
protected
|
86
|
+
#########
|
87
|
+
|
88
|
+
### Parse the author index into an array of Messages.
|
89
|
+
###
|
90
|
+
def load_index
|
91
|
+
@messages = []
|
92
|
+
@threads = []
|
93
|
+
|
94
|
+
path = self.author_path
|
95
|
+
raise "Unknown author: %p" % [ self.id ] unless path.exist?
|
96
|
+
|
97
|
+
path.open( 'r', encoding: Encoding::ISO8859_1 ) do |fh|
|
98
|
+
fh.each_line.with_index do |line, i|
|
99
|
+
if i.zero?
|
100
|
+
@name = line.match( /^\w+ (.+)/ )[1]
|
101
|
+
else
|
102
|
+
match = line.match( /^(\d+):\d+:(\w+) / ) or next
|
103
|
+
self.messages << match[1].to_i
|
104
|
+
self.threads << match[2]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
### Return the path on disk for the author index.
|
112
|
+
###
|
113
|
+
def author_path
|
114
|
+
prefix = self.id[ 0 .. 1 ]
|
115
|
+
hash = self.id[ 2 .. -1 ]
|
116
|
+
return self.list.listdir + 'archive' + 'authors' + prefix + hash
|
117
|
+
end
|
118
|
+
|
119
|
+
end # class Ezmlm::List::Author
|
@@ -0,0 +1,121 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
|
4
|
+
|
5
|
+
# An individual list message.
|
6
|
+
#
|
7
|
+
# message = Ezmlm::List::Message.new( list, 24 )
|
8
|
+
# message.thread #=> (a thread object this message is part of)
|
9
|
+
# message.from #=> ["jalon.hermann@example.com"]
|
10
|
+
# puts message.to_s #=> (raw email)
|
11
|
+
#
|
12
|
+
# This class passes all heavy lifting to the Mail::Message library.
|
13
|
+
# Please see it for specifics on usage.
|
14
|
+
#
|
15
|
+
# == Version
|
16
|
+
#
|
17
|
+
# $Id: message.rb,v 23c7f5c8ee39 2017/05/16 20:58:34 mahlon $
|
18
|
+
#
|
19
|
+
#---
|
20
|
+
|
21
|
+
require 'pathname'
|
22
|
+
require 'ezmlm' unless defined?( Ezmlm )
|
23
|
+
require 'mail'
|
24
|
+
|
25
|
+
|
26
|
+
### A Ruby interface to an individual list message.
|
27
|
+
###
|
28
|
+
class Ezmlm::List::Message
|
29
|
+
|
30
|
+
### Instantiate a new messag from a +list+ and a +message_number+.
|
31
|
+
###
|
32
|
+
def initialize( list, message_number=0 )
|
33
|
+
raise ArgumentError, "Unknown list object." unless list.respond_to?( :listdir )
|
34
|
+
raise ArgumentError, "Invalid message number (impossible)" if message_number < 1
|
35
|
+
raise "Archiving is not enabled." unless list.archived?
|
36
|
+
raise ArgumentError, "Invalid message number (out of list bounds)" if message_number > list.message_count
|
37
|
+
|
38
|
+
@list = list
|
39
|
+
@id = message_number
|
40
|
+
@post = self.load_message
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# The list object this message is stored in.
|
45
|
+
attr_reader :list
|
46
|
+
|
47
|
+
# The list message delivery identifier.
|
48
|
+
attr_reader :id
|
49
|
+
|
50
|
+
# The Mail::Message object for this post.
|
51
|
+
attr_reader :post
|
52
|
+
|
53
|
+
|
54
|
+
### Return the thread object this message is
|
55
|
+
### a member of.
|
56
|
+
###
|
57
|
+
def thread
|
58
|
+
unless @thread_id
|
59
|
+
idx = self.list.index
|
60
|
+
@thread_id = idx[ self.id - 1 ][ :thread ]
|
61
|
+
end
|
62
|
+
|
63
|
+
return Ezmlm::List::Thread.new( self.list, @thread_id )
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
### Return the author object this message is
|
68
|
+
### a member of.
|
69
|
+
###
|
70
|
+
def author
|
71
|
+
unless @author_id
|
72
|
+
idx = self.list.index
|
73
|
+
@author_id = idx[ self.id - 1 ][ :author ]
|
74
|
+
end
|
75
|
+
|
76
|
+
return Ezmlm::List::Author.new( self.list, @author_id )
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
### Render the message as a string.
|
81
|
+
###
|
82
|
+
def to_s
|
83
|
+
return self.post.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
### Provide implicit arrays (Mail::Message does not.)
|
87
|
+
###
|
88
|
+
def to_ary
|
89
|
+
return [ self.post ]
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
### Pass all unknown methods to the underlying Mail::Message object.
|
94
|
+
###
|
95
|
+
def method_missing( meth, *args )
|
96
|
+
return self.post.method( meth ).call( *args )
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
#########
|
101
|
+
protected
|
102
|
+
#########
|
103
|
+
|
104
|
+
### Parse the message into a Mail::Message.
|
105
|
+
###
|
106
|
+
def load_message
|
107
|
+
path = self.message_path
|
108
|
+
raise "Unable to determine message path: %p" % [ path ] unless path.exist?
|
109
|
+
return Mail.read( path.to_s )
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
### Return the path on disk for the message.
|
114
|
+
###
|
115
|
+
def message_path
|
116
|
+
hashdir = self.id / 100
|
117
|
+
message = "%02d" % [ self.id % 100 ]
|
118
|
+
return self.list.listdir + 'archive' + hashdir.to_s + message.to_s
|
119
|
+
end
|
120
|
+
|
121
|
+
end # class Ezmlm::List::Message
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
|
4
|
+
|
5
|
+
# A collection of messages for a specific archive thread.
|
6
|
+
#
|
7
|
+
# thread = Ezmlm::List::Thread.new( list, 'acgcbmbmeapgpfckcdol' )
|
8
|
+
# thread.subject #=> "Help - navigate on interface?"
|
9
|
+
# thread.first.date.to_s #=> "2017-05-07T14:55:05-07:00"
|
10
|
+
#
|
11
|
+
#
|
12
|
+
# == Version
|
13
|
+
#
|
14
|
+
# $Id: thread.rb,v 23c7f5c8ee39 2017/05/16 20:58:34 mahlon $
|
15
|
+
#
|
16
|
+
#---
|
17
|
+
|
18
|
+
require 'pathname'
|
19
|
+
require 'ezmlm' unless defined?( Ezmlm )
|
20
|
+
|
21
|
+
|
22
|
+
### A collection of messages for a specific archive thread.
|
23
|
+
###
|
24
|
+
class Ezmlm::List::Thread
|
25
|
+
include Enumerable
|
26
|
+
|
27
|
+
### Instantiate a new thread of messages given
|
28
|
+
### a +list+ and a +thread_id+.
|
29
|
+
###
|
30
|
+
def initialize( list, thread_id )
|
31
|
+
raise ArgumentError, "Unknown list object." unless list.respond_to?( :listdir )
|
32
|
+
raise ArgumentError, "Malformed Thread ID." unless thread_id =~ /^\w{20}$/
|
33
|
+
raise "Archiving is not enabled." unless list.archived?
|
34
|
+
|
35
|
+
@list = list
|
36
|
+
@id = thread_id
|
37
|
+
@subject = nil
|
38
|
+
@messages = nil
|
39
|
+
|
40
|
+
self.load_thread
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# The list object this message is stored in.
|
45
|
+
attr_reader :list
|
46
|
+
|
47
|
+
# The thread's identifier.
|
48
|
+
attr_reader :id
|
49
|
+
|
50
|
+
# The subject line of the thread.
|
51
|
+
attr_reader :subject
|
52
|
+
|
53
|
+
# An array of member messages.
|
54
|
+
attr_reader :messages
|
55
|
+
|
56
|
+
# An array of member authors.
|
57
|
+
attr_reader :authors
|
58
|
+
|
59
|
+
|
60
|
+
### Enumerable API: Lazy load each message ID as a
|
61
|
+
### Ezmlm::List::Message, yielding it to the block.
|
62
|
+
###
|
63
|
+
def each
|
64
|
+
self.load_thread # refresh for any thread updates since object was created
|
65
|
+
self.messages.each do |id|
|
66
|
+
yield Ezmlm::List::Message.new( self.list, id )
|
67
|
+
end
|
68
|
+
end
|
69
|
+
alias_method :each_message, :each
|
70
|
+
|
71
|
+
|
72
|
+
### Lazy load each author ID as a Ezmlm::List::Author, yielding it
|
73
|
+
### to the block.
|
74
|
+
###
|
75
|
+
def each_author
|
76
|
+
self.load_thread # refresh for any thread updates since object was created
|
77
|
+
self.authors.each do |id|
|
78
|
+
yield Ezmlm::List::Author.new( self.list, id )
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
#########
|
84
|
+
protected
|
85
|
+
#########
|
86
|
+
|
87
|
+
### Parse the subject index into an array of Messages.
|
88
|
+
###
|
89
|
+
def load_thread
|
90
|
+
@messages = []
|
91
|
+
@authors = []
|
92
|
+
path = self.thread_path
|
93
|
+
raise "Unknown thread: %p" % [ self.id ] unless path.exist?
|
94
|
+
|
95
|
+
path.open( 'r', encoding: Encoding::ISO8859_1 ) do |fh|
|
96
|
+
fh.each_line.with_index do |line, i|
|
97
|
+
if i.zero?
|
98
|
+
@subject = line.match( /^\w+ (.+)/ )[1]
|
99
|
+
else
|
100
|
+
match = line.match( /^(\d+):\d+:(\w+) / ) or next
|
101
|
+
self.messages << match[1].to_i
|
102
|
+
self.authors << match[2]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
### Return the path on disk for the thread index.
|
110
|
+
###
|
111
|
+
def thread_path
|
112
|
+
prefix = self.id[ 0 .. 1 ]
|
113
|
+
hash = self.id[ 2 .. -1 ]
|
114
|
+
return self.list.listdir + 'archive' + 'subjects' + prefix + hash
|
115
|
+
end
|
116
|
+
|
117
|
+
end # class Ezmlm::List::Thread
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ezmlm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mahlon E. Smith <mahlon@martini.nu>
|
8
|
+
- Michael Granger <ged@faeriemud.org>
|
9
|
+
- Jeremiah Jordan <jeremiah.m.jordan@gmail.com>
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2017-05-16 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: mail
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.6'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '2.6'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: rake-compiler
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '1.0'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '1.0'
|
43
|
+
description: |
|
44
|
+
This is a ruby interface for interacting with ezmlm-idx, an email list
|
45
|
+
manager for use with the Qmail MTA, and the messages contained therein.
|
46
|
+
(The -idx provides an extended feature set over the original ezmlm
|
47
|
+
environment.)
|
48
|
+
email: mahlon@martini.nu
|
49
|
+
executables: []
|
50
|
+
extensions:
|
51
|
+
- ext/ezmlm/hash/extconf.rb
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- ext/ezmlm/hash/extconf.rb
|
55
|
+
- ext/ezmlm/hash/hash.c
|
56
|
+
- ext/ezmlm/hash/hash.h
|
57
|
+
- lib/ezmlm.rb
|
58
|
+
- lib/ezmlm/list.rb
|
59
|
+
- lib/ezmlm/list/author.rb
|
60
|
+
- lib/ezmlm/list/message.rb
|
61
|
+
- lib/ezmlm/list/thread.rb
|
62
|
+
homepage: https://bitbucket.org/mahlon/Ruby-Ezmlm
|
63
|
+
licenses:
|
64
|
+
- BSD-3-Clause
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '2.1'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 2.6.8
|
83
|
+
signing_key:
|
84
|
+
specification_version: 4
|
85
|
+
summary: Interact with Ezmlm-IDX mailing lists.
|
86
|
+
test_files: []
|