[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);
}

参考サイト