2010-08-13 :-)
_ [首都圏外郭放水路見学]首都圏外郭放水路見学
行ってきた。
オレが本当に欲しかったのは明るい広角レンズだということが分かった。
F4 では暗すぎたんだ...
管理室というのはどこもそう変わらないんだなあとか
「首都圏外郭放水路 超厚水路で 歌手、声優として活躍中の水樹奈々さんの "WILD EYES" MUSIC CLIP撮影が行われました」
_ [QuickML][コードリーディング][Ruby]QuickML を読む - メーリングリストの作成と参加
メーリングリストの作成と参加の一連の処理を見てみる。
メーリングリストの作成方法はこう。
横着プログラミング 第5回: QuickML: 超お手軽なメーリングリスト
○○○@quickml.com のような任意のアドレスにいきなりメールを送るだけで、新しいメーリングリストを作成できる。たとえば宴会のメーリングリストを作るには enkai@quickml.com にメールを送ればいい。このとき、 From: と Cc: のアドレスがメーリングリストに登録される。
Subject: 宴会メーリングリスト To: enkai@quickml.com ← 作りたいMLのアドレス From: satoru@namazu.org ← 自分のアドレス Cc: masui@pitecan.com ← 参加者リスト 突然ですが、宴会好きのメーリング ← 本文 リストを作ってみました。
では先ほどの続きで、 process() を見る。
class Session
:
def process
until @socket.closed?
begin
mail = Mail.new
receive_mail(mail)
if mail.valid?
processor = Processor.new(@config, mail)
processor.process
end
rescue TooLargeMail
cleanup_connection
report_too_large_mail(mail) if mail.valid?
@logger.log "Too Large Mail: #{mail.from}"
rescue TooLongLine
cleanup_connection
@logger.log "Too Long Line: #{mail.from}"
end
end
end
Mail を生成し、mail を受け取り、processor に渡している。
Mail を見る。
Mail クラスは lib/quickml/mail.rb に定義されている。
class Mail
def initialize
@mail_from = nil
@recipients = []
@header = []
@body = ""
@charset = nil
@content_type = nil
@bare = nil
end
メールそのものだ。
receive_mail() を見る。
lib/quickml/server.rb に戻る。
class Session
:
def receive_mail (mail)
while line = @socket.safe_gets
line.xchomp!
command, arg = line.split(/\s+/, 2)
command = command.downcase.intern # "HELO" => :helo
if @command_table.include?(command)
@logger.vlog "Command: #{line}"
send(command, mail, arg)
else
@logger.vlog "Unknown SMTP Command: #{command} #{arg}"
@socket.puts "502 Error: command not implemented"
end
break if command == :quit or command == :data
end
end
ソケットから 1 行ずつ読み取り、メールを処理する。
ここの処理は SMTP のコマンドを想像すると分かりやすい。
send(command, mail, arg)
send() はこれ。
オブジェクトのメソッド name を、引数に args を渡して呼び出し、メソッドの実行結果を返します。
つまり rcpt, data などのコマンド名をそのままメソッド名として解釈させ、受け取った SMTP コマンドごとの該当するメソッドを呼んでいる。
ML に参加する手順では、from() で参加したいひとのメールアドレス、rcpt() でメーリングリストのアドレスを処理することになる。
from() を見てみる。
def from
address = if not self["From"].empty?
collect_address(self["From"]).first
else
@mail_from
end
address = "unknown" if address.nil? or address.empty?
normalize_address(address)
end
collect_address() はこう。addresses にメールアドレスを追加していく。
def collect_address (field)
address_regex =
/(("?)[-0-9a-zA-Z_.+?\/]+\2@[-0-9a-zA-Z]+\.[-0-9a-zA-Z.]+)/ #/
addresses = []
parts = remove_comment_in_field(field).split(',')
parts.each {|part|
if (/<(.*?)>/ =~ part) or (address_regex =~ part)
addresses.push(normalize_address($1))
end
}
addresses.uniq
end
次に rcpt() を見てみる。
class Session
:
def rcpt (mail, arg)
if mail.mail_from.nil?
@socket.puts "503 Error: need MAIL command"
elsif /^To:\s*<(.*)>/i =~ arg or /^To:\s*(.*)/i =~ arg
address = $1
if Mail.address_of_domain?(address, @config.domain)
mail.add_recipient(address)
@socket.puts "250 ok"
else
@socket.puts "554 <#{address}>: Recipient address rejected"
@logger.vlog "Unacceptable RCPT TO:<#{address}>"
end
else
@socket.puts "501 Syntax: RCPT TO: <address>"
end
end
ドメインの有無チェックなどがあるが、ようするに add_recipient() してメーリングリストのメールアドレスを追加していく。
class Mail
:
def add_recipient (address)
@recipients.push(normalize_address(address))
end
recipients にひたすら追加している。
受け取ったメールアドレスを格納していったので、次にそれを処理するための Processor を見る。Processor.process() で処理している。
ファイルは lib/quickml/core.rb
class Processor
:
public
def process
mail_log
if @mail.looping?
@logger.log "Looping Mail: from #{@mail.from}"
return
end
@mail.recipients.each {|recipient|
process_recipient(recipient)
}
end
溜め込んだメーリングリストのメールアドレス recipients を処理していく。
process_recipient() を見る。
class Processor
:
def process_recipient (recipient)
mladdress = recipient
if to_return_address?(mladdress)
handler = ErrorMailHandler.new(@config, @message_charset)
handler.handle(@mail)
elsif @config.confirm_ml_creation and
to_confirmation_address?(mladdress)
validate_confirmation(mladdress)
else
begin
@config.ml_mutex(mladdress).synchronize {
ml = QuickML.new(@config, mladdress, @mail.from, @message_charset)
@message_charset = (@message_charset or ml.charset)
(unsubscribe(ml); return) if unsubscribe_requested?
submit(ml)
}
rescue InvalidMLName
report_invalid_mladdress(mladdress)
end
end
end
QuickML.new() からがその処理。QuickML クラスはメーリングリストを管理する。
submit() はこう。
class Processor
:
def submit (ml)
if ml.exclude?(@mail.from)
@logger.log "Invalid From Address: #{@mail.from}"
elsif ml.forward?
@logger.log "Forward Address: #{ml.address}"
ml.submit(@mail)
elsif confirmation_required?(ml)
ml.prepare_confirmation(@mail)
elsif acceptable_submission?(ml)
submit_article(ml)
else
report_rejection(ml)
end
end
quickmlrc で confirm_ml_creation を true にしていなければ( デフォルト False )、submit_article() が呼ばれる。
submit_article() を見てみる。
class Processor
:
def submit_article (ml)
@unadded_addresses = []
if ml_address_in_to?(ml)
add_member(ml, @mail.from)
@mail.collect_cc.each {|address|
add_member(ml, address)
}
end
unless @unadded_addresses.empty?
report_too_many_members(ml, @unadded_addresses)
end
ml.submit(@mail)
end
ここで、指定したメーリングリストが新規作成される場合は ml.submit(@mail) のみが呼ばれる。メーリングリストがすでに存在しておりそのメーリングリストにメンバーを追加する場合は from で格納したメールアドレスを add_member() で登録する。
add_member() はこう。
class Processor
:
def add_member (ml, address)
begin
ml.add_member(address)
rescue TooManyMembers
@unadded_addresses.push(address)
end
end
ml.add_member() はこう。
class QuickML
:
def add_member (address)
if exclude?(address)
@logger.vlog "Excluded: #{address}"
return
end
return if @active_members.include?(address)
raise TooManyMembers if too_many_members?
@former_members.delete(address)
@active_members.push(address)
save_member_file
@logger.log "[#{@name}]: Add: #{address}"
@added_members.push(address)
@member_added_p = true
end
メーリングリストにメールアドレスを追加などし、メーリングリストの管理用のファイルを書き込んでいる。
ml.submit() は結局これが呼ばれる。
class QuickML
:
def _submit (mail)
inc_count
save_charset
remove_alertedp_file
subject = Mail.rewrite_subject(mail["Subject"], @short_name, @count)
body = rewrite_body(mail)
header = []
mail.each_field {|key, value|
k = key.downcase
next if k == "subject" or k == "reply-to"
header.push([key, value])
}
header.push(["Subject", subject],
["Reply-To", @address],
["X-Mail-Count",@count])
header.concat(quickml_fields)
Mail.send_mail(@config.smtp_host, @config.smtp_port, @logger,
:mail_from => @return_address,
:recipients => @active_members,
:header => header,
:body => body)
end
メールを組み立て Mail.send_mail() を呼ぶ。
これ。特異クラスとなっている。
class << self
def send_mail (smtp_host, smtp_port, logger, optional = {})
mail_from = optional[:mail_from]
recipients = optional[:recipients]
header = optional[:header]
body = optional[:body]
if optional[:recipient]
raise unless optional[:recipient].kind_of?(String)
recipients = [optional[:recipient]]
end
raise if mail_from.nil? or recipients.nil? or
body.nil? or header.nil?
contents = ""
header.each {|field|
key = field.first; value = field.last
contents << "#{key}: #{value}\n" if key.kind_of?(String)
}
contents << "\n"
contents << body
begin
sender = MailSender.new(smtp_host, smtp_port, true)
sender.send(contents, mail_from, recipients)
rescue => e
logger.log "Error: Unable to send mail: #{e.class}: #{e.message}"
end
end
sender.send() でメール送信する。
これ。
class MailSender
:
def send (message, mail_from, recipients)
recipients = [recipients] if recipients.kind_of?(String)
s = TCPSocket.open(@smtp_host, @smtp_port)
send_command(s, nil, 220)
send_command(s, "EHLO #{Socket.gethostname}", 250)
if @use_xverp and @xverp_available and (not mail_from.empty?)
send_command(s, "MAIL FROM: <#{mail_from}> XVERP===", 250)
else
send_command(s, "MAIL FROM: <#{mail_from}>", 250)
end
recipients.each {|recipient|
send_command(s, "RCPT TO: <#{recipient}>", 250)
}
send_command(s, "DATA", 354)
message.each_line {|line|
line.sub!(/\r?\n/, '')
line.sub!(/^\./, "..")
line << "\r\n"
s.print(line)
}
send_command(s, ".", 250)
send_command(s, "QUIT", 221)
s.close
end
SMTP をしゃべっている。
どうして Net::SMTP などを使わずに自力でしゃべっているのか。
ChangeLog を見てみる。
2003-01-17 Satoru Takabayashi <satoru@namazu.org> * lib/quickml/utils.rb (Net::NetPrivate::SMTPCommand): Removed. * lib/quickml/mail.rb (QuickML::Mail::send_mail): Use MailSender instead of Net::SMTP. * lib/quickml/server.rb (QuickML::Session::report_too_large_mail): Change the recipient address: @mail.envelope_from => @mail.from * lib/quickml/mail.rb (QuickML::MailSender): New class.
うーん
MailSender で use_xverp しているので....
module QuickML
class MailSender
def initialize (smtp_host, smtp_port, use_xverp = false)
@smtp_port = smtp_port
@smtp_host = smtp_host
@use_xverp = use_xverp
@xverp_available = false
end
XVERP 関連の処理を描き直したかったのかしら。






