eyemap 0.8.0

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