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.
@@ -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: []