トップ «前の日記(2010-08-12) 最新 次の日記(2010-08-14)» 編集

ヨタの日々

2001|08|09|10|11|12|
2002|01|02|03|04|05|06|07|08|09|10|11|12|
2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|12|
2024|01|02|03|04|

2010-08-13 :-)

_ 朝ッ

0930 起床

だる

ねむ

_ [首都圏外郭放水路見学]首都圏外郭放水路見学

行ってきた。

オレが本当に欲しかったのは明るい広角レンズだということが分かった。

F4 では暗すぎたんだ...

IMG_5038

管理室というのはどこもそう変わらないんだなあとか

IMG_5012

「首都圏外郭放水路 超厚水路で 歌手、声優として活躍中の水樹奈々さんの "WILD EYES" MUSIC CLIP撮影が行われました」

IMG_5015

_ [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 関連の処理を描き直したかったのかしら。