#!/usr/bin/env ruby
=begin
tw.rb - tDiary Writer.
Copyright (C) 2004 by Hahahaha.
<rin_ne@big.or.jp>
http://www20.big.or.jp/~rin_ne/
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=end
=begin
#
# Configure file example.
id:yourid
password:yourpassword
cgi_url:http://example.com/tdiary/update.rb
# txt_dir:/usr/yourid/diary
# touch:/usr/yourid/diary/hw.touch
# proxy:http://www.example.com:8080/
# g:yourgroup
# client_encoding:Shift_JIS
# server_encoding:UTF-8
## for Unix, if Encode module is not available.
# filter:iconv -f euc-jp -t utf-8 %s
=end
$:.push( File.dirname(File.expand_path($0)) )
require 'optparse'
require 'uri'
require 'iconv'
require 'stringio'
require 'tempfile'
require 'net/http'
Net::HTTP.version_1_2
Version = '0.2.0'
ProgramName = 'tDiary Writer'
require 'tdcommon'
# Main routine.
#
class MainRoutine
include MessagePrint
def initialize
@conf = nil
@server = nil
end
def run
@conf = Configure.new
# Parse ARGV.
ARGV.options {|opt|
begin
opt.banner = Banner + "\n" + "#{opt.banner}"
opt.summary_width = 16
opt.on( '-d',
"Debug. Use this switch for verbose log.") {
unless debug
debug_on
print_debug("Debug flag on.")
print_message(Banner)
end
}
opt.on( '-u username',
"Username. Specify username.") {|arg|
@conf.user = arg
}
opt.on( '-p password',
"Password. Specify password.") {|arg|
@conf.password = arg
}
opt.on( '-a agent',
"User agent. Default value is '#{DefaultAgent}'.") {|arg|
@conf.agent = arg
}
opt.on( '-T seconds',
"Timeout. Default value is #{@conf.timeout}.") {|arg|
raise("-T #{arg}: not integer.") if /\d+/ !~ arg
@conf.timeout = arg.to_i
}
opt.on( '-f filename',
"File. Send only this file without checking timestamp.") {|arg|
if (DATE_FORMAT !~ File.basename(arg, ".txt"))
raise("-f #{arg}: Invalid filename format.")
end
raise("-f #{arg}: No such file.") unless File.file?(arg)
@conf.target_files.push(arg)
@conf.force_update = true
}
opt.on( '-F filename',
"Nearly -f option, but only one file with arbitrary filename.") {|arg|
raise("-F #{arg}: No such file.") unless File.file?(arg)
@conf.target_file = arg
@conf.force_update = true
}
opt.on( '-s [yyyy-mm-dd]',
"STDIN or -F filename is treated as 'yyyy-mm-dd' or current date.") {|arg|
if arg
raise("-s #{arg}: Invalid date format.") if DATE_FORMAT !~ arg
@conf.target_date = arg
else
@conf.target_date = Time.now.localtime
@conf.mode = 'append'
end
@conf.force_update = true
}
opt.on( '-n config_file',
"Config file. Default value is '#{@conf.config_file}'.") {|arg|
raise("-n #{arg}: No such file.") unless FileTest.file?(arg)
@conf.config_file = arg
}
opt.parse!
rescue
error_exit($!)
end
}
if (@conf.target_file)
error_exit('Set -s option when -F set.') unless @conf.target_date
end
@conf.load
if (@conf.target_date)
@conf.target_date = (@conf.target_date - @conf.delay).strftime('%Y-%m-%d')
end
@server = TDiaryServer.new(@conf)
count = 0
files = setup_file_list
if (files.empty? and @conf.target_date)
title, body = read_title_body
@server.open('post')
print_message("Post #{mode}:#{@conf.target_date}")
@server.send_entry( title, body )
print_message('Post OK.')
count = 1
else
files.each {|file|
@conf.target_date = File.basename(file.downcase, '.txt') unless @conf.target_date
title, body = read_title_body(file)
@server.open('post')
print_message("Post replace:#{@conf.target_date}")
@server.send_entry( title, body )
print_message('Post OK.')
sleep(1)
count += 1
}
end
@server.close
if (count==0)
print_message('No files are posted.')
else
unless (@conf.force_update)
begin
File.open(@conf.touch_file, 'w') {|file|
file.puts(get_timestamp)
}
rescue
error_exit("[MainRoutine#run] #$!")
end
end
end
end
# Setup file list.
def setup_file_list
files = []
if (@conf.target_file)
files.push(@conf.target_file)
print_debug("MainRoutine#setup_file_list: option -F: #{files.join(', ')}")
elsif (not @conf.target_files.empty?)
files.concat(@conf.target_files)
print_debug("MainRoutine#setup_file_list: option -f: #{files.join(', ')}")
end
if (files.empty?)
Dir.glob("#{@conf.txt_dir}/*.txt") {|file|
next if ( File.file?(@conf.touch_file) and
(File.mtime(file) < File.mtime(@conf.touch_file)) )
next if DATE_FORMAT !~ File.basename(file.downcase, ".txt")
files.push(file)
}
print_debug("MainRoutine#setup_file_list: " +
"current dir (#{@conf.txt_dir}) : #{files.join(', ')}")
end
return files
end
# Read title and body.
def read_title_body(file = nil)
input = nil
begin
# Read file.
if (file)
input = StringIO.new(File.open(file).read)
print_debug("read_title_body: input: #{file}.");
else
input = StringIO.new($stdin.read)
print_debug("read_title_body: input: STDIN.");
end
# Execute filter command, if any.
if (@conf.filter_command)
tmpfile = Tempfile.open($0)
tmpfile.write(input.string)
tmpfile.close
input = StringIO.new(`#{sprintf(@conf.filter_command, tmpfile.path)}`)
raise('cannot execute filter command.') if ($?.to_i != 0)
print_debug("read_title_body: execute filter.");
end
title = convert_to_server(input.gets.chomp)
body = convert_to_server(input.read)
return([title, body]);
rescue
error_exit("read_title_body: #$!")
end
end
# Convert from client to server.
def convert_to_server(str)
return '' unless str
return str if @conf.server_encoding == @conf.client_encoding
return Iconv.conv(@conf.server_encoding, @conf.client_encoding, str)
end
# Convert from server to client.
def convert_to_client(str)
return '' unless str
return str if @conf.server_encoding == @conf.client_encoding
return Iconv.conv(@conf.client_encoding, @conf.server_encoding, str)
end
# Get "YYYYMMDDhhmmss" for now.
def get_timestamp
return Time.now.localtime.strftime('%Y%m%d%H%M%S')
end
end
begin
routine = MainRoutine.new
routine.run
end