sup 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sup might be problematic. Click here for more details.

data/doc/TODO CHANGED
@@ -1,29 +1,53 @@
1
1
  for 0.0.8
2
2
  ---------
3
- create attachments
4
- forward attachments
5
- warnings: top-posting, missing attachment
6
- maildir
3
+ x nice little startup config program
4
+ x bugfix: triggering a pageup when cursor scrolling up jumps to the
5
+ bottom of the page rather than the next line
6
+ x bugfix: final logging messages to stdout?
7
+ x bugfix: mbox directory shouldn't generate an exception, just an error
8
+ x bugfix: m in thread-view-mode when a person is not selected should open up a
9
+ blank compose-mode rather than do nothing
10
+ x bugfix: stars on messages with blue backgrounds still have green bgs
11
+ x ferret upgrade script (dump & restore)
12
+ x bugfix: mark messages as read immediately when t-v-m is opened
13
+ x compose in thread-view-mode auto-fills in person
14
+ x bugfix: 'N' in thread-view-mode (expand only new messages) crashes
15
+ x bugfix: detect source corruption at startup
16
+ x maildir
17
+ x bugfix: single-line messages come empty upon reply
7
18
 
8
- for 0.0.9
19
+ next release
20
+ ------------
21
+ _ bugfix: when one new message comes into an imap folder, we don't
22
+ catch it until a reload (sometimes?)
23
+ _ bugfix: add new message counts until keypress
24
+ _ bugfix: attachment filenames sometimes not detected (filename=)
25
+ _ split out threading & message chunk parsing to a separate library
26
+
27
+ near future
9
28
  ---------
10
- select all, starred, to me, etc
11
- undo
12
- use Net::SMTP
13
- gmail
29
+ _ Net::SMTP support (cuz I'm going to need it soon)
30
+ _ create attachments
31
+ _ forward attachments
32
+ _ select all, starred, to me, etc
33
+ _ undo
34
+ _ gmail
35
+ _ warnings: top-posting, missing attachment, ruby-talk:XXXX detection
36
+ _ mboxz, mboxbz
14
37
 
15
38
  future
16
39
  ------
40
+ search results: highlight relevant snippets and open to relevant portion of thread
41
+ email address to name mapping needs some work. automatic email addresses (noreply@...) are often assigned to something screwy.
17
42
  decode RFC 2047 ("encoded word") headers
18
43
  - see: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/101949, http://dev.rubyonrails.org/ticket/6807
19
44
  swappable keymappings
20
45
  within-buffer search
21
46
  bugfix: when returning from a shelling out, ncurses is crazy
22
- bugfix: miscellaneous weirdnesses in buffer line editing
47
+ more control character support in buffer line editing
23
48
  wide character support
24
49
  i18n support
25
50
  batch deletion
26
- support for message-content modules such as ruby-talk:XXXXX detection
27
51
  tab completion on labels, contacts
28
52
  contact selector in edit-message-mode
29
53
  maybe: filters
@@ -33,6 +57,7 @@ pop
33
57
  be able to mark individual messages as spam in thread-view-mode
34
58
  toggle wrapping
35
59
  maybe: de-archived messages auto-added to inbox
60
+ prune old entries from contacts.txt so that it doesn't arbitrarily
36
61
 
37
62
  done
38
63
  ----
@@ -82,3 +107,4 @@ x highlighting/different color stuff
82
107
  x config: your email, sendmail, etc
83
108
  x status: to/from_you, cc_you_others
84
109
  x status: new/not, important
110
+ x bugfix: miscellaneous weirdnesses in buffer line editing
@@ -26,12 +26,21 @@ for messages or view your inbox, Sup talks only to the index (stored
26
26
  locally on disk). When you view a thread, Sup requests the full
27
27
  content of all the messages from the source.
28
28
 
29
- So let's add some sources to Sup's source list. Run 'sup-add' with
30
- a URI pointing to an email source. The URI should be of the form:
31
- - mbox://path/to/a/filename, for an mbox file on disk. (You can also
32
- just provide the filename).
33
- - imap://imap.server/folder or imaps://secure.imap.server/folder for
34
- an IMAP folder. (Leave the folder blank for INBOX.)
29
+ The easiest way to set up all your sources is to run "sup-config".
30
+ This will interactively walk you through some basic configuration,
31
+ prompt you for all the sources you need, and optionally import
32
+ messages from them.
33
+
34
+ sup-config works by calling sup-add and sup-sync with the right
35
+ arguments. If you have a non-trivial setup, you may need to run
36
+ sup-add and sup-sync manually.
37
+
38
+ You can manually add a source to Sup's source list by running
39
+ 'sup-add' with a URI pointing to it. The URI should be of the form:
40
+ - mbox://path/to/a/filename, for an mbox file on disk.
41
+ - maildir://path/to/a/filename, for an maildir directory on disk.
42
+ - imap://imap.server/folder for an unsecure IMAP folder.
43
+ - imaps://secure.imap.server/folder for a secure IMAP folder.
35
44
  - mbox+ssh://remote.machine/path/to/a/filename for a remote mbox file.
