メールを扱うためのコードを書きはじめました。 こんな感じでいいんでしょうか。satoru
?Q? な quoted-printable には対応していません。rfc.quopri が欲しいかも。
% gosh -u rfc.quoted-printable
(quoted-printable-encode-string "abc")
(use gauche.regexp) (use gauche.charconv) (use rfc.base64) (use rfc.quoted-printable)
;; メールのヘッダのフィールドを復号する関数
;; (decode-field "=?iso-2022-jp?B?GyRCJCIbKEI=?=\n abc" "eucjp") => "あabc"
(define (decode-field str to-code)
(with-error-handler
(lambda (e) str)
(lambda ()
;; only `B' encoding is supported.
(regexp-replace-all #/=\?([^?]+)\?([BQ])\?([^?]+)\?=\s*/
str
(lambda (m)
(let* ((charcode (rxmatch-substring m 1))
(encoding (rxmatch-substring m 2))
(message (rxmatch-substring m 3))
(decode (if (equal? encoding "B")
base64-decode-string
quoted-printable-decode-string)))
(ces-convert (decode message)
charcode
to-code)))))))
;; (send-mail "localhost" 25 (open-input-string "Hello!")
;; "from@localhost" "to@localhost")
(define (send-mail host port iport mail-from recipients)
(with-error-handler
(lambda (e) (errorf "send-mail failed: ~a" (slot-ref e 'message)))
(lambda ()
(call-with-client-socket
(make-client-socket 'inet host port)
(lambda (in out)
(let ((send-command
(lambda (command code)
(when command (format out "~a\r\n" command) (flush out))
(let* ((line (read-line in))
(return-code (string->number (substring line 0 3))))
(if (eq? return-code code)
line
(errorf "smtp-error: ~a => ~a" command line))))))
(send-command #f 220)
(send-command (format "HELO ~a" (sys-gethostname)) 250)
(send-command (format "MAIL FROM: <~a>" mail-from) 250)
(for-each (lambda (rcpt)
(send-command (format "RCPT TO: <~a>" rcpt) 250))
(if (string? recipients) (list recipients) recipients))
(send-command "DATA" 354)
(port-for-each (lambda (line)
(if (equal? "." line)
(format out "..\r\n")
(format out "~a\r\n" line)))
(lambda () (read-line iport)))
(send-command "." 250)
(send-command "QUIT" 221)))))))
satoru さすがに HELO localhost 決め打ちは強引かも。 ローカルホストのホスト名を取得するにはどうすればいいのでしょう。 Ruby だと ruby -rsocket -e 'puts Socket.gethostname' でいけるんですが、 Gauche のやり方をパッと調べられなかったのでここで質問してしまいます。 あと、 rcpt-to はリストを受け取れるようにした方がいいかな。 1回のsmtpセッションで複数のメールを送れるように、 とか言い始めると複雑になりますね。
(for-each (lambda (r) (send-command (format "RCPT TO: <~a>" r) 250) rcpt-to-list)