[C++] 163 SMTPプロトコル 仮想SMTPサーバーとの通信

[Intel Mac, Big Sur 11.6.5, clang 13.0.0, NO IDE]

『C言語によるTCP/IPネットワークプログラミング』(2001年)第2章 SMTPプロトコルの所を学習しています。

仮想SMTPサーバー FakeSMTPにメールを送信しデータのやり取りをさせました。

サンプルコードにあったEUC-JP→ISO-2022-JP変換関数とBase64変換関数は大幅に変更しました。EUC-JP→ISO-2022-JP変換(MacはUTF-8から変換)はネットにあったコードを丸々拝借しました。libiconvライブラリを使用します。Base64変換についてはclxライブラリをダウンロードして使っています。これによりcppファイル2つを削減しました。

FakeSMTPのソースコードを入手しM1 Macでビルドしたものの、残念ながら起動しませんでした。

Apple Silicon版仮想SMTPサーバーを自製できれば良いのですが。

#include <mailtest.h>
#include <clx/base64.h>

int main(int argc,char *argv[])
{
	struct passwd	*pwd;
	char	hostname[MAXHOSTNAMELEN+1];
	time_t	t;
	char	from[65];
	char	b_subject[1024];
	char	j_data[1024*2];
	char	*ptr;
	char	buf[1024];
	char	boundary[80];
	char	*f_buf,*b_buf;
	FILE	*fpd;
	struct stat	st;
	int	i;
	int	soc;

	if(argc <= 1){
		fprintf(stderr,"mailtest recpient [file] [file] ...\n");
		return 0;
	}

	 // JIS:Base64のサブジェクトを標準入力から取得
	GetSubject(b_subject);

	 // 本文データを標準入力から取得
	GetData(j_data);

	cout << "GetData pass" << endl;

	 // 送信者設定
	pwd = getpwuid(getuid());
	gethostname(hostname,MAXHOSTNAMELEN);
	sprintf(from,"%s@%s",pwd->pw_name,hostname);

	cout << "from: " << from << endl;

	 // ローカルホストのSMTPに接続
	if((soc = ConnectHost("localhost","smtp",25)) == -1){
		fprintf(stderr,"Cannot connect to local smtp.\n");
		return 0;
	}

	 // SMTPとの通信
	SOCprintf(soc,"HELO %s\r\n",hostname);
	SOCrecv(soc,buf);
	SOCprintf(soc,"MAIL FROM:%s\r\n",from);
	SOCrecv(soc,buf);
	SOCprintf(soc,"RCPT TO:%s\r\n",argv[1]);
	SOCrecv(soc,buf);

	 // データ送信開始
	SOCprintf(soc,"DATA\r\n");
	SOCrecv(soc,buf);

	 // ヘッダ部送信
	t=time(&t);
	SOCprintf(soc,"Date: %s",ctime(&t));
	SOCprintf(soc,"From: %s\r\n",from);
	SOCprintf(soc,"Subject: =?ISO-2022-JP?B?%s?=\r\n",b_subject);
	SOCprintf(soc,"To: %s\r\n",argv[1]);
	SOCprintf(soc,"MIME-Version: 1.0\r\n");
	sprintf(boundary,"%010d_%09d",t,getpid()); // プロセスID取得
	SOCprintf(soc,"Content-Type: MULTIPART/mixed; BOUNDARY=%s\r\n",boundary);
	SOCprintf(soc,"\r\n");

	 // データ部送信
	SOCprintf(soc,"--%s\r\n",boundary);
	SOCprintf(soc,"Content-Type: TEXT/plain; charset=ISO-2022-JP\r\n");
	SOCprintf(soc,"\r\n");

	send(soc,j_data,strlen(j_data),0);

	SOCprintf(soc,"\r\n");

	 // 添付ファイル送信
	for(i = 2; i < argc; i++){
		if((fpd = fopen(argv[i], "r"))!= NULL){
			printf("argv[%d] %s\n",i,argv[i]);

			fstat(fileno(fpd), &st); // ファイル状態を取得
			f_buf=(char *)malloc(st.st_size);
			b_buf=(char *)malloc(st.st_size * 2);
			fread(f_buf, 1, st.st_size, fpd); // ファイルデータを1バイトずつ全サイズ分をf_bufに格納
			fclose(fpd);
			SOCprintf(soc,"--%s\r\n",boundary);
			SOCprintf(soc,"Content-Type: application; name=%s; x-unix-mode=0777\r\n",argv[i]);
			SOCprintf(soc,"Content-Transfer-Encoding: BASE64\r\n");
			SOCprintf(soc,"Content-Description: %s\r\n",argv[i]);
			SOCprintf(soc,"\r\n");
			
			// Base64変換
			string f_buf_str = string(f_buf);
			string f_buf_64 = clx::base64::encode(f_buf_str);
			f_buf_64.copy(b_buf, st.st_size *2 -1);

			// base64(f_buf,st.st_size,b_buf); // base64関数は廃止

			send(soc,b_buf,strlen(b_buf),0);
			SOCprintf(soc,"\r\n");
			SOCprintf(soc,"\r\n");
			free(f_buf);
			free(b_buf);
		}
		else{
			fprintf(stderr,"%s cannot read.\n",argv[i]);
		}
	}

	SOCprintf(soc,"--%s--\r\n",boundary);

	 // データ送信完了 
	SOCprintf(soc,".\r\n");
	SOCrecv(soc,buf);

	 // 終了 
	SOCprintf(soc,"QUIT\r\n");
	SOCrecv(soc,buf);

	 // ソケットクローズ 
	SocketClose(soc);
}

