ezmlm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []