プログラムリスト

smtpauth.c (薄い色の文字の部分はすべてのプログラムで共通)

/**********************************************************************
Winsock2を用いたSMTPサンプルプログラム
このサンプルプログラムはSMTPサーバを介してテストメールを自分のメールア
ドレスに送付します。
(1)smtpサーバ名,自分のパソコン名,自分のメールアドレス,ユーザID,パ
スワードは正しいものを指定してください。
(2)コンパイル時に「ws2_32.lib」をリンクしてください。
(3)本文で2バイトコード(漢字コードなど)を送信することは出来ません。
送信する場合はJISコードを使用し,本文ヘッダにそのことを記述してください。
(4)サブジェクトに2バイトコード(漢字コードなど)を送信することは出来
ません。送信する場合はmime変換をしてください。
**********************************************************************/

#define REQUEST_INFO
const char *smtpservername="smtp.mail.yahoo.co.jp";
const char *smtpclientname="happyday.tokyo-ct.ac.jp";
const char *mymailaddress="
watashi@yahoo.co.jp";
const char *userID="watashi";
const char *passwd="uso800";
const int port=587;

/* Import Library: Link with ws2_32.lib */
#include <stdio.h>
#include <string.h>
#include <winsock2.h>

/*ソケットsに文字列データdataを送信する関数*/
void sendData(SOCKET s, char *data)
{
    send(s,data,strlen(data),0);
}

/*ソケットsからバッファサイズbuffsizeのデータバッファdatabuffに
データを受け取る関数 返す値は取得バイト数*/
int receiveData(SOCKET s, char *databuff, int buffsize)
{
    char *p=databuff;
    int i;
    for (i=0;i<buffsize;i++) *p++=0;
    return recv(s,databuff,buffsize,0);
}

/**************** for printing information  HEAD ************************/
#ifdef REQUEST_INFO
void printWinsockInfo(WSADATA *wsaData)
{
    union {
        short int s;
        unsigned char c[2];
    } v,hv;
    if (wsaData==NULL) return;
    v.s=wsaData->wVersion;
    hv.s=wsaData->wHighVersion;
    printf("== winsock information ==\n");
    printf("winsock version   = %d.%d\n",v.c[0],v.c[1]);
    printf("winsock HighVer   = %d.%d\n",hv.c[0],hv.c[1]);
    printf("Description       = %s\n",wsaData->szDescription);
    printf("SystemStatus      = %s\n" ,wsaData->szSystemStatus);
    printf("Max Sockets       = %d\n" ,wsaData->iMaxSockets);
    printf("Max UDP byte size = %d\n" ,wsaData->iMaxUdpDg);
    printf("\n");
}
void printServerInfo(LPHOSTENT lpHost)
{
    int i;
    if (lpHost==NULL) return;
    printf("== server information ==\n");
    printf("host name      = %s\n" , lpHost->h_name);
    printf("address type   = %d\n" , lpHost->h_addrtype); /*AF_INETは2*/
    printf("address length = %d\n" , lpHost->h_length);
    for(i = 0 ; lpHost->h_aliases[i] ; i++) {
        printf("aliases        = %s\n" , lpHost->h_aliases[i]);
    }

    for(i = 0 ; lpHost->h_addr_list[i] ; i++) {
        IN_ADDR *ip;
        ip = (IN_ADDR *)lpHost->h_addr_list[i];
        printf("IP address     = %s\n" , inet_ntoa(*ip));
    }
    printf("\n");
}
void printServiceInfo(LPSERVENT lpServ)
{
    int i;
    if (lpServ==NULL) return;
    printf("== service information ==\n");
    printf("service name = %s\n" , lpServ->s_name);
    for(i = 0 ; lpServ->s_aliases[i] ; i++) {
        printf("aliases      = %s\n" , lpServ->s_aliases[i]);
    }
    printf("port number  = %d\n" , lpServ->s_port);
    printf("protocol     = %s\n" , lpServ->s_proto);
    printf("\n");
}
void printPortInfo(int port)
{
    printf("== port number information ==\n");
    printf("port number = %d\n" , ntohs((short)port));
    /*ntohsはホストバイトオーダーに戻す関数*/
    printf("\n");
}
#endif
/**************** for printing information  TAIL ************************/

/************************************************************
ソケットをオープンし,目的のサーバのポートに接続する
server:     サーバの名前(ドメイン名,ドメインを省略してはいけない)
            例えばaaaa.tokyo-ct.ac.jp
servicename:"smtp"とか"http"などのサービスの名前,
            次のところに名前の一覧がある
            Windowsシステムのsystem32\drivers\etc\services
service:    サービスのポート番号,smtpなら25,httpなら80など
            この情報は,servicenameが不明の場合に必要なだけで,
            通常は使われない
不成功ならINVALID_SOCKETを返す
*************************************************************/
SOCKET connectSocket(const char *server, char *servicename, short int service)
{
    WSADATA wsaData; /*winsock*/
    LPHOSTENT lpHost;
    LPSERVENT lpServ;
    SOCKET sock;
    SOCKADDR_IN sockAddr;
    int port; /*ポート番号*/
    int status;

    /*winsockをver1.1で初期化を要求*/
    status=WSAStartup(MAKEWORD(1, 1), &wsaData);
    if (status != 0) {
        fprintf(stderr,"WSAStartup() failed\n");
        return INVALID_SOCKET;
    }

    /*サーバ名からホストの情報を得る*/
    lpHost = gethostbyname(server);
    if(lpHost == NULL) {
        fprintf(stderr,"server name error\n");
        WSACleanup();
        return INVALID_SOCKET;
    }

    /*サービス情報からポート番号取得*/
    lpServ = getservbyname(servicename, NULL);
    if (lpServ == NULL) {
        port=htons(service);
        /*ホストバイトオーダーからネットワークバイトオーダー(ビッグエンディアン)に変換*/
    } else {
        port=lpServ->s_port;
    }
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printWinsockInfo(&wsaData); /*Winsocの情報表示*/
        printServerInfo(lpHost);    /*サーバの情報表示*/
        printServiceInfo(lpServ);   /*サービスの情報表示*/
        printPortInfo(port);        /*ポート番号の情報表示*/
    #endif

    /*ソケットオープン*/
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) {
        fprintf(stderr,"socket() error\n");
        WSACleanup();
        return INVALID_SOCKET;
    }

    /*ソケット結合*/
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = port;
    sockAddr.sin_addr = *((LPIN_ADDR)*lpHost->h_addr_list);
    status=connect(sock, (PSOCKADDR)&sockAddr, sizeof(SOCKADDR_IN));
    if (status) {
        fprintf(stderr,"connect() error\n");
        closesocket(sock);
        WSACleanup();
        return INVALID_SOCKET;
    }
    return sock;
}

#define sndBufSize 100*1024
#define rcvBufSize 1024

typedef struct {
    char sender[128];      /*送信者すなわち自分のメールアドレス*/
    char receiver[128];    /*送信先のメールアドレス*/
    char subject[1024];    /*サブジェクト(件名)*/
    char body[sndBufSize]; /*メール本体*/
} mail_t;

//mime変換のためのテーブル      0123456789012345678901234567890123456789012345678901234567890123
const char MimeEncodeTable[70]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

//文字列intext(normal)を文字列otext(mime encoded)に変換
//BASE64を利用したMIME変換
void PureMimeEncode(char *intext, char *otext, int len)
{
    unsigned char *p;
    unsigned long int x,x1;
    unsigned char tmp[8]={0,0,0,0,0,0,0,0};
    char one[4]="A";
    int add,len1,i,j;
    add=0;
    len1=len;
    if (len%3!=0) {
        add= 3-(len%3);
        len1=len/3*3;
    }
    p=(unsigned char *)intext;
    *otext=0;
    for (i=0; i<len1; i+=3) {
        x=(p[0]<<16)+(p[1]<<8)+p[2];
        x1=(x>>18)&0x3f; one[0]=MimeEncodeTable[x1]; strcat(otext,one);
        x1=(x>>12)&0x3f; one[0]=MimeEncodeTable[x1]; strcat(otext,one);
        x1=(x>>06)&0x3f; one[0]=MimeEncodeTable[x1]; strcat(otext,one);
        x1=(   x )&0x3f; one[0]=MimeEncodeTable[x1]; strcat(otext,one);
        p+=3;
    }
    if (add) {
        for (j=0 ; i<len; i++,j++) tmp[j]=*p++;
        x=(tmp[0]<<16)+(tmp[1]<<8)+tmp[2];
        x1=(x>>18)&0x3f; one[0]=MimeEncodeTable[x1]; strcat(otext,one);
        x1=(x>>12)&0x3f; one[0]=MimeEncodeTable[x1]; strcat(otext,one);
        x1=(x>>06)&0x3f; one[0]=MimeEncodeTable[x1]; strcat(otext,one);
        x1=(   x )&0x3f; one[0]=MimeEncodeTable[x1]; strcat(otext,one);
    }
    //add=0 -> del=0, add=1 -> del=1, add=2 -> del=2
    p=otext+strlen(otext)-1;
    for (i=0; i<add; i++) *p--='=';
}

//ユーザIDuserとパスワードpassからauth文字列(mime(user\0user\0pass))を作る
void makeAuthPlain(char *user, char *pass, char *auth)
{
    int num,i;
    char plain[256];
    strcpy(plain,user);
    strcat(plain,"\t");
    strcat(plain,user);
    strcat(plain,"\t");
    strcat(plain,pass);
    //puts(plain);
    num=strlen(plain);
    for (i=0;i<num;i++) if (plain[i]=='\t') plain[i]=0;
    PureMimeEncode(plain,auth,num);
}

typedef struct {
    char smtpserver[128];
    int port;
    char userID[128];
    char passwd[128];
    char authplainstr[128];
} smtp_t;

void setAuthPlainString(smtp_t *smtp)
{
    makeAuthPlain(smtp->userID, smtp->passwd, smtp->authplainstr);
}

/*SMTPサーバsmtpにメールmailを送る関数 失敗したら0以外を戻す*/
int sendMail(smtp_t *smtp, const char *smtpclient, mail_t *mail)
{
    SOCKET sock;
    static char sndBuf[sndBufSize],rcvBuf[rcvBufSize];
    setAuthPlainString(smtp);

    /*SMTPサーバへの接続とリプライ受信*/
    sock=connectSocket(smtp->smtpserver,"secure_smtp",(short int)smtp->port);
    if (sock == INVALID_SOCKET) return 1;
    receiveData(sock,rcvBuf,rcvBufSize);
    if ('3'<rcvBuf[0]) {
        printf("** ERROR ** %s\n",rcvBuf);
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("connecting Socket\n");
        printf("receiving : %s\n",rcvBuf);
    #endif

    /*"EHLO"コマンドの送信とリプライ受信*/
    /*クライアントの認証に失敗する場合はここの部分にsmtpサーバ名を書く*/
    sprintf(sndBuf,"EHLO %s \r\n",smtpclient);
    sendData(sock, sndBuf);
    receiveData(sock,rcvBuf,rcvBufSize);
    if ('3'<rcvBuf[0]) {
        printf("** ERROR ** %s\n",rcvBuf);
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
        printf("receiving : %s\n",rcvBuf);
    #endif

    /*"AUTH PLAIN"コマンドの送信とリプライ受信*/
    sprintf(sndBuf,"AUTH PLAIN %s\r\n",smtp->authplainstr);
    sendData(sock, sndBuf);
    receiveData(sock,rcvBuf,rcvBufSize);
    if ('3'<rcvBuf[0]) {
        printf("** ERROR ** %s\n",rcvBuf);
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
        printf("receiving : %s\n",rcvBuf);
    #endif

    /*"MAIL FROM"コマンドの送信とリプライ受信*/
    sprintf(sndBuf,"MAIL FROM:<%s>\r\n",mail->sender);
    sendData(sock, sndBuf);
    receiveData(sock,rcvBuf,rcvBufSize);
    if ('3'<rcvBuf[0]) {
        printf("** ERROR ** %s\n",rcvBuf);
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
        printf("receiving : %s\n",rcvBuf);
    #endif

    /*"RCPT TO"コマンドの送信とリプライ受信*/
    /*複数のあて先に同報送信する場合は,RCPT TO"コマンドの送信とリプライ受信をここに並べればよい*/
    sprintf(sndBuf,"RCPT TO:<%s>\r\n",mail->receiver);
    sendData(sock, sndBuf);
    receiveData(sock,rcvBuf,rcvBufSize);
    if ('3'<rcvBuf[0]) {
        printf("** ERROR ** %s\n",rcvBuf);
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
        printf("receiving : %s\n",rcvBuf);
    #endif

    /*"DATA"コマンドの送信とリプライ受信*/
    sprintf(sndBuf,"DATA\r\n");
    sendData(sock, sndBuf);
    receiveData(sock,rcvBuf,rcvBufSize);
    if ('3'<rcvBuf[0]) {
        printf("** ERROR ** %s\n",rcvBuf);
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
        printf("receiving : %s\n",rcvBuf);
    #endif

    /*本文ヘッダの送信(サブジェクト)*/
    sprintf(sndBuf,"Subject: %s\r\n",mail->subject);
    sendData(sock, sndBuf);
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
    #endif

    /*本文ヘッダの送信(「To」行)*/
    sprintf(sndBuf,"To: %s\r\n",mail->receiver);
    sendData(sock, sndBuf);
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
    #endif

    /*本文ヘッダの送信(「From」行)*/
    sprintf(sndBuf,"From: %s\r\n",mail->sender);
    sendData(sock, sndBuf);
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
    #endif

    /*本文ヘッダの送信(ヘッダ終了の意味の空行)*/
    sprintf(sndBuf,"\r\n");
    sendData(sock, sndBuf);
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
    #endif
    /*そのほかの本文ヘッダがある場合は本文本体送信前に記述すればよい*/

    /*本文本体の送信*/
    sprintf(sndBuf,"%s\r\n",mail->body);
    sendData(sock, sndBuf);
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
    #endif

    /*本文終了マークの送信*/
    sprintf(sndBuf,".\r\n");
    sendData(sock, sndBuf);
    receiveData(sock,rcvBuf,rcvBufSize);
    if ('3'<rcvBuf[0]) {
        printf("** ERROR ** %s\n",rcvBuf);
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
        printf("receiving : %s\n",rcvBuf);
    #endif

    /*"QUIT"コマンドの送信とリプライ受信*/
    sprintf(sndBuf,"QUIT\r\n");
    sendData(sock, sndBuf);
    receiveData(sock,rcvBuf,rcvBufSize);
    if ('3'<rcvBuf[0]) {
        printf("** ERROR ** %s\n",rcvBuf);
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    #ifdef REQUEST_INFO /*情報要求がある場合(学習のため)*/
        printf("sending   : %s",sndBuf);
        printf("receiving : %s\n",rcvBuf);
    #endif

    closesocket(sock);
    WSACleanup();
    return 0;
}

int main()
{
    mail_t mymail;
    smtp_t smtp;
    strcpy(mymail.subject,"test mail"); /*メールのサブジェクトの設定*/
    strcpy(mymail.body,"test mail body  Hello from Tokyo."); /*メールの本文の設定*/
    strcpy(mymail.sender,mymailaddress); /*送信者アドレスの設定*/
    strcpy(mymail.receiver,mymailaddress); /*受信者アドレスの設定*/
    strcpy(smtp.smtpserver,smtpservername); /*smtpサーバ名の設定*/
    smtp.port=port; /*ポート番号のセット*/
    strcpy(smtp.userID,userID); /*認証用ユーザIDの設定*/
    strcpy(smtp.passwd,passwd); /*パスワードの設定*/
    sendMail(&smtp, smtpclientname, &mymail);
    return 0;
}