36
45
 
37
46
  Before you add the source, you need make two decisions. The first is
@@ -46,39 +55,33 @@ inbox, but will be found when you search. (Your inbox in Sup is, by
46
55
  definition, the set of all all non-archived messages). Specify
47
56
  --archive to automatically archive all messages from the source. This
48
57
  is useful for sources that contain, for example, high-traffic mailing
49
- lists that you don't want "polluting" your inbox.
58
+ lists that you don't want polluting your inbox.
50
59
 
51
60
  If Sup requires account information, e.g. for IMAP servers and remote
52
61
  mbox files, sup-add will ask for it.
53
62
 
54
63
  Now that you've added the source, let's import all the current
55
- messages from it, by running sup-import with the source URI. You can
64
+ messages from it, by running sup-sync with the source URI. You can
56
65
  specify --archive to automatically archive all messages in this
57
66
  import; typically you'll want to specify this for every source you
58
67
  import except your actual inbox. You can also specify --read to mark
59
68
  all imported messages as read; the default is to preserve the
60
69
  read/unread status from the source.
61
70
 
62
- sup-import will now load all the messages from the source into the
71
+ sup-sync will now load all the messages from the source into the
63
72
  index. Depending on the size of the source, this may take a
64
73
  while. Don't panic! It's a one-time process.
65
74
 
66
- We're almost ready. But before we run 'sup' again, take a moment to
67
- edit your ~/.sup/config.yaml file. Replace "Your Name Here" with your
68
- name, "your.email.here@domain.tld" with your email address, and fill
69
- in your .signature file if you choose. You can also set the default
70
- editor.
71
-
72
75
  Ok, now run 'sup'. You should see the most recent unarchived messages
73
76
  appear in your inbox. Congratulations, you've got Sup working!
74
77
 
75
- If you're coming from the world of traditional email, there are a
78
+ If you're coming from the world of traditional MUAs, there are a
76
79
  couple differences you should be aware of at this point. First, Sup
77
80
  does not use folders. Instead, you organize and find messages by a
78
81
  combination of search and labels (knowns as 'tags' everywhere else in
79
- the world). I like to say that 95% of the functionality of folders is
82
+ the world). I claim that 95% of the functionality of folders is
80
83
  superceded by a quick, easy-to-access, and powerful search mechanism,
81
- and the other 5% by labels. (The Sup statement of philosophy has a
84
+ and the other 5% by labels. (The Sup philosophical treatise has a
82
85
  little more on this.)
83
86
 
84
87
  Search and labels are an integral part of Sup because in Sup, rather
@@ -96,18 +99,18 @@ life can be easier than that if you just trust the search engine, and
96
99
  use labels judiciously for things that are too hard to find with
97
100
  search.
98
101
 
99
- But enough chit-chat! Let's take a look at your inbox. You'll see that
100
- Sup groups messages together into threads: each line in the inbox is a
101
- thread, and the number in parentheses is the number of messages in
102
- that thread. (If there's no number in parens, it means there's just
103
- one message.) In Sup, you rarely operate on an individual message. The
104
- idea is that you rarely want to operate on a message independent of
105
- its context; you typically want to view, archive, kill, or label all
106
- the messages in a thread at one time.
102
+ Now let's take a look at your inbox. You'll see that Sup groups
103
+ messages together into threads: each line in the inbox is a thread,
104
+ and the number in parentheses is the number of messages in that
105
+ thread. (If there's no number, there's just one message.) In Sup, most
106
+ operations are on threads, not individual messages. The idea is that
107
+ you rarely want to operate on a message independent of its context.
108
+ You typically want to view, archive, kill, or label all the messages
109
+ in a thread at one time.
107
110
 
108
111
  Use the up and down arrows to highlight a thread. ('j' and 'k' do the
109
112
  same thing, and 'J' and 'K' will scroll the whole window. Even the
110
- left and right arrow keys work!) By default, Sup only loads as many
113
+ left and right arrow keys work.) By default, Sup only loads as many
111
114
  threads as it takes to fill the window; if you'd like to load more,
112
115
  press 'M'. You can hit tab to cycle between only threads with new
113
116
  messages.
@@ -122,19 +125,18 @@ to expand or collapse all messages or 'N' to expand only the new
122
125
  messages. You'll also notice that Sup hides quoted text and
123
126
  signatures. If you highlight a particular hidden chunk, you can press
124
127
  enter to expand it, or you can press 'o' to toggle every hidden chunk
125
- in a particular message. (Don't worry about remembering all these
126
- things---you can hit '?' to see the full list of keyboard commands at
127
- any point.)
128
+ in a particular message. (You can hit '?' to see the full list of
129
+ keyboard commands at any point.)
128
130
 
129
131
  A few other useful commands while viewing a thread. Press 'd' to
130
132
  toggle a detailed header fpr a message. If you've scrolled too far to
131
133
  the right, press '[' to jump all the way to the left. Finally, you can
132
134
  press 'n' and 'p' to jump forward and backward between open
133
- messages. (I find that very useful!)
135
+ messages, aligning the display as necessary.
134
136
 
135
137
  Now press 'x' to kill the thread view buffer. You should see the inbox
136
138
  again. If you don't, you can cycle through the buffers by pressing
137
- 'b', or you can press 'A' to see a list of all buffers and simply
139
+ 'b', or you can press 'B' to see a list of all buffers and simply
138
140
  select the inbox.
139
141
 
140
142
  There are many operations you can perform on threads beyond viewing
@@ -190,11 +192,11 @@ details on more sophisticated queries (date ranges, "within n words",
190
192
  etc.)
191
193
 
192
194
  At this point, you're well on your way to figuring out all the cool
193
- things Sup can do. By repeated applications of the '?' key, see if you
195
+ things Sup can do. By repeated application of the '?' key, see if you
194
196
  can figure out how to:
195
197
  - List some recent contacts
196
198
  - Easily search for all mail from a recent contact
197
- - Easily search for all mail from several recent contacts!
199
+ - Easily search for all mail from several recent contacts
198
200
  - Add someone to your address book
199
201
  - Postpone a message (i.e., save a draft)
200
202
  - Quickly re-edit a just-saved draft message
@@ -203,11 +205,10 @@ can figure out how to:
203
205
 
204
206
  There's one last thing to be aware of when using Sup: how it interacts
205
207
  with other email programs. Sup stores data about messages in the
206
- index---information necessary for searching, and message state---but
207
- doesn't duplicate the message contents themselves. The messages remain
208
- on the source. If the index and the source every fall out of sync,
209
- e.g. due to another email client modifying the source, then Sup will
210
- be unable to operate on that source.
208
+ index, but doesn't duplicate the message contents themselves. The
209
+ messages remain on the source. If the index and the source every fall
210
+ out of sync, e.g. due to another email client modifying the source,
211
+ then Sup will be unable to operate on that source.
211
212
 
212
213
  For example, for mbox files, Sup stores a byte offset into the file
213
214
  for each message. If a message deleted from that file by another
@@ -216,8 +217,8 @@ offsets will be wrong.
216
217
 
217
218
  That's the bad news. The good news is that Sup is fairly good at being
218
219
  able to detect this type of situation, and fixing it is just a matter
219
- of running sup-import --rebuild on the source. Sup will even tell you
220
- how to invoke sup-import when it detects a problem. This is a
220
+ of running sup-sync --changed on the source. Sup will even tell you
221
+ how to invoke sup-sync when it detects a problem. This is a
221
222
  complication you will almost certainly run in to if you use both Sup