参考サイト

[C++] 162 SMTPプロトコル 仮想SMTPサーバーの設置 Intel Macへの回帰

[Intel Mac, Big Sur 11.6.5, clang 13.0.0, NO IDE]

『C言語によるTCP/IPネットワークプログラミング』(2001年)第2章 SMTPプロトコルのサンプルコードを参考に仮想SMTPサーバーへメールを送る実験に取り組んでいます。

Java製の仮想SMTPサーバー FakeSMTPがM1 Macでは起動できなかったため、Intel Macでの開発に切り替えました。

メールタイトルはUTF-8からISO-2022-JPを経て添付ファイルとともにbase64に変換しました。試行錯誤の末、やっとメールの送信に成功しました。

開発環境は整ったので、これから本腰を入れます。やはりアプリ開発・プログラミング学習をする上でIntel Macはまだまだ必要ですね。

送信したメールの内容
mailtestコマンド(仮送信先と添付ファイル)
--------------------------------------------------
mailtest test_recv@example.com mailtest.h
--------------------------------------------------
出力
--------------------------------------------------
Subject: 日本語
instr 日本語
outstr BF|K\8l // ISO-2022-JP
sub_jis: BF|K\8l
sub_64: GyRCRnxLXDhs // Base64
b_subject: GyRCRnxLXDhs
GetData開始
test
.
instr test

outstr test

GetData pass
from: noMac-mini.local
soc: 3
sockaddr: 0x7ffee0f0b0c8
IP adrress: 0x7ffee0f0b0cc
HELO 
>>>>>220 localhost ESMTP SubEthaSMTP null

MAIL FROM:
>>>>>250 localhost

RCPT TO:
>>>>>250 Ok

DATA
>>>>>250 Ok

Date: From: 
Subject: =?ISO-2022-JP?B??=
To: 
MIME-Version: 1.0
Content-Type: MULTIPART/mixed; BOUNDARY=

--
Content-Type: TEXT/plain; charset=ISO-2022-JP

--
Content-Type: application; name=; x-unix-mode=0777
Content-Transfer-Encoding: BASE64
Content-Description: 

----
.
>>>>>354 End data with <CR><LF>.<CR><LF>

QUIT
>>>>>250 Ok