From bd4968397296402e5eff92615de39f60c695167e Mon Sep 17 00:00:00 2001 From: Colin McCabe Date: Wed, 27 Jan 2010 17:02:38 -0800 Subject: [PATCH] Add snarf_mail script to get IMAP mail --- snarf_mail.rb | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 133 insertions(+), 0 deletions(-) create mode 100755 snarf_mail.rb diff --git a/snarf_mail.rb b/snarf_mail.rb new file mode 100755 index 0000000..bfc0873 --- /dev/null +++ b/snarf_mail.rb @@ -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 -- 1.6.6.rc1.39.g9a42