6 # Copies mail from an IMAP account
9 # http://ruby-doc.org/stdlib/libdoc/net/imap/rdoc/index.html
19 opts.mailboxes = Array.new
20 opts.delete_after = false
22 # Fill in $opts values
23 parser = OptionParser.new do |myparser|
24 myparser.banner = "Usage: #{ File.basename($0) } [opts]"
25 myparser.separator("Specific options:")
26 myparser.on("--delete-after", "-d",
27 "Delete emails after fetching them.") do |d|
28 opts.delete_after = true
30 myparser.on("--username USERNAME", "-u",
31 "Email account to fetch. (example: \
32 RareCactus@gmail.com)") do |u|
35 myparser.on("--list-folders", "-l",
36 "List the IMAP folders that are present.") do |a|
37 raise "can only specify one action" if (opts.action)
40 myparser.on("--snarf", "-S",
41 "Copy mail to the current directory.") do |a|
42 raise "can only specify one action" if (opts.action)
45 myparser.on("--box [MAILBOX]", "-b",
46 "Act on a given mailbox. You may specify -b more than once for \
47 multiple mailboxes.") do |a|
50 myparser.on("--server [SERVER]", "-s",
51 "Email server to use") do |u|
57 raise "must specify an action" unless opts.action
58 raise "must give a username" unless opts.username
59 raise "must give a server" unless opts.server
64 # Get a password from STDIN without echoing it.
65 # This is kind of ugly, but it does work.
66 def get_password(prompt)
67 shell_cmds = 'stty -echo && read password && echo ${password}'
71 pipe = IO.popen(shell_cmds, "r") do |pipe|
74 echo_status = $?.exitstatus
77 if (echo_status != 0) then
78 raise "get_password: error executing: #{shell_cmds}"
84 # We don't know how to deal with non-numeric UIDs. Best just to leave them
86 return uid if (uid =~ /[^0123456789]/)
88 # Pad numeric uids out to 6 digits
89 return sprintf("%006d", uid)
92 def get_sanitized_email_name(mailbox, uid)
96 return "#{msn}#{format_uid(uid)}"
99 def write_email_to_disk(mailbox, data)
101 filename = get_sanitized_email_name(mailbox, arr["UID"])
102 fp = File.open(filename, 'w')
103 fp.write(arr["RFC822.HEADER"])
104 fp.write(arr["RFC822.TEXT"])
108 def snarf_mailbox(imap, mailbox)
114 imap.search(["NOT", "DELETED"]).each do |message_id|
115 if (first_time == true) then
116 # Print a dot immediately after making first contact with the server.
117 # It is reassuring to the user.
122 data = imap.fetch(message_id, [ "UID", "RFC822.HEADER", "RFC822.TEXT" ])
123 write_email_to_disk(mailbox, data)
126 full_count = full_count + 1
128 # Print out a dot to signify progress
135 puts "fetched: #{full_count} messages from #{mailbox}"
138 def snarf_and_delete_mailbox(imap, mailbox)
143 msg_seqnos = Array.new
146 imap.search(["NOT", "DELETED"]).each do |message_id|
147 if (first_time == true) then
148 # Print a dot immediately after making first contact with the server.
149 # It is reassuring to the user.
154 data = imap.fetch(message_id, [ "UID", "RFC822.HEADER", "RFC822.TEXT" ])
155 write_email_to_disk(mailbox, data)
157 full_count = full_count + 1
158 msg_seqnos << data[0].seqno.to_i
159 #break if (count > 20)
162 puts "fetched and deleted: #{full_count} messages from #{mailbox}"
166 # Print out a dot to signify progress
171 imap.store(msg_seqnos, "+FLAGS", [:Deleted])
178 $opts = MyOptions.parse(ARGV)
179 rescue Exception => msg
180 $stderr.print("#{msg}.\nType --help to see usage information.\n")
184 password = get_password("Please enter the password for #{$opts.username}:")
185 imap = Net::IMAP.new($opts.server, 993, true)
186 imap.login($opts.username, password)
189 imap.list("", "*").each do |mbl|
193 $opts.mailboxes.each do |mailbox|
194 if ($opts.delete_after == true)
195 snarf_and_delete_mailbox(imap, mailbox)
197 snarf_mailbox(imap, mailbox)
201 raise "unknown action #{$opts.action}"