Chewie71-Notes
- !/usr/local/bin/ruby
- == Synopsis
- This application updates RSS Feed folders in Zmibra user accounts.
- == Examples
- This command does RSS folder updates.
- update_feeds
- Other examples:
- ruby_cl_skeleton -q bar.doc
- ruby_cl_skeleton --verbose foo.html
- == Usage
- update_feeds [options]
- For help use: update_feeds -h
- == Options
- -u, --user User to synchronize {uid|email|all}
- -h, --help Displays help message
- -t, --test Displays test/debug information and logs DEBUG messages
- -v, --version Display the version, then exit
- -q, --quiet Output as little as possible, overrides verbose
- -V, --verbose Verbose output
- TO DO - add additional options
- == Author
- Matt Mencel
- == Copyright
- Copyright (c) 2008 Matt Mencel. Licensed under the MATT License:
require "date" require "logger" require "pathname" require "open3" require "optparse" require "ostruct" require "rdoc/usage"
require 'rubygems' require 'icalendar' require 'rss/1.0' require 'rss/2.0' require 'open-uri' require 'hpricot'
class App
VERSION = '0.0.1' attr_reader :options
def initialize(arguments, stdin) @arguments = arguments
puts "INITARGS: #{@arguments}" puts "ARGSLGTH: #{@arguments.size}"
@stdin = stdin
# Set defaults @options = OpenStruct.new @options.verbose = false @options.quiet = false # TO DO - add additional defaults end
# Parse options, check arguments, then process the command def run puts "OPTS: #{parsed_options?}"
puts "VALIDARGS: #{arguments_valid?}"
if parsed_options? && arguments_valid? puts "Start at #{DateTime.now}\\n\\n" if @options.verbose output_options if @options.verbose # [Optional] process_arguments process_command puts "\\nFinished at #{DateTime.now}" if @options.verbose else output_usage end end protected def parsed_options? # Specify options opts = OptionParser.new opts.on('-h', '--help') { output_help } opts.on('-v', '--version') { output_version ; exit 0 } opts.on('-V', '--verbose') { @options.verbose = true } opts.on('-q', '--quiet') { @options.quiet = true } # TO DO - add additional options opts.on('-u', '--user USER', 'User to sync RSS folders {uid|mail|all}.') do |u| options.user = u end opts.parse!(@arguments) rescue return false
process_options true end
# Performs post-parse processing on options def process_options @options.verbose = false if @options.quiet end
def output_options puts "Options:\\n"
@options.marshal_dump.each do |name, val| puts " #{name} = { #{val}" end end
# True if required arguments were provided def arguments_valid? # TO DO - implement real logic here puts "VALIDARGS: #{@arguments}" puts "ARGV Length: #{@arguments.size}" true if @arguments.length >= 0 end
# Setup the arguments def process_arguments # TO DO - place in local vars, etc end
def output_help output_version RDoc::usage() #exits app end
def output_usage RDoc::usage('usage') # gets usage from comments above end
def output_version puts "#{File.basename(__FILE__)} version #{VERSION}" end
def process_command # Setup Logger $logger = Logger.new('rss_folder.log', 'daily') if @options.verbose $logger.level = Logger::DEBUG else $logger.level = Logger::INFO end # OPEN THE ZMPROV PROMPT #$cmdin, $cmdout, $cmderr = Open3.popen3("zmprov") $cmdin, $cmdout, $cmderr = Open3.popen3("zmprov 2>&1") puts $cmdout.readpartial(4096) # Create RSS and Calendar Hashes feeds=Hash.new calendars = Hash.new $cmdin.puts("sm shares@wiu.edu") $cmdin.puts("gaf") $cmdin.puts("sm") count=0 $cmdout.each do |line| count += 1 if line == "\n" line.split(" ").each do |subline| if subline.include?("Feeds/") record = subline.split("(") feeds[record[0].rstrip] = record[1].rstrip.chop elsif subline.include?("Calendars/") if subline.include?("(") record = subline.split("(") calendars[record[0].rstrip] = record[1].rstrip.chop else calendars[subline.chomp] = "from feed" end end end break if count == 2 end puts "FEEDS: #{feeds.inspect}" puts "CALENDARS: #{calendars.inspect}" # Testing RSS Feeds for Admin COS Users #users = `zmprov sa zimbraCOSId=5487b59c-de80-4220-a819-07a6a84ed122`.split() users=[] # start with an empty array puts "Fetching employee list..." employees = Hash.new employees_array = Array.new(`zmprov sa "(zimbraCOSId=70fadfad-cf1f-4f67-bb58-dd3e44f77942)"`.split() + `zmprov sa "(zimbraCOSId=5487b59c-de80-4220-a819-07a6a84ed122)"`.split()) employees_array.each do |emp| employees[emp.downcase] = 1 end f = File.open("testusers.full") or die "Unable to open file..." f.each_line {|line| users << line.chomp.downcase } #users = ["mr-mencel@wiu.edu","f-seaton@wiu.edu","r-runquist@wiu.edu"] user_count = users.size() # Refresh WIU Feeds In Share Account feeds.each_pair {|folder, feed_url| folder_path = Pathname.new(folder) puts "REFRESH FEED #{folder_path}" refresh_feed("shares@wiu.edu", folder_path, feed_url) } # Refresh WIU Calendars in Share Account calendars.each do |calendar, calendar_url| folder_path = Pathname.new(calendar) puts "REFRESH CALENDAR: #{folder_path}" if folder_path.to_s.include?("Athletics") cal = build_calendar_ics("Athletics") pru(cal, folder_path) elsif folder_path.to_s.include?("Fine Arts") cal = build_calendar_ics("Art Gallery, BCA, Fine Arts, Theater") pru(cal, folder_path) elsif folder_path.to_s.include?("Registrar") cal = build_calendar_ics("Registrar") pru(cal, folder_path) else refresh_calendar("shares@wiu.edu", folder_path, calendar_url) end end # Subscribe users to Feeds and Calendars user_count = users.size() t = Time.now users.each do |user| puts "Creating Shares for #{user}...#{user_count-=1} remaining" feeds.each_pair {|folder, feed_url| folder_path = Pathname.new(folder) create_share(user, employees, folder_path) } calendars.each_pair do |folder, calendar_url| folder_path = Pathname.new(folder) create_share(user, employees, folder_path) end if user_count%25 == 0 t2 = Time.new diff = t2.to_i - t.to_i puts "#{(diff)*(user_count/25/60)} minutes remaining" t = Time.new end end
#process_standard_input # [Optional] end
def process_standard_input input = @stdin.read # TO DO - process input
# [Optional] # @stdin.each do |line| # # TO DO - process each line #end end
end
- TO DO - Add Modules, Classes, etc
- FUNCTIONS ###
- def create_feed_folder(user, folder_path, feed_url)
- $logger.debug("DEBUG: zmmailbox -z -m #{user} createFolder -u '#{feed_url}' '#{folder_path}'")
- $logger.info("Creating missing folder #{folder_path} for #{user}")
- cmd_result = cmd_to_sys("zmmailbox -z -m #{user} cf -u '#{feed_url}' '#{folder_path}'")
- $logger.debug("ERROR?: #{cmd_result[1]}")
- if check_command_result(cmd_result[1], user, feed_url) == "unknown folder"
- $logger.warn(cmd_result[1])
- create_feed_folder(user, folder_path.dirname(), nil)
- $logger.debug("ATTEMPTING TO RECREATE: #{folder_path}")
- cmd_result = cmd_to_sys("zmmailbox -z -m #{user} cf -u '#{feed_url}' '#{folder_path}'")
- check_command_result(cmd_result[1], user, feed_url)
- end
- $logger.debug("Created Folder #{folder_path}")
- end
def build_calendar_ics(cal_list)
# Create a new calendar object cal = Calendar.new cal_list.split(",").each do |cal_name| $all_day = false case cal_name when /Art Gallery/ ; source = "http://www.wiu.edu/wiucalendar/xml.sphp?calendar=Art+Gallery" when /Athletics/ ; source = "http://www.wiuathletics.com/rss.dbml?db_oem_id=12000&media=schedules" when /BCA/ ; source = "http://www.wiu.edu/wiucalendar/xml.sphp?calendar=Bureau+of+Cultural+Affairs" when /Fine Arts/ ; source = "http://www.wiu.edu/wiucalendar/xml.sphp?calendar=School+of+Music" when /Registrar/ ; source = "http://www.wiu.edu/wiucalendar/xml.sphp?calendar=Office+of+the+Registrar" ; $all_day = true when /Theater/ ; source = "http://www.wiu.edu/wiucalendar/xml.sphp?calendar=Department+of+Theatre+%26+Dance" end content = "" # raw content of rss feed will be loaded here open(source) do |s| content = s.read end rss = RSS::Parser.parse(content, false) puts "Fetching #{rss.items.size} RSS items for: #{cal_name}" #print "number of items: ", rss.items.size, "\n" rss.items.each do |item| result = parse_feeds_for_calendars(item) event_location = result.split(" :: ")[0] item.description = result.split(" :: ")[1] #puts DateTime.parse(item.date.to_s).new_offset(of=0) #puts Date.parse(item.date.to_s) endtime = item.date+(60*60*2) cal.event do if $all_day dtstart Date.parse(item.date.to_s) dtend Date.parse(item.date.to_s) else dtstart DateTime.parse(item.date.to_s).new_offset(of=0) dtend DateTime.parse(endtime.to_s).new_offset(of=0) end summary item.title description item.description location event_location uid "#{item.link}#{DateTime.parse(item.date.to_s)}" end end end return cal
end
def parse_feeds_for_calendars(item)
if item.link.include?("wiuathletics") $all_day = false #puts "DESC: "+item.description #puts "LINK: "+item.link #puts "DATE: #{item.date}" event_data = item.description.split(" - ") event_location = event_data[0].split("at ")[1] event_time = event_data[1].split("-")[0] if !event_time.include?(":") event_time = "#{event_time.split(' ')[0]}:00 #{event_time.split(' ')[1]}" end if event_time.include?('p.m.') event_time = "#{event_time.split(' ')[0].split(':')[0].to_i + 12}:#{event_time.split(' ')[0].split(':')[1]}" elsif event_time.include?("a.m.") event_time = event_time.split(' ')[0] event_time = "0#{event_time}" if event_time.split(':')[0].to_i < 10 end if event_time.include?("TBA") $all_day = true item.date = "#{Date.parse(item.date.to_s)}T08:00:00+00:00" else item.date = "#{Date.parse(item.date.to_s)}T#{event_time}:00+00:00" end item.description = "#{event_location} :: #{item.link}" #html/body/table/tbody/tr/td/div[2]/div/table/tbody/tr/td[2]/table/tbody/tr[7]/td/table else @url = item.link @response = # open-uri RDoc: http://stdlib.rubyonrails.org/libdoc/open-uri/rdoc/index.html open(@url, "User-Agent" => "Ruby/#{RUBY_VERSION}", "From" => "help@wiu.edu", "Referer" => "http://zimbra.wiu.edu") { |f| puts "Fetched document: #{f.base_uri}" #puts "\\t Content Type: #{f.content_type}\\n" #puts "\\t Charset: #{f.charset}\\n" #puts "\\t Content-Encoding: #{f.content_encoding}\\n" #puts "\\t Last Modified: #{f.last_modified}\\n\\n" # Save the response body @response = f.read } #Rdoc: http://code.whytheluckystiff.net/hpricot/ doc = Hpricot(@response)
event_location = (doc/"/html/body/div/div[4]/div/dl/dd[2]").inner_html.split(': </strong>')[1].strip item.description = "#{event_location} :: #{item.description}\n\n#{item.link}" end
end
def create_share(user, employees, folder_path)
if folder_path.to_s.include?("Staff") && !employees.has_key?(user) puts "Not Staff...continuing with next folder" return end $logger.debug("DEBUG: zmmailbox -z -m #{user} createMountpoint -c purple '#{folder_path}' shares@wiu.edu '#{folder_path}'") $logger.info("Creating missing share #{folder_path} for #{user}") cmd_result = sm(user) cmd_result = cm(user, folder_path) #puts cmd_result if cmd_result != nil case cmd_result when /ALREADY_EXISTS/ ; return when /unknown folder/ ; cf(user, folder_path) when /permission denied/ ; mfg(user, folder_path) else ; $logger.fatal("Unknown Error: #{cmd_result[1]} on share #{folder_path} for #{user}")
end
$logger.debug("ATTEMPTING TO RECREATE SHARE #{folder_path}") create_share(user, folder_path) else $logger.info "Shared #{folder_path} with #{user}" end
end
def is_integer(to_test)
begin Integer(to_test) return true rescue ArgumentError return false end
end
def refresh_calendar(user, folder_path, calendar_url)
$logger.debug("DEBUG: zmmailbox -z -m #{user} syncFolder '#{folder_path}'") cmd_result = sm(user) #syncFolder cmd_result = sf(folder_path) #puts cmd_result if cmd_result != nil case cmd_result when /unknown folder/ ; puts "Missing or Bad Share!?"; exit
end
else $logger.info "Refreshed #{folder_path} for #{user}" end
end
def refresh_feed(user, folder_path, feed_url)
$logger.debug("DEBUG: zmmailbox -z -m #{user} syncFolder '#{folder_path}'") cmd_result = sm(user) #Mark and Purge Old Messages msg_ids_for_deletion = smb(folder_path, "before:-30days") dm(msg_ids_for_deletion) msg_ids_for_marking = smb(folder_path, "before:-1day") if !msg_ids_for_marking.empty? mmr(msg_ids_for_marking) end #syncFolder cmd_result = sf(folder_path) #puts cmd_result if cmd_result != nil case cmd_result when /unknown folder/ ; create_feed_folder(user, folder_path, feed_url)
end
else $logger.info "Refreshed #{folder_path} for #{user}" end
end
def remove_feeds(user, folder_path, feed_url)
$logger.debug("CMD: zmmailbox -z -m #{user} deleteFolder '#{folder_path}'") cmd_result = cmd_to_sys("zmmailbox -z -m #{user} df '#{folder_path}'")
end
- createFolder
def cf(user, folder_path)
if folder_path.to_s.include?("Feeds") zmcommand = "cf -c purple '#{folder_path}'" elsif folder_path.to_s.include?("Calendars") zmcommand = "cf -c purple -V appointment '#{folder_path}'" end #puts zmcommand $cmdin.puts(zmcommand) count = 0 while (( line = $cmdout.gets )) count += 1 #puts "#{count}:: #{line}" #puts is_integer(line.split.last) if is_integer(line.split.last) return elsif line.include?("unknown folder") $logger.warn(line) cf(user, folder_path.dirname) cm(user, folder_path) return else $logger.fatal(line) puts "EXITING:: #{line}" exit end end
end
- createMountpoint
def cm(user, folder_path)
if folder_path.to_s.include?("Feeds") zmcommand = "cm -c purple '#{folder_path}' shares@wiu.edu '#{folder_path}'" elsif folder_path.to_s.include?("Calendars") zmcommand = "cm -c purple -V appointment '#{folder_path}' shares@wiu.edu '#{folder_path}'" end $cmdin.puts(zmcommand) count=0 while (( line = $cmdout.gets )) count += 1 #puts "#{count}:: #{line}" if is_integer(line.split.last) return end case line when /ALREADY_EXISTS/ ; $logger.warn(line) ; return when /NO_SUCH_ACCOUNT/ ; $logger.fatal(line) ; puts "EXITING:: #{line}" ; exit when /unknown folder/ ; cf(user, folder_path) ; return when /NO_SUCH_FOLDER/ ; cf(user, folder_path) ; return when /permission denied/ ; mfg(user, folder_path) ; sm(user) ; cm(user, folder_path) ; return else ; $logger.fatal(line) ; puts "EXITING:: #{line}" ; exit
end
end
end
- deleteMessages
def dm(msg_ids_for_deletion)
$cmdin.puts("dm #{msg_ids_for_deletion}") $cmdin.puts("bogus") $cmdout.each do |line| if line.include?("bogus") $logger.info "Deleted #{msg_ids_for_deletion}" return end end
end
- markFolderRead
def mfr(folder_path)
$cmdin.puts("mfr '#{folder_path}'") $cmdin.puts("bogus") $cmdout.each do |line| if line.include?("bogus") $logger.info "Marked Folder Read: #{folder_path}" return end end
end
- markMessageRead
def mmr(msg_ids_for_marking)
$cmdin.puts("mmr #{msg_ids_for_marking} 1") $cmdin.puts("bogus") $cmdout.each do |line| if line.include?("bogus") $logger.info "Messages Marked Read: #{msg_ids_for_marking}" return end end
end
- modifyFolderGrant
def mfg(user, folder_path)
sm("shares@wiu.edu") $cmdin.puts("mfg '#{folder_path}' account #{user} r") $cmdin.puts("bogus") $cmdout.each do |line| if line.include?("ERROR:") and !line.include?("bogus") $logger.fatal(line) puts "EXITING: #{line}" exit elsif line.include?("bogus") $logger.info "Granted read permission to #{folder_path} for #{user}" return else $logger.fatal(line) puts "UNKNOWN ERROR: #{line}" exit end end
end
- postRestUrl
def pru(cal, folder_path)
f = File.new("cal.ics", "w") or die "Unable to create file..." f.puts cal.to_ical f.close sm("shares@wiu.edu") $cmdin.puts("pru \"#{folder_path}\" cal.ics") $cmdin.puts("bogus") $cmdout.each do |line| if line.include?("ERROR:") and !line.include?("bogus") $logger.fatal(line) puts "EXITING: #{line}" exit elsif line.include?("bogus") $logger.info "ICS posted to #{folder_path}" return else $logger.fatal(line) puts "UNKNOWN ERROR: #{line}" exit end end exit
end
- searchMailbox for messages older than 30 days
def smb(folder_path, date_fmt)
msg_ids = Array.new $cmdin.puts("s -l 1000 -t message 'in:\"#{folder_path}\" #{date_fmt}'") $cmdin.puts("bogus") $cmdout.each do |line| if line.include?("mess") msg_ids << line.split(' ')[1] end if line.include?("bogus") return msg_ids.join(',') end end
end
- selectMailbox
def sm(user)
$cmdin.puts("sm #{user}") $cmdout.each do |line| if line.include?("mailbox:") return elsif line.include?("NO_SUCH_ACCOUNT") $logger.fatal(line) puts "EXITING:: #{line}" exit else $logger.fatal(line) puts "UNKNOWN ERROR: #{line}" exit end end
end
- syncFolder
def sf(folder_path)
$cmdin.puts("sf \"#{folder_path}\"") $cmdin.puts("sm") count=0 while(( line = $cmdout.gets )) count+=1 if line.include?("unknown folder:") $logger.warn(line) return line elseif line.include?("ERROR") $logger.fatal(line) puts "UNKNOWN ERROR:: #{line}" exit end return if count == 4 end
end
- Create and run the application
include Icalendar app = App.new(ARGV, STDIN) app.run