一歩進んだ Rexx

Classic REXX (REstructured eXtended eXecutor)
File #01

RxSock での Email送信 (3)

--- つづき --- ≪ 前回(2)へもどる

送受信のサブルーチン。
まず, 送信側。サブルーチンにする必要なさそうだけど, ログを取る気になれば, ここでそういうことも可能, ということで。

リスト 3-1
/* -- メッセージ送信 */
SndMesg: procedure
parse arg sk, mesg
return SockSend(sk, mesg)

次に受信側。TCP はストリームなので, このようなバッファ管理をしている。 (↑)でも書いたけど, 区切りは復帰・改行(='0d0a'x) になっている。 また, reply は複数の行で返される事もある ... reply コードの後ろが '-' なら続きがあるという意味。 それから, ここでは待ち時間については一回につき, 最大 5秒にしている。

リスト 3-2
/* -- 応答待ち */
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 の基礎は, 説明できたら今後載せていくつもり。
あと, くれぐれもいたずらメールを作り出さないように (^^;;

(↑)とかいいながら 続いてしまった


RxSock での Email送信 (2)

--- つづき --- ≪ 前回(1)へもどる

次に, 送受信。キャッチボールのように電文をやり取りする。電文は, 行区切りで区切られる (行区切り = 復帰・改行)。 SMTP では, 行頭に意味があって, 次のようになっている。(クライアントはこのプログラム側のほう)

そこで, よく使うコマンドの定義を行う。

リスト 2-1
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'

そして, クライアント(このプログラム)が使うパラメータ。

リスト 2-2
m._domain = 'RxMail'
m._from = 'xxx@domain.com'
m._to = 'yyy@domain.com'
m._sfile = sendfile
m._subject = 'こんにちは'

で, 実際のプロトコル。この中では, 送信/受信をサブルーチンにしている。

リスト 2-3
   /* 途中でエラーになったら、一気に下へ下りる */
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

サブルーチンは, また次回に


RxSock での Email送信 (1)

TCP/IP での通信を行うには, 外部関数パッケージ "RxSock"を使う。 この部分は TCP/IP REXX Sockets API というタイトルで infファイルにまとめられてはいるけど, ちょっと分かりにくいかも (←あ, OS/2での話ね)。 "C 言語の同名の関数と同じ" とか言われても, それを知らない人だと多分苦労するはず。 あ, J_Pocket に そういう解説入ってる (これも infファイル)。

ってことで SMTP を使ってメールを送信するプログラムを作ってみる。
基礎はすっ飛ばして, (ぉぃ)

まず接続。

リスト 1-1
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アドレスに変換することにしよう。

リスト 1-2
/* -- 名前解決 */
parse var MTAaddr n'.'
if \datatype(n, 'W') then do
if verify(MTAaddr, xrange(0,9)'.') > 0 then do
   Rc = SockGetHostByName(MTAaddr, 'host.')
   if \Rc then do
      say 'Host Not Found ('MTAaddr')'
      exit 2
   end
   MTAaddr = host.addr
end

というところで, また今度(^o^)/~~


Base64のデコード

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 の基礎から (参考資料)

一から Rexxを学ぶには, 次のような資料があったりする。

  1. OS/2 に添付されている rexx.inf
  2. OS/2 に添付されている orexx.inf
  3. PC-DOS J7/V または それ以降に添付されている(はずの) dosrexx.inf
  4. 日本アイ・ビー・エムから出している本 "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てのもよいかも。


Copyright (C) 1998-2004 Rexx使いの織華
email: ori@drive.co.jp