222
223
  and another MUA on the same source, so it's good to be aware of it.
223
224
 
data/lib/sup.rb CHANGED
@@ -13,7 +13,7 @@ class Object
13
13
  end
14
14
 
15
15
  module Redwood
16
- VERSION = "0.0.7"
16
+ VERSION = "0.0.8"
17
17
 
18
18
  BASE_DIR = ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
19
19
  CONFIG_FN = File.join(BASE_DIR, "config.yaml")
@@ -93,9 +93,52 @@ module Redwood
93
93
  Redwood::LabelManager.save
94
94
  Redwood::ContactManager.save
95
95
  Redwood::PersonManager.save
96
+ Redwood::BufferManager.deinstantiate!
96
97
  end
97
98
 
98
- module_function :register_yaml, :save_yaml_obj, :load_yaml_obj, :start, :finish
99
+ ## not really a good place for this, so I'll just dump it here.
100
+ def report_broken_sources opts={}
101
+ return unless BufferManager.instantiated?
102
+
103
+ broken_sources = Index.usual_sources.select { |s| s.error.is_a? FatalSourceError }
104
+ unless broken_sources.empty?
105
+ BufferManager.spawn "Broken source notification", TextMode.new(<<EOM), opts
106
+ Source error notification
107
+ -------------------------
108
+
109
+ Hi there. It looks like one or more message sources is reporting
110
+ errors. Until this is corrected, messages from these sources cannot
111
+ be viewed, and new messages will not be detected.
112
+
113
+ #{broken_sources.map { |s| "Source: " + s.to_s + "\n Error: " + s.error.message.wrap(70).join("\n ")}.join('\n\n')}
114
+ EOM
115
+ #' stupid ruby-mode
116
+ end
117
+
118
+ desynced_sources = Index.usual_sources.select { |s| s.error.is_a? OutOfSyncSourceError }
119
+ unless desynced_sources.empty?
120
+ BufferManager.spawn "Out-of-sync source notification", TextMode.new(<<EOM), opts
121
+ Out-of-sync source notification
122
+ -------------------------------
123
+
124
+ Hi there. It looks like one or more sources has fallen out of sync
125
+ with my index. This can happen when you modify these sources with
126
+ other email clients. (Sorry, I don't play well with others.)
127
+
128
+ Until this is corrected, messages from these sources cannot be viewed,
129
+ and new messages will not be detected. Luckily, this is easy to correct!
130
+
131
+ #{desynced_sources.map do |s|
132
+ "Source: " + s.to_s +
133
+ "\n Error: " + s.error.message.wrap(70).join("\n ") +
134
+ "\n Fix: sup-sync --changed #{s.to_s}"
135
+ end}
136
+ EOM
137
+ #' stupid ruby-mode
138
+ end
139
+ end
140
+
141
+ module_function :register_yaml, :save_yaml_obj, :load_yaml_obj, :start, :finish, :report_broken_sources
99
142
  end
100
143
 
101
144
  ## set up default configuration file
@@ -105,8 +148,8 @@ else
105
148
  $config = {
106
149
  :accounts => {
107
150
  :default => {
108
- :name => "Your Name Here",
109
- :email => "your.email.here@domain.tld",
151
+ :name => "Sup Rocks",
152
+ :email => "sup-rocks@reading-my-emails",
110
153
  :alternates => [],
111
154
  :sendmail => "/usr/sbin/sendmail -oem -ti",
112
155
  :signature => File.join(ENV["HOME"], ".signature")
@@ -127,6 +170,7 @@ require "sup/update"
127
170
  require "sup/message"
128
171
  require "sup/source"
129
172
  require "sup/mbox"
173
+ require "sup/maildir"
130
174
  require "sup/imap"
131
175
  require "sup/person"
132
176
  require "sup/account"
@@ -50,6 +50,7 @@ module Redwood
50
50
  class Buffer
51
51
  attr_reader :mode, :x, :y, :width, :height, :title
52
52
  bool_reader :dirty
53
+ bool_accessor :force_to_top
53
54
 
54
55
  def initialize window, mode, width, height, opts={}
55
56
  @w = window
@@ -57,6 +58,7 @@ class Buffer
57
58
  @dirty = true
58
59
  @focus = false
59
60
  @title = opts[:title] || ""
61
+ @force_to_top = opts[:force_to_top] || false
60
62
  @x, @y, @width, @height = 0, 0, width, height
61
63
  end
62
64
 
@@ -156,18 +158,33 @@ class BufferManager
156
158
 
157
159
  def raise_to_front buf
158
160
  raise ArgumentError, "buffer not on stack: #{buf.inspect}" unless @buffers.member? buf
161
+
159
162
  @buffers.delete buf
160
- @buffers.push buf
161
- focus_on buf
163
+ if @buffers.length > 0 && @buffers.last.force_to_top?
164
+ @buffers.insert(-2, buf)
165
+ else
166
+ @buffers.push buf
167
+ focus_on buf
168
+ end
162
169
  @dirty = true
163
170
  end
164
171
 
172
+ ## we reset force_to_top when rolling buffers. this is so that the
173
+ ## human can actually still move buffers around, while still
174
+ ## programmatically being able to pop stuff up in the middle of
175
+ ## drawing a window without worrying about covering it up.
176
+ ##
177
+ ## if we ever start calling roll_buffers programmatically, we will
178
+ ## have to change this. but it's not clear that we will ever actually
179
+ ## do that.
165
180
  def roll_buffers
181
+ @buffers.last.force_to_top = false
166
182
  raise_to_front @buffers.first
167
183
  end
168
184
 
169
185
  def roll_buffers_backwards
170
186
  return unless @buffers.length > 1
187
+ @buffers.last.force_to_top = false
171
188
  raise_to_front @buffers[@buffers.length - 2]
172
189
  end
173
190
 
@@ -258,14 +275,14 @@ class BufferManager
258
275
  ## w = Ncurses::WINDOW.new(height, width, (opts[:top] || 0),
259
276
  ## (opts[:left] || 0))
260
277
  w = Ncurses.stdscr
261
- b = Buffer.new w, mode, width, height, :title => realtitle
278
+ b = Buffer.new w, mode, width, height, :title => realtitle, :force_to_top => (opts[:force_to_top] || false)
262
279
  mode.buffer = b
263
280
  @name_map[realtitle] = b
281
+
282
+ @buffers.unshift b
264
283
  if opts[:hidden]
265
- @buffers.unshift b
266
284
  focus_on b unless @focus_buf
267
285
  else
268
- @buffers.push b
269
286
  raise_to_front b
270
287
  end
271
288
  b
@@ -12,7 +12,7 @@ class DraftManager
12
12
 
13
13
  def self.source_name; "sup://drafts"; end
14
14
  def self.source_id; 9999; end
15
- def new_source; @source = DraftLoader.new; end
15
+ def new_source; @source = Recoverable.new DraftLoader.new; end
16
16
 
17
17
  def write_draft
18
18
  offset = @source.gen_offset
@@ -22,7 +22,7 @@ class DraftManager
22
22
  my_message = nil
23
23
  @source.each do |thisoffset, theselabels|
24
24
  m = Message.new :source => @source, :source_info => thisoffset, :labels => theselabels
25
- Index.add_message m
25
+ Index.sync_message m
26
26
  UpdateManager.relay self, :add, m
27
27
  my_message = m if thisoffset == offset
28
28
  end
@@ -2,6 +2,7 @@ require 'uri'
2
2
  require 'net/imap'
3
3
  require 'stringio'
4
4
  require 'time'
5
+ require 'rmail'
5
6
 
6
7
  ## fucking imap fucking sucks. what the FUCK kind of committee of
7
8
  ## dunces designed this shit.
@@ -35,7 +36,6 @@ class IMAP < Source
35
36
  ## upon these errors we'll try to rereconnect a few times
36
37
  RECOVERABLE_ERRORS = [ Errno::EPIPE, Errno::ETIMEDOUT ]
37
38
 
38
- attr_reader_cloned :labels
39
39
  attr_accessor :username, :password
40
40
 
41
41
  def initialize uri, username, password, last_idate=nil, usual=true, archived=false, id=nil
@@ -52,7 +52,6 @@ class IMAP < Source
52
52
  @ids = []
53
53
  @last_scan = nil
54
54
  @labels = [:unread]
55
- @labels << :inbox unless archived?
56
55
  @labels << mailbox.intern unless mailbox =~ /inbox/i
57
56
  @mutex = Mutex.new
58
57
  end
@@ -64,6 +63,18 @@ class IMAP < Source
64
63
  x.nil? || x.empty? ? 'INBOX' : x
65
64
  end
66
65
  def ssl?; @parsed_uri.scheme == 'imaps' end
66
+
67
+ def check
68
+ ids =
69
+ @mutex.synchronize do
70
+ unsynchronized_scan_mailbox
71
+ @ids
72
+ end
73
+
74
+ start = ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}."
75
+ end
76
+
77
+ ## is this necessary? TODO: remove maybe
67
78
  def == o; o.is_a?(IMAP) && o.uri == self.uri && o.username == self.username; end
68
79
 
69
80
  def load_header id
@@ -89,7 +100,7 @@ class IMAP < Source
89
100
  synchronized :raw_full_message
90
101
 
91
102
  def connect
92
- return if broken? || @imap
103
+ return if @imap
93
104
  safely { } # do nothing!
94
105
  end
95
106
  synchronized :connect
@@ -121,12 +132,12 @@ class IMAP < Source
121
132
  @ids
122
133
  end
123
134
 
124
- start = ids.index(cur_offset || start_offset) or die_from "Unknown message id #{cur_offset || start_offset}.", :suggest_rebuild => true # couldn't find the most recent email
135
+ start = ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}."
125
136
 
126
137
  start.upto(ids.length - 1) do |i|
127
138
  id = ids[i]
128
139
  self.cur_offset = id
129
- yield id, labels
140
+ yield id, @labels.clone
130
141
  end
131
142
  end
132
143
 
@@ -196,26 +207,6 @@ private
196
207
  @say_id = nil
197
208
  end
198
209
 
199
- def die_from e, opts={}
200
- @imap = nil
201
-
202
- message =
203
- case e
204
- when Exception
205
- "Error while #{opts[:while]}: #{e.message.chomp} (#{e.class.name})."
206
- when String
207
- e
208
- end
209
-
210
- message += " It is likely that messages have been deleted from this IMAP mailbox. Please run sup-import --rebuild #{to_s} to correct this problem." if opts[:suggest_rebuild]
211
-
212
- self.broken_msg = message
213
- Redwood::log message
214
- BufferManager.flash "Error communicating with IMAP server. See log for details." if BufferManager.instantiated?
215
- raise SourceError, message
216
- end
217
-
218
- ## build a fake unique id
219
210
  def make_id imap_stuff
220
211
  # use 7 digits for the size. why 7? seems nice.
221
212
  msize, mdate = imap_stuff.attr['RFC822.SIZE'] % 10000000, Time.parse(imap_stuff.attr["INTERNALDATE"])
@@ -223,13 +214,12 @@ private
223
214
  end
224
215
 
225
216
  def get_imap_fields id, *fields
226
- raise SourceError, broken_msg if broken?
227
- imap_id = @imap_ids[id] or die_from "Unknown message id #{id}.", :suggest_rebuild => true
217
+ imap_id = @imap_ids[id] or raise OutOfSyncSourceError, "Unknown message id #{id}"
228
218
 
229
219
  retried = false
230
220
  results = safely { @imap.fetch imap_id, (fields + ['RFC822.SIZE', 'INTERNALDATE']).uniq }.first
231
221
  got_id = make_id results
232
- die_from "IMAP message mismatch: requested #{id}, got #{got_id}.", :suggest_rebuild => true unless got_id == id
222
+ raise OutOfSyncSourceError, "IMAP message mismatch: requested #{id}, got #{got_id}." unless got_id == id
233
223
 
234
224
  fields.map { |f| results.attr[f] }
235
225
  end
@@ -250,8 +240,8 @@ private
250
240
  end
251
241
  raise
252
242
  end
253
- rescue Net, SocketError, Net::IMAP::Error, SystemCallError => e
254
- die_from e, :while => "communicating with IMAP server"
243
+ rescue SocketError, Net::IMAP::Error, SystemCallError, IOError => e
244
+ raise FatalSourceError, "While communicating with IMAP server: #{e.message}"
255
245
  end
256
246
  end
257
247