--- つづき --- ≪ 前回(2)へもどる
送受信のサブルーチン。
まず, 送信側。サブルーチンにする必要なさそうだけど, ログを取る気になれば, ここでそういうことも可能, ということで。
/* -- メッセージ送信 */ SndMesg: procedure parse arg sk, mesg return SockSend(sk, mesg) |
次に受信側。TCP はストリームなので, このようなバッファ管理をしている。 (↑)でも書いたけど, 区切りは復帰・改行(='0d0a'x) になっている。 また, reply は複数の行で返される事もある ... reply コードの後ろが '-' なら続きがあるという意味。 それから, ここでは待ち時間については一回につき, 最大 5秒にしている。
/* -- 応答待ち */ WaitFor: procedure expose nl True False MAX_RECVLEN RecvTxt ErrFlag parse arg sk, CheckCode +1 if symbol('ErrFlag') == 'LIT' then ErrFlag = False if ErrFlag then return False if symbol('RecvTxt') == 'LIT' then RecvTxt = '' Retry: do 3 while pos(nl, RecvTxt) = 0 r.0 = 1; r.1 = sk; Rc = SockSelect('r.',,, 5) if Rc < 0 then do say SockPSock_Errno('select Err') return False end if Rc > 0 & r.0 > 0 & r.1 = sk then do Rc = SockRecv(sk, 'txt', MAX_RECVLEN) RecvTxt = RecvTxt''txt end end if pos(nl, RecvTxt) = 0 then return False parse var RecvTxt txt (nl) RecvTxt =1 rcd +3 s +1 /* if \contflag & cont == '-' then contflag = true */ /* if contflag & cont == ' ' then contflag = false */ /* if contflag then iterate */ say left(txt, 70) if \datatype(rcd, 'w') | s \= '' then signal Retry if left(ResCodeStr(rcd), 1) == CheckCode then return True Rc = SndMesg(sk, SMTP._RESET nl) Rc = SndMesg(sk, SMTP._QUIT nl) ErrFlag = True return False ResCodeStr: procedure parse arg rcd +1 ResStr.1 = 'Error' ResStr.2 = 'Success' ResStr.3 = 'Warning' ResStr.4 = 'Failure' ResStr.5 = 'Failure' return ResStr.rcd |
少々(結構?)手抜きかもしれないけど, とりあえずはこれで終わり。
TCP/IP の基礎は, 説明できたら今後載せていくつもり。
あと, くれぐれもいたずらメールを作り出さないように (^^;;
◇
(↑)とかいいながら 続いてしまった。
--- つづき --- ≪ 前回(1)へもどる
次に, 送受信。キャッチボールのように電文をやり取りする。電文は, 行区切りで区切られる (行区切り = 復帰・改行)。 SMTP では, 行頭に意味があって, 次のようになっている。(クライアントはこのプログラム側のほう)
そこで, よく使うコマンドの定義を行う。
nl = '0d0a'x /* -- Const */ SMTP._HELLO = 'HELO' SMTP._MAIL = 'MAIL' SMTP._RECIPIENT = 'RCPT' SMTP._DATA = 'DATA' SMTP._RESET = 'RSET' SMTP._NOOP = 'NOOP' SMTP._QUIT = 'QUIT' |
そして, クライアント(このプログラム)が使うパラメータ。
m._domain = 'RxMail' m._from = 'xxx@domain.com' m._to = 'yyy@domain.com' m._sfile = sendfile m._subject = 'こんにちは' |
で, 実際のプロトコル。この中では, 送信/受信をサブルーチンにしている。
/* 途中でエラーになったら、一気に下へ下りる */ if WaitFor(sk, 'Success') then Rc = SndMesg(sk, SMTP._HELLO m._domain''nl) if WaitFor(sk, 'Success') then Rc = SndMesg(sk, SMTP._MAIL 'FROM:<'m._from'>'nl) if WaitFor(sk, 'Success') then Rc = SndMesg(sk, SMTP._RECIPIENT 'TO:<'m._to'>'nl) if WaitFor(sk, 'Success') then Rc = SndMesg(sk, SMTP._DATA nl) if WaitFor(sk, 'Warning') then do Rc = SndMesg(sk, 'From: "ぴか/2 with RxSock'SockVersion()'" <'m._from'>' nl) Rc = SndMesg(sk, 'To: <'m._to'>'nl) Rc = SndMesg(sk, 'Subject:' m._subject nl) Rc = SndMesg(sk, nl) /* ←Header の区切り */ do while Lines(m._sfile) > 0 txt = LineIn(m._sfile) Rc = SockSend(sk, txt''nl) end Rc = SockSend(sk, nl'.'nl) end if WaitFor(sk, 'Success') then Rc = SockSend(sk, SMTP._QUIT nl) if WaitFor(sk, 'Success') then RetCode = 0; else RetCode = 1; if SockSoClose(sk) < 0 then say SockPSock_Errno('Close Err') if RetCode = 0 then say 'Mail Send OK !' else say 'Error:' RetCode exit RetCode |
サブルーチンは, また次回に。
TCP/IP での通信を行うには, 外部関数パッケージ "RxSock"を使う。 この部分は TCP/IP REXX Sockets API というタイトルで infファイルにまとめられてはいるけど, ちょっと分かりにくいかも (←あ, OS/2での話ね)。 "C 言語の同名の関数と同じ" とか言われても, それを知らない人だと多分苦労するはず。 あ, J_Pocket に そういう解説入ってる (これも infファイル)。
ってことで SMTP を使ってメールを送信するプログラムを作ってみる。
基礎はすっ飛ばして, (ぉぃ)
まず接続。
call RxFuncAdd 'SockLoadFuncs', 'rxsock', 'SockLoadFuncs' call SockLoadFuncs sk = SockSocket('AF_INET', 'SOCK_STREAM', 0) remaddr.family = 'AF_INET' remaddr.port = 25 /* SMTP は Port=25 */ remaddr.addr = MTAaddr if SockConnect(sk, 'remaddr.') < 0 then do say SockPSock_Errno('Connect Err') exit end |
でもその前に, アドレスの解決。
RxFTP とは異なりアドレスにホスト名を直接指定する事はできない。
ここでは, ホスト名が指定されていたら IPアドレスに変換することにしよう。
/* -- 名前解決 */ |
というところで, また今度。(^o^)/~~
base64 は binary dataを E-mailで送る時などによく使われる。 他にも web server にアクセスする時の認証にも使われる事がある (Basic Realm (基礎領域?) とか)。 実際, この処理は 〆(^^) の作った SilkWeb でも使っている。
その base64 のデコード処理 (ただし, 複数行には対応していない)。
しかし, 速度を求めるのなら, これを使うより RxJis 外部関数パッケージを使うのがよい (T_T
Base64DecoStr: procedure BaseChars = xrange('A', 'Z')xrange('a', 'z')xrange('0', '9')'+/=' parse arg txt '=' res = '' val = translate(txt, xrange(, '3f'x), BaseChars) do i = 1 to length(val) res = res||substr(x2b(c2x(substr(val, i, 1))), 3) end return x2c(b2x(left(res, length(res) %8 *8)))
一から Rexxを学ぶには, 次のような資料があったりする。
〆(^^) が知っている範囲なので, 他にもっとよいものがあるかもしれない。 そういえば OS/2 の本にも (4)に相当する本があった気がする。
お勧めは (3) か (4) (←OS/2 で使う場合でも)で, 次が (2)。
翻訳の質の高さから言えば, 一番よいのは orexx.inf なのだけど, これは orexxについて書かれているので, 違いが分かっていればお勧め。(Rexxをある程度習得した後とか)
(1) は翻訳に難があり, しかも内容もいまいちで, 残念ながらお勧めできない (標準なのに (T_T)
英語でもよいなら Hursley 研究所 という手段もある。
ここから Rexx, ORexx, NetRexx
そのほか RedBook などを たどることができる。
Object REXX for Linuxに含まれている (はずの), rexxref.pdf rexxpg.pdfてのもよいかも。