Add snarf_mail script to get IMAP mail
authorColin McCabe <colinmcc@lab126.com>
Thu, 28 Jan 2010 01:02:38 +0000 (17:02 -0800)
committerColin McCabe <colinmcc@lab126.com>
Thu, 28 Jan 2010 01:02:38 +0000 (17:02 -0800)
snarf_mail.rb [new file with mode: 0755]

diff --git a/snarf_mail.rb b/snarf_mail.rb
new file mode 100755 (executable)
index 0000000..bfc0873
--- /dev/null
@@ -0,0 +1,133 @@
+#!/usr/bin/env ruby
+
+#
+# snarf_mail.rb
+#
+# Copies mail from an IMAP account
+#
+# Handy reference:
+# http://ruby-doc.org/stdlib/libdoc/net/imap/rdoc/index.html
+#
+
+require 'net/imap'
+require 'optparse'
+require 'ostruct'
+
+class MyOptions
+  def self.parse(args)
+    opts = OpenStruct.new
+               opts.mailboxes = Array.new
+
+    # Fill in $opts values
+    parser = OptionParser.new do |myparser|
+      myparser.banner = "Usage: #{ File.basename($0) } [opts]"
+      myparser.separator("Specific options:")
+      myparser.on("--username USERNAME", "-u",
+              "Email account to fetch. (example: \
+RareCactus@gmail.com)") do |u|
+                               opts.username = u
+      end
+      myparser.on("--list-folders", "-l",
+              "List the IMAP folders that are present.") do |a|
+                               raise "can only specify one action" if (opts.action)
+                               opts.action = :list
+      end
+      myparser.on("--snarf", "-S",
+                                                       "Copy mail to the current directory.") do |a|
+                               raise "can only specify one action" if (opts.action)
+                               opts.action = :snarf
+                       end
+      myparser.on("--box [MAILBOX]", "-b",
+              "Act on a given mailbox. You may specify -b more than once for \
+multiple mailboxes.") do |a|
+                               opts.mailboxes << a
+      end
+      myparser.on("--server [SERVER]", "-s",
+                                                       "Email server to use") do |u|
+                               opts.server = u
+                       end
+    end
+
+    parser.parse!(args)
+    raise "must specify an action" unless opts.action
+    raise "must give a username" unless opts.username
+    raise "must give a server" unless opts.server
+    return opts
+  end
+end
+
+# Get a password from STDIN without echoing it.
+# This is kind of ugly, but it does work.
+def get_password(prompt)
+       shell_cmds = 'stty -echo && read password && echo ${password}'
+       printf "#{prompt}"
+       STDOUT.flush
+       pass = ""
+       pipe = IO.popen(shell_cmds, "r") do |pipe|
+               pass = pipe.read
+       end
+       echo_status = $?.exitstatus
+       system("stty sane")
+       puts
+       if (echo_status != 0) then
+               raise "get_password: error executing: #{shell_cmds}"
+       end
+       return pass.chomp
+end
+
+def format_uid(uid)
+       # We don't know how to deal with non-numeric UIDs. Best just to leave them
+       # alone.
+       return uid if (uid =~ /[^0123456789]/)
+
+       # Pad numeric uids out to 6 digits
+       return sprintf("%006d", uid)
+end
+
+def snarf_mailbox(imap, mailbox)
+       imap.select(mailbox)
+       count = 0
+       imap.search(["NOT", "DELETED"]).each do |message_id|
+               data = imap.fetch(message_id, [ "UID", "RFC822.HEADER", "RFC822.TEXT" ])
+               a = data[0].attr
+               filename = "#{mailbox}#{format_uid(a["UID"])}"
+               fp = File.open(filename, 'w')
+               fp.write(a["RFC822.HEADER"])
+               fp.write(a["RFC822.TEXT"])
+               fp.close
+               count = count + 1
+               if (count > 10) then
+                       count = 0
+                       printf(".")
+                       STDOUT.flush()
+               end
+       end
+       puts "fetched #{count} messages from #{mailbox}"
+end
+
+# MAIN
+begin
+  $opts = MyOptions.parse(ARGV)
+rescue Exception => msg
+  $stderr.print("#{msg}.\nType --help to see usage information.\n")
+  exit 1
+end
+
+password = get_password("Please enter the password for #{$opts.username}:")
+imap = Net::IMAP.new($opts.server, 993, true)
+imap.login($opts.username, password)
+case ($opts.action)
+when :list
+       imap.list("", "*").each do |mbl|
+               puts "#{mbl.name}"
+       end
+when :snarf
+       $opts.mailboxes.each do |mailbox|
+               snarf_mailbox(imap, mailbox)
+       end
+else
+       raise "unknown action #{$opts.action}"
+end
+imap.logout()
+imap.disconnect()
+exit 0