eyemap 0.8.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,7 @@
1
+ $:.unshift(File.dirname(File.expand_path(__FILE__)))
2
+ require 'eyemap/eyemap'
3
+ require 'eyemap/message'
4
+ require 'eyemap/folder'
5
+ require 'eyemap/exception'
6
+ require 'eyemap/search'
7
+ $:.shift
File without changes
@@ -0,0 +1,12 @@
1
+ require 'eyemap/drivers/courier_folder'
2
+
3
+ class EyeMap::Driver::Courier < EyeMap::Driver
4
+ def initialize(*args)
5
+
6
+ super(args)
7
+
8
+ self[:folder_class] = EyeMap::Folder::Courier
9
+ self[:delimiter] = '.'
10
+
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ class EyeMap::Folder::Courier < EyeMap::Folder
2
+
3
+ def delete
4
+ # courier has an interesting situation where it does not
5
+ # allow the currently selected mailbox to be deleted.
6
+ # until I'm positive this is "normal" behavior I'm going to keep it
7
+ # here.
8
+
9
+ @driver.conn.select("INBOX")
10
+ super
11
+ end
12
+
13
+ end
@@ -0,0 +1,12 @@
1
+ require 'eyemap/drivers/dovecot_folder'
2
+
3
+ class EyeMap::Driver::Dovecot < EyeMap::Driver
4
+ def initialize(*args)
5
+
6
+ super(args)
7
+
8
+ self[:folder_class] = EyeMap::Folder::Dovecot
9
+ self[:delimiter] = '/'
10
+
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ class EyeMap::Folder::Dovecot < EyeMap::Folder
2
+
3
+ def create
4
+ raise EyeMap::Exception::DriverIncapable.new("Dovecot cannot handle inferior folders.")
5
+ end
6
+
7
+ def folder(folder_name)
8
+ raise EyeMap::Exception::DriverIncapable.new("Dovecot cannot handle inferior folders.")
9
+ end
10
+
11
+ def subfolders
12
+ list = nil
13
+
14
+ if @folder_name.length > 0
15
+ list = @driver.conn.list("", "#{@folder_name}#{@driver[:delimiter]}%")
16
+ else
17
+ list = @driver.conn.list("", "%")
18
+ end
19
+
20
+ retval = []
21
+
22
+ if list
23
+ list.each do |f|
24
+ retval.push(@driver[:folder_class].
25
+ new(f.name, @driver, f.delim,
26
+ # deep copy
27
+ Marshal.load(Marshal.dump(f.attr))))
28
+ end
29
+ end
30
+
31
+ return retval
32
+ end
33
+
34
+ end
@@ -0,0 +1,8 @@
1
+ class EyeMap::Exception
2
+ class EyeMap::Exception::BadCall < Exception
3
+ end
4
+ class EyeMap::Exception::InvalidDriver < Exception
5
+ end
6
+ class EyeMap::Exception::DriverIncapable < Exception
7
+ end
8
+ end
@@ -0,0 +1,238 @@
1
+ require 'net/imap'
2
+
3
+ begin
4
+ require 'rubygems'
5
+ rescue LoadError => e
6
+ end
7
+
8
+ require 'active_support'
9
+
10
+ #
11
+ # EyeMap - an Objective interface to IMAP
12
+ #
13
+ # Those who have worked with IMAP should know that the servers that
14
+ # implement it have a world of hurt coming to them, and that's why
15
+ # they don't implement every single part of the spec right. There are
16
+ # too many SHOULD's and not enough MUST's.
17
+ #
18
+ # Anyways, our solution to this model is pretty simple. Provide
19
+ # drivers for the IMAP servers, which control their quirks, and
20
+ # present a unified interface in which the user does not have to care
21
+ # about the nasty underpinnings and can just get work done.
22
+ #
23
+ # EyeMap is a part of the Re: Mail project.
24
+ #
25
+ # To connect to an IMAP store, use the EyeMap.connect() call.
26
+ #
27
+ # To figure out what to do after you've got your connection, look at
28
+ # the methods in EyeMap::Driver.
29
+ #
30
+
31
+ class EyeMap
32
+
33
+ #
34
+ # EyeMap::Driver - Top level calls for IMAP connections.
35
+ #
36
+ # <documentation about implementing a driver goes here>
37
+ #
38
+
39
+ class Driver
40
+ protected
41
+
42
+ #
43
+ # Assign a driver capability in key/value format.
44
+ #
45
+
46
+ def []=(key, value)
47
+ @capabilities[key] = value
48
+ end
49
+
50
+ public
51
+
52
+ #
53
+ # Create a new driver object. This really shouldn't be called by
54
+ # itself, but as a super() method, as it just fills in defaults
55
+ # intended to be overridden.
56
+ #
57
+
58
+ def initialize(args)
59
+ args = args[0]
60
+
61
+ if ! args.include? :user or
62
+ ! args.include? :password or
63
+ ! args.include? :host
64
+
65
+ raise EyeMap::Exception::BadCall.new("User, Password and Host must be provided to connect")
66
+
67
+ end
68
+ @capabilities = EyeMap::Capabilities.new
69
+ self[:driver_class] = args[:driver_class]
70
+ self[:driver] = args[:driver]
71
+ self[:message_class] = EyeMap::Message
72
+ self[:folder_class] = EyeMap::Folder
73
+ self[:delimiter] = '/'
74
+ self[:user] = args[:user]
75
+ self[:password] = args[:password]
76
+ self[:host] = args[:host]
77
+ self[:auth_mech] = 'LOGIN' # for now
78
+ self[:ssl] = !!args[:ssl]
79
+ self[:verify_ssl] = args[:ssl] ? !!args[:verify_ssl] : false
80
+ self[:cert] = args[:cert]
81
+
82
+ if ! args[:port]
83
+ args[:port] = args[:ssl] ? 993 : 143
84
+ end
85
+
86
+ self[:port] = args[:port]
87
+
88
+ end
89
+
90
+ #
91
+ # Fetch a capability. Capabilities are a struct, but are presented
92
+ # in hash form. See EyeMap::Capabilities for more information.
93
+ #
94
+
95
+ def [](key)
96
+ return @capabilities[key]
97
+ end
98
+
99
+ #
100
+ # Creates a new folder with the name specified.
101
+ #
102
+
103
+ def create(folder_name)
104
+ self.conn.create(folder_name)
105
+ self[:folder_class].new(folder_name, self, self[:delimiter])
106
+ end
107
+
108
+ #
109
+ # Get an EyeMap::Folder (or derivative) object for the name of
110
+ # the folder in question. Requires the full folder name.
111
+ #
112
+
113
+ def folder(folder_name=nil)
114
+ return self[:folder_class].
115
+ new(folder_name,
116
+ self,
117
+ self[:delimiter])
118
+ end
119
+
120
+ #
121
+ # Get the underlying Net::IMAP connection.
122
+ #
123
+
124
+ def conn
125
+ @conn
126
+ end
127
+
128
+ #
129
+ # Returns true if the connection is still alive.
130
+ #
131
+
132
+ def connected?
133
+ ! @conn.disconnected?
134
+ end
135
+
136
+ #
137
+ # Initiates a connection (or reconnection) to the server.
138
+ #
139
+
140
+ def connect
141
+ if @conn and connected?
142
+ begin
143
+ @conn.authenticate("LOGIN", self[:user], self[:password]) unless
144
+ @conn.login(self[:user], self[:password])
145
+ rescue Exception => e
146
+ end
147
+
148
+ else
149
+ @conn = Net::IMAP.new(self[:host], self[:port],
150
+ self[:ssl], self[:cert],
151
+ self[:verify_ssl])
152
+ connect() # recurse
153
+ end
154
+ end
155
+
156
+ #
157
+ # Disconnect from the server
158
+ #
159
+
160
+ def disconnect
161
+ @conn.disconnect
162
+ end
163
+
164
+ end # Driver
165
+
166
+ #
167
+ # Use a driver to connect to an IMAP store.
168
+ #
169
+ # The arguments here are a collection of EyeMap::Capabilities
170
+ # items. If there are any driver-specific items, those will be noted
171
+ # for that driver. Please read the documentation for both.
172
+ #
173
+ # Capabilities are /driver/ capabilities and not IMAP capabilities in
174
+ # the traditional sense (although there are a few correlations).
175
+ #
176
+ # You will generally not work with these directly, outside of passing
177
+ # them to the EyeMap.connect() call, or if you're writing a
178
+ # driver.
179
+ #
180
+ # All items are symbols, so while not listed here, they start with a colon:
181
+ #
182
+ # * delimiter: The folder delimiter that the IMAP server uses.
183
+ # * folder_class: The class that new folder objects are created from.
184
+ # * message_class: The class that new message objects are created
185
+ # from.
186
+ # * driver_class: Calculated from 'driver', this is the class of
187
+ # the driver that is being used.
188
+ # * driver: The 'text' name of the driver, used to locate the
189
+ # driver.
190
+ # * user: The username to connect to the IMAP store with.
191
+ # * password: The password to connect to the IMAP store with.
192
+ # * host: The host of the IMAP store.
193
+ # * ssl: Set to true to use SSL to connect to the IMAP
194
+ # store.
195
+ # * auth_mech: The authentication mechanism to use.
196
+ # * verify_ssl: Verify our SSL connection?
197
+ # * port: The port to use in our IMAP connection.
198
+ # * cert: The certificate to use (SSL only)
199
+ #
200
+
201
+ def EyeMap.connect(*args)
202
+ args = args[0]
203
+
204
+ if ! args[:driver]
205
+ args[:driver] = 'auto'
206
+ end
207
+
208
+ # require the appropriate driver from the driver/ directory
209
+ begin
210
+ require "eyemap/drivers/#{args[:driver]}"
211
+ rescue LoadError => e
212
+ throw EyeMap::Exception::InvalidDriver.new("Driver '#{args[:driver]}' doesn't exist or isn't working properly.")
213
+ end
214
+
215
+ # fetch the driver's class object from the symbol table (Kernel is hte root level)
216
+ # and store it in the :driver_class argument.
217
+ args[:driver_class] = EyeMap::Driver.const_get(Inflector.camelize(args[:driver]).to_sym)
218
+
219
+ # call, connect, and return the value of the constructor
220
+ obj = args[:driver_class].new(args)
221
+
222
+ obj.connect()
223
+ return obj
224
+
225
+ end
226
+
227
+ def initialize
228
+ throw EyeMap::Exception::BadCall.new("Use EyeMap.connect() to connect to a driver")
229
+ end
230
+
231
+ end # EyeMap
232
+
233
+ EyeMap::Capabilities = Struct.new(:delimiter, :folder_class,
234
+ :message_class, :driver_class,
235
+ :driver, :user, :password,
236
+ :host, :ssl, :auth_mech,
237
+ :verify_ssl, :port,
238
+ :cert)
@@ -0,0 +1,211 @@
1
+ class EyeMap::Folder
2
+
3
+ attr_reader :properties
4
+ attr_reader :folder_name
5
+ attr_reader :driver
6
+ attr_reader :delimiter
7
+
8
+ #
9
+ # Constructor. Inheriting drivers must implement this interface.
10
+ #
11
+
12
+ def initialize(folder_name, driver, delimiter, properties={ })
13
+ @driver = driver
14
+ @folder_name = (folder_name and folder_name.length > 0) ? folder_name : "INBOX"
15
+ @folder_name.freeze
16
+ @delimiter = delimiter
17
+ @properties = properties || Hash.new
18
+ end
19
+
20
+ #
21
+ # Get a specific folder underneath this one.
22
+ #
23
+
24
+ def folder(folder_name)
25
+ self.class.new(@folder_name + @delimiter + folder_name,
26
+ @driver,
27
+ @delimiter)
28
+ end
29
+
30
+ # return the 'short name' of the folder - the name of the folder
31
+ # without anything "lower" than the delimiter (including the
32
+ # delimiter itself).
33
+
34
+ def short_name
35
+ return @folder_name.sub(/.*?([^#{@delimiter}]+)$/, '\1')
36
+ end
37
+
38
+ #
39
+ # Get the list of subfolders for this folder.
40
+ #
41
+
42
+ def subfolders
43
+ list = @driver.conn.list(@folder_name, "%")
44
+
45
+ retval = []
46
+
47
+ if list
48
+ list.each do |f|
49
+ retval.push(@driver[:folder_class].
50
+ new(f.name, @driver, f.delim,
51
+ # deep copy
52
+ Marshal.load(Marshal.dump(f.attr))))
53
+ end
54
+ end
55
+
56
+ return retval
57
+ end
58
+
59
+ #
60
+ # Returns all the folders below this folder in a series of ::Folder objects.
61
+ #
62
+
63
+ def folder_tree
64
+ skel = { :folder => nil, :children => [] }
65
+ retval = []
66
+ subfolders.each do |sub|
67
+ folder = skel.dup
68
+ folder[:folder] = sub
69
+ folder[:children] = folder[:folder].folder_tree
70
+ retval.push(folder)
71
+ end
72
+
73
+ return retval
74
+ end
75
+
76
+ def total
77
+ @driver.conn.status(@folder_name, ["MESSAGES"])["MESSAGES"]
78
+ end
79
+
80
+ def recent
81
+ @driver.conn.status(@folder_name, ["RECENT"])["RECENT"]
82
+ end
83
+
84
+ def unseen
85
+ @driver.conn.status(@folder_name, ["UNSEEN"])["UNSEEN"]
86
+ end
87
+
88
+ def find_message(message_id)
89
+ @driver.conn.examine(@folder_name)
90
+
91
+ if ! message_id.kind_of? Numeric
92
+ raise EyeMap::Exception::BadCall.new("Message ID must be numeric")
93
+ end
94
+
95
+ messages = @driver.conn.fetch(message_id, "UID")
96
+ uid = nil
97
+
98
+ uid = messages[0].attr["UID"] if messages[0]
99
+
100
+ return @driver[:message_class].new(uid, @folder_name, @driver) if uid
101
+
102
+ return nil
103
+ end
104
+
105
+ #
106
+ # Search through a range of message ids (or an array of them).
107
+ #
108
+ # pass 'nil' to search for all messages (the default)
109
+ #
110
+
111
+ def find_messages(message_ids=nil)
112
+ # this next line of code would be great for AOP
113
+ @driver.conn.examine(@folder_name)
114
+ messages = []
115
+
116
+ if message_ids.kind_of? Range
117
+ query = "#{message_ids.first}:#{message_ids.last}"
118
+ elsif message_ids.kind_of? Array
119
+ query = message_ids.join(",")
120
+ elsif ! message_ids
121
+ query = "ALL"
122
+ elsif message_ids.kind_of? Numeric
123
+ return [find_message(message_ids)]
124
+ else
125
+ raise EyeMap::Exception::BadCall.new("Invalid query parameters")
126
+ end
127
+
128
+ @driver.conn.uid_search([query]).each do |uid|
129
+ messages.push(@driver[:message_class].
130
+ new(uid, @folder_name, @driver))
131
+ end
132
+
133
+ return messages
134
+
135
+ end
136
+
137
+ #
138
+ # Get a list of valid messages for the current folder, that contain
139
+ # the number of items per page and reflect the current "page" for
140
+ # that inbox: page = (num_per_page * 10) - 9 -> (num_per_page *
141
+ # 10). The reason for this is that IMAP message id's start with 1.
142
+ #
143
+
144
+ def paginate_headers(page, num_per_page=10)
145
+ @driver.conn.examine(@folder_name)
146
+ uids = @driver.conn.uid_search(["ALL"])
147
+
148
+ # get out now if we don't have any messages.
149
+ unless uids and uids.length > 0
150
+ return []
151
+ end
152
+
153
+ # for our query, the high and low mid's, respective to the array.
154
+ low_mid = (page * num_per_page) - (num_per_page - 1)
155
+ high_mid = page * num_per_page
156
+
157
+ # no messages this high
158
+ if uids[low_mid].nil?
159
+ return []
160
+ end
161
+
162
+ if uids[high_mid].nil?
163
+ high_mid = -1
164
+ end
165
+
166
+ return uids[low_mid..high_mid]
167
+
168
+ end
169
+
170
+ #
171
+ # Permanently deletes all messages marked with the :Deleted flag
172
+ #
173
+
174
+ def expunge
175
+ @driver.conn.examine(@folder_name)
176
+ @driver.conn.expunge()
177
+ end
178
+
179
+ #
180
+ # Creates a new folder underneath this one with the name specified.
181
+ #
182
+ # It will prepend the current folder information and the delimiter
183
+ # to the folder name passed.
184
+ #
185
+
186
+ def create(folder_name)
187
+ new_folder = @folder_name + @delimiter + folder_name
188
+ @driver.conn.create(new_folder)
189
+ self.class.new(new_folder, @driver, @delimiter)
190
+ end
191
+
192
+ #
193
+ # Delete this folder.
194
+ #
195
+
196
+ def delete
197
+ @driver.conn.delete(@folder_name)
198
+ end
199
+
200
+ #
201
+ # Add a new message to this folder.
202
+ #
203
+ # Our message in this case is just a block of text. This does not use a message object.
204
+ #
205
+
206
+ def add_message(message_text)
207
+ @driver.conn.append(@folder_name, message_text.gsub(/[^\r]\n/, "\r\n"),
208
+ [], Time.now)
209
+ end
210
+
211
+ end
@@ -0,0 +1,99 @@
1
+ class EyeMap::Message
2
+
3
+ def initialize(uid, folder_name, driver)
4
+ @uid = uid
5
+ @folder_name = folder_name
6
+ @driver = driver
7
+ end
8
+
9
+ def envelope
10
+ @driver.conn.uid_fetch(@uid, "ENVELOPE")[0].attr["ENVELOPE"]
11
+ end
12
+
13
+ def move(folder)
14
+ copy(folder)
15
+ delete
16
+ end
17
+
18
+ def copy(folder)
19
+ @driver.conn.uid_copy(@uid, folder.folder_name)
20
+ end
21
+
22
+ #
23
+ # Fetches headers and sanitizes various fields from the IMAP message.
24
+ # Takes an array of header names.
25
+ #
26
+
27
+ def headers(headers)
28
+ @driver.conn.examine(@folder_name)
29
+
30
+ fetch_command = "BODY.PEEK[HEADER.FIELDS ("
31
+
32
+ fetch_fields = ""
33
+ headers.each do |h|
34
+ fetch_fields << h << " "
35
+ end
36
+
37
+ fetch_command << fetch_fields.strip << ")]"
38
+
39
+ return_headers = Hash.new
40
+
41
+ # send the command - Net::IMAP returns the value as
42
+ # BODY[HEADER.FIELDS instead of
43
+ # BODY.PEEK[HEADER.FIELDS, so we compensate for that.
44
+ body = @driver.conn.uid_fetch(@uid, fetch_command)[0].
45
+ attr["BODY[HEADER.FIELDS (" + fetch_fields.upcase.strip + ")]"]
46
+
47
+ # pull out the fields we just nabbed
48
+
49
+ headers.each do |h|
50
+ r = Regexp.new %r!(?:^|\r\n)(#{h}:.+?)\r\n!i
51
+ header = nil
52
+ while m = r.match(body)
53
+ match = m[1]
54
+ match.sub! /^#{h}:\s/, ""
55
+
56
+ if header
57
+ header = [header] unless header.kind_of? Array
58
+ header.push(match)
59
+ else
60
+ header = match
61
+ end
62
+
63
+ body.sub! /(?:^|\r\n)(#{h}:.+?)\r\n/i, ""
64
+ end
65
+
66
+ return_headers[h] = header
67
+ end
68
+
69
+ return return_headers
70
+
71
+ end
72
+
73
+ #
74
+ # Delete a message from the mail store.
75
+ #
76
+ # Unfortunately, as soon as you close this mailbox or select
77
+ # another, the messages that were marked deleted will be expunged
78
+ # due to the nature of the IMAP protocol.
79
+ #
80
+ # Instead of using this call at all, it would be wiser to move your
81
+ # messages to delete to a separate folder and then delete them there
82
+ # when you are ready.
83
+ #
84
+
85
+ def delete()
86
+ @driver.conn.select(@folder_name)
87
+ @driver.conn.uid_store(@uid, "+FLAGS", [:Deleted])
88
+ end
89
+
90
+ #
91
+ # This does the opposite of delete()
92
+ #
93
+
94
+ def undelete()
95
+ @driver.conn.select(@folder_name)
96
+ @driver.conn.uid_store(@uid, "-FLAGS", [:Deleted])
97
+ end
98
+
99
+ end
@@ -0,0 +1,2 @@
1
+ class EyeMap::Search
2
+ end
@@ -0,0 +1,160 @@
1
+ require 'test/unit'
2
+ require 'eyemap'
3
+
4
+ Dir['lib/eyemap/drivers/*.rb'].each do |x|
5
+ load x
6
+ end
7
+
8
+ $USER = ENV['EYEMAP_USER']
9
+ $PASS = ENV['EYEMAP_PASS']
10
+ $HOST = ENV['EYEMAP_HOST']
11
+ $DOVECOT_PORT = ENV['EYEMAP_DOVECOT_PORT']
12
+ $COURIER_PORT = ENV['EYEMAP_COURIER_PORT']
13
+
14
+ #
15
+ # Most of the tests just work against dovecot and courier right now.
16
+ #
17
+ # Ideally, we should have tests which work against each connection in their
18
+ # own test file with a driver or something that communicates with a inner series
19
+ # of tests. These tests would assert general functionality while maintaining their
20
+ # specific-ness.
21
+ #
22
+
23
+ module GenericTestsMain
24
+
25
+ def test_folder
26
+ assert_kind_of(EyeMap::Folder, @conn.folder)
27
+ end
28
+
29
+ def test_capabilities
30
+ assert_kind_of(Class, @conn[:driver_class])
31
+ assert_kind_of(Class, @conn[:message_class])
32
+ assert_kind_of(Class, @conn[:folder_class])
33
+ assert_kind_of(String, @conn[:driver])
34
+ end
35
+
36
+ def test_connections
37
+ assert(@conn.connected?)
38
+ assert_nothing_raised() { @conn.disconnect }
39
+ assert(!@conn.connected?)
40
+ assert_nothing_raised() { @conn.connect }
41
+ assert(@conn.connected?)
42
+ end
43
+
44
+ end
45
+
46
+ module GenericTestsFolder
47
+
48
+ def test_subfolders_and_create_and_delete
49
+ if @conn[:driver_class] == EyeMap::Driver::Dovecot
50
+ assert_nothing_raised() { @conn.folder.subfolders }
51
+ assert_equal([], @conn.folder.subfolders)
52
+ assert_raise(EyeMap::Exception::DriverIncapable) do
53
+ @conn.folder.folder("monkey")
54
+ end
55
+ assert_raise(EyeMap::Exception::DriverIncapable) do
56
+ @conn.folder.create
57
+ end
58
+ else
59
+ folders = nil
60
+ assert_nothing_raised() { folders = @conn.folder.subfolders }
61
+
62
+ if folders.collect { |f| f.short_name }.include? "monkey"
63
+ assert_nothing_raised() { @conn.folder.folder("monkey").delete }
64
+ else
65
+ assert_raise(Net::IMAP::NoResponseError) { @conn.folder.folder("monkey").delete }
66
+ end
67
+
68
+ assert_kind_of(EyeMap::Folder, @conn.folder.create("monkey"))
69
+ assert_nothing_raised() { @conn.folder.folder("monkey").delete }
70
+ end
71
+ end # test_subfolders_and_create_and_delete
72
+
73
+ def test_short_name
74
+ assert_equal(@conn.folder("INBOX").folder_name, @conn.folder("INBOX").short_name)
75
+
76
+ unless @conn[:driver_class] == EyeMap::Driver::Dovecot
77
+ assert_nothing_raised() { @conn.folder("INBOX").create("Monkey") }
78
+ assert_equal("Monkey", @conn.folder("INBOX").folder("Monkey").short_name)
79
+ assert_nothing_raised() { @conn.folder("INBOX").folder("Monkey").delete }
80
+ end
81
+ end # test_short_name
82
+
83
+ end
84
+
85
+ module GenericTestsMessage
86
+ def test_message
87
+ message = ""
88
+ assert_nothing_raised() do
89
+ f = File.open("test/test_message")
90
+ tmp_irs = $/
91
+ $/ = nil
92
+ message << f.readline
93
+ $/ = tmp_irs
94
+ f.close
95
+ end
96
+
97
+ folder = nil
98
+ new_message = nil
99
+
100
+ assert_nothing_raised() do
101
+ if @conn[:driver_class] == EyeMap::Driver::Dovecot
102
+ folder = @conn.create("EyeMap")
103
+ else
104
+ folder = @conn.folder.create("EyeMap")
105
+ end
106
+ end
107
+
108
+ assert_nothing_raised() do
109
+ @conn.folder.add_message(message.gsub(/(?:[^\r])\n/, "\r\n"))
110
+ # type change: String -> EyeMap::Message
111
+ message = @conn.folder.find_message(@conn.folder.total)
112
+ message.copy(folder)
113
+ new_message = @conn.folder(folder.folder_name).find_message(@conn.folder(folder.folder_name).total)
114
+ end
115
+
116
+ assert_equal(new_message.headers(%w(Subject)), message.headers(%w(Subject)))
117
+
118
+ assert_nothing_raised() do
119
+ new_message.delete
120
+ message.move(folder)
121
+ new_message = @conn.folder(folder.folder_name).find_message(@conn.folder(folder.folder_name).total)
122
+ end
123
+
124
+ assert_equal(new_message.headers(%w(Subject)), message.headers(%w(Subject)))
125
+
126
+ assert_nothing_raised() do
127
+ new_message.delete
128
+ folder.delete
129
+ end
130
+ end
131
+ end
132
+
133
+ class TestDovecotEyeMap < Test::Unit::TestCase
134
+
135
+ def setup
136
+ @conn = EyeMap.connect(:driver => 'dovecot',
137
+ :user => $USER,
138
+ :password => $PASS,
139
+ :port => $DOVECOT_PORT,
140
+ :host => $HOST)
141
+ end
142
+
143
+ include GenericTestsMain
144
+ include GenericTestsFolder
145
+ include GenericTestsMessage
146
+ end
147
+
148
+ class TestCourierEyeMap < Test::Unit::TestCase
149
+ def setup
150
+ @conn = EyeMap.connect(:driver => 'courier',
151
+ :port => $COURIER_PORT,
152
+ :user => $USER,
153
+ :password => $PASS,
154
+ :host => $HOST)
155
+ end
156
+
157
+ include GenericTestsMain
158
+ include GenericTestsFolder
159
+ include GenericTestsMessage
160
+ end
@@ -0,0 +1,6 @@
1
+ From: testmonkey@example.org
2
+ To: testmonkey@example.org
3
+ Subject: ActiveIMAP Test Message
4
+
5
+ hello there
6
+
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
4
+ name: eyemap
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.8.0
7
+ date: 2007-03-26 00:00:00 -07:00
8
+ summary: Provides a Driver Architecture and Framework for talking to IMAP servers
9
+ require_paths:
10
+ - lib
11
+ email: erik@hollensbe.org
12
+ homepage:
13
+ rubyforge_project: eyemap
14
+ description:
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Erik Hollensbe
31
+ files:
32
+ - lib/eyemap.rb
33
+ - lib/eyemap/exception.rb
34
+ - lib/eyemap/eyemap.rb
35
+ - lib/eyemap/folder.rb
36
+ - lib/eyemap/message.rb
37
+ - lib/eyemap/search.rb
38
+ - lib/eyemap/drivers/auto.rb
39
+ - lib/eyemap/drivers/courier.rb
40
+ - lib/eyemap/drivers/courier_folder.rb
41
+ - lib/eyemap/drivers/dovecot.rb
42
+ - lib/eyemap/drivers/dovecot_folder.rb
43
+ - test/activeimap.rb
44
+ - test/test_message
45
+ test_files: []
46
+
47
+ rdoc_options: []
48
+
49
+ extra_rdoc_files: []
50
+
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ requirements: []
56
+
57
+ dependencies:
58
+ - !ruby/object:Gem::Dependency
59
+ name: activesupport
60
+ version_requirement:
61
+ version_requirements: !ruby/object:Gem::Version::Requirement
62
+ requirements:
63
+ - - ">"
64
+ - !ruby/object:Gem::Version
65
+ version: 0.0.0
66
+ version: