プログラムリスト

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

/**********************************************************************
Winsock2を用いたPop3サンプルプログラム
このサンプルプログラムはpop3サーバにloginし,
メニューによりメールリストを表示したり,メール本文を保存したりして終了します。
(1)サーバ名はpopプロトコルを受け付けるサーバ名を指定してください。
(2)username,passwordは自分のものを使ってください。
(3)コンパイル時に「ws2_32.lib」をリンクしてください。
**********************************************************************/

#define REQUEST_INFO
const char *servername="pops.portmail.jp";
const char *username="xxxxx@tokyo-ct.ac.jp";
const char *password="xxxxxxxxxx";


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

/**********************************************************************/
/*                            文字列変換用関数                        */
//次の2つが外から呼ばれる
//void makeMineDecodeTable(void)
//int mailconv(char *buff,int size)

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

//逆mime変換のためのテーブル
char MimeDecodeTable[0x80];

//逆mime変換のためのテーブル作成(逆mime変換作業前に作っておく必要がある)
void makeMineDecodeTable(void)
{
    int i;
    for (i=0; i<0x40; i++) MimeDecodeTable[MimeEncodeTable[i]]=i;
    for (i=0; i<0x80; i++) {
        printf(" %02x,",MimeDecodeTable[i]);
        if (i%0x10==0xf) printf("\n");
    }
}

//文字列intext(mime encoded)を文字列otext(normal)に変換
//otextの有効文字数を返す
int PureMimeDecode(char *intext, char *otext)
{
    char *p;
    char three[4]="ABC";
    unsigned long int x;
    int del,op,len,i;
    len=strlen(intext);
    del=0;
    if (len) {
        while (intext[len-1-del]=='=') del++;
    }
    //printf("del=%d\n",del);
    op=0;
    p=intext;
    while (*p) {
        x=MimeDecodeTable[*p++];    //123456
        x<<=6;                       //123456000000
        if (!*p) break;
        x|=MimeDecodeTable[*p++];   //123456123456
        x<<=6;                       //123456123456000000
        if (!*p) break;
        x|=MimeDecodeTable[*p++];   //123456123456123456
        x<<=6;                       //123456123456123456000000
        if (!*p) break;
        x|=MimeDecodeTable[*p++];   //123456123456123456123456
        three[2]=(char)x;
        x>>=8;
        three[1]=(char)x;
        x>>=8;
        three[0]=(char)x;
        for (i=0; i<3; i++,op++) otext[op]=three[i];
    }
    otext[op]=0;
    return op-del;
}

//文字列intext(mime encoded)を文字列otext(normal)に変換
//otextの有効文字数を返す
int MimeDecode(char *intext, char *otext)
{
    char *p,*q;
    int num;
    q=NULL;
    if (strstr(intext,"=?ISO-2022-JP?B?")) {
        q=strstr(intext,"?=");
        if (q) *q=0;
        p=intext+strlen("=?ISO-2022-JP?B?");
    } else {
        p=intext;
    }
    num=PureMimeDecode(p,otext);
    if (q) *q='?';
    return num;
}

//strstr start From First character with toupper
int strstrfftu(char *str, char *key)
{
    int same=1;
    char tmp;
    while (*key) {
        tmp=toupper(*str);
        if (tmp!=*key) {
            same=0;
            break;
        }
        key++; str++;
    }
    return same;
}

//文字列intext(mime encoded)を文字列otext(normal)に変換
//混合文字列に対して行う
void strMimeDecode(char *intext, char *otext)
{
    char *next;
    char tmp[10*1024];
    while (*intext) {
        if (strstrfftu(intext,"=?ISO-2022-JP?B?")) {
            next=strstr(intext,"?=")+2;
            MimeDecode(intext,tmp);
            strcpy(otext,tmp);
            otext+=strlen(tmp);
            intext=next;
        } else {
            *otext++=*intext++;
        }
    }
}

//文字列jistext(JIS)を文字列sjistext(Shift JIS)に変換
//改行は\r\nから\nに変換
void Jisstr2Sjisstr(char *jistext, char *sjistext)
{
    char one[4]="A";
    char two[4]="AB";
    unsigned short int sjis,jis;
    const char shiftin[4]={ 0x1b, 0x24, 0x42, 0 };
    const char shiftout[4]={ 0x1b, 0x28, 0x42, 0 };
    unsigned char *p=(unsigned char *)jistext;
    int kanjimode=0;
    while (*p) {
        if (strstr((char *)p,shiftin)==(char *)p) {
            p+=2;
            kanjimode=1;
        } else if (strstr((char *)p,shiftout)==(char *)p) {
            p+=2;
            kanjimode=0;
        } else if (kanjimode==1) {
            jis=((*p)<<8);
            p++;
            if (!(*p)) break;
            jis+=((*p)&0xff);
            sjis=_mbcjistojms(jis);
            sjistext[1]=(char)sjis;
            sjistext[0]=(char)(sjis>>8);
            sjistext+=2;
        } else if (*p=='\r') {
            /*コピーしない*/
        } else {
            *sjistext++=*p;
        }
        p++;
    }
    *sjistext=0;
}

//\nの次がスペースやタブだったら詰める(改行は\nのみの文字列が対象)
void reduceLine(char *in, char *out)
{
    while (*in && *in!='\n') *out++=*in++;
    while (*in) {
        if (*in=='\n' && (in[1]==' ' || in[1]=='\t')) {
            in+=2;
            while (*in==' ' ||*in=='\t') in++;
        }
        *out++=*in++;
    }
    *out=0;
}

//buffの位置から走査し最初の「"」から次の「"」までの文字列を抜き出す
void getquateword(char *buff, char *txt)
{
    while (*buff!='\"') buff++;
    buff++;
    while (*buff!='\"') *txt++=*buff++;
    *txt=0;
}

//buffの位置から\r\nまでを抜き出しlineにコピーする
//buffの位置の次の行の先頭を返す
//lineに\r\nは含ませない
char *getline(char *buff,char *line)
{
    while(buff[0]!='\r'||buff[1]!='\n') *line++=*buff++;
    *line=0;
    return buff+2;
}

//各キーワードごとの表示にする
/* 例
+OK 2152 octets
Return-Path: <xxxxx@tokyo-ct.ac.jp>
Received: from pwm-rg500-01.portmail.jp (pwm-rg500-01.portmail.jp [10.56.25.21])by pwm-m500-01.portmail.jpwith ESMTP id AEX53608;Sat, 22 Nov 2008 17:42:33 +0900 (JST)
Received: from localhost (localhost [127.0.0.1])by pwm-rg500-01.portmail.jp (MOS 3.8.4-GA)id AJQ57917;Sat, 22 Nov 2008 17:42:33 +0900 (JST)
Received: from pwm-rg100-04.portmail.jp (pwm-rg100-04.portmail.jp [10.56.25.42])by pwm-rg500-01.portmail.jpwith ESMTP id AJQ57913;Sat, 22 Nov 2008 17:42:33 +0900 (JST)
Received: from pwm-m500-01.portmail.jp (pwm-m500-01.portmail.jp [10.56.25.10])by pwm-rg100-04.portmail.jpwith ESMTP id AID70274;Sat, 22 Nov 2008 17:42:32 +0900 (JST)
Received: (from pwm-m500-01.portmail.jp [10.56.25.31])by pwm-m500-01.portmail.jp (MOS 3.8.4-GA)with HTTP/1.1 id AEX53602 (AUTH xxxxxx@tokyo-ct.ac.jp);Sat, 22 Nov 2008 17:42:32 +0900 (JST)
From: xxxxxx <xxxxxx@tokyo-ct.ac.jp>
Subject: 11月29日の件
To: xxxx@tokyo-ct.ac.jp, xxxxx@tokyo-ct.ac.jp, xxxxxx@tokyo-ct.ac.jp,xxxxxxx@tokyo-ct.ac.jp, xxxxxx@tokyo-ct.ac.jp
Cc: xxxxx@tokyo-ct.ac.jp, xxxxx@tokyo-ct.ac.jp, xxxxx@tokyo-ct.ac.jp
X-Mailer: bbbbb Webmail Direct 3.8.4-GA
MIME-Version: 1.0
Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit
Message-Id: <20081122174232.AEX53602@pwm-m500-01.portmail.jp>
Date: Sat, 22 Nov 2008 17:42:32 +0900 (JST)
X-Junkmail-Status: score=10/50, host=pwm-rg100-04.portmail.jp
X-Junkmail-SD-Raw: score=unknown,refid=str=0001.0A150203.4927C5F9.0032,ss=1,fgs=0,ip=10.56.25.10,so=2007-08-24 13:43:07,dmn=5.7.1/2008-09-02
X-bbbbb-Loop-Id: bc332aab06d972e19852c1a25e6e711ebc332aab06d972e19852c1a25e6e711e
Body:
XXXX様
XXXX様
XXXX様
XXXX様
XXXX様
XXXX様

お世話になっております。XX工学科のXXです。

11月29日(土)の会議は、
12:00からXXX室で実施することになりました。
休日のところ、大変恐縮ですが、
11:30にXXX室にご集合いただきたく、
よろしくお願いいたします。
*/
int mailconv(char *buff,int size)
{
    char *headerend,*p;
    char boundarystr[128];
    char *body;
   
    buff[size]=0;
    headerend=strstr(buff,"\r\n\r\n");
    if (headerend) *headerend=0;
    body=headerend+4;

    strMimeDecode(buff,buff);
    Jisstr2Sjisstr(buff,buff);
    reduceLine(strchr(buff,'\n')+1,buff);
// reduceLine(buff,buff);
    p=strstr(buff,"boundary=");
    if (p) {
        getquateword(p,boundarystr);
        if(p) {
            p=strstr(body,boundarystr);
            p=strstr(p,"\r\n\r\n");
            body=p+4;
            p=strstr(body,boundarystr);
            if (p) {
                *p--=0;
                while (*p!='\n') *p--=0;
                *p--=0;*p--=0;
                p--;
                if (p[0]=='\r' && p[1]=='\n') p[0]=0;
            }
        }
    } else {
        p=strstr(body,"\r\n.\r\n");
        if (p) *p=0;
    }
    Jisstr2Sjisstr(body,body);
    strcat(buff,"\nBody: \n");
    strcat(buff,body);
    return strlen(buff);
}

/**********************************************************************/

/*ソケット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 1024
#define rcvBufSize 1024*1024
//#define rcvBufSize 100*1024+535
static char sndBuf[sndBufSize],rcvBuf[rcvBufSize];

//最終バイトのアドレスより終了形かどうかを判定
//size直前の読み出しバイト数
int isTail(char *lastbyte, int size)
{
    char *p1=lastbyte-3;
    int result=0;
    if (3<size) {
        if (p1[0]=='\n' && p1[1]=='.' && p1[2]=='\r' && p1[3]=='\n') result=1;
    } else if (size==3) {
        if (p1[1]=='.' && p1[2]=='\r' && p1[3]=='\n') result=1;
    } else if (size==2) {
        if (p1[2]=='\r' && p1[3]=='\n') result=1;
    } else if (size==1) {
        if (p1[3]=='\n') result=1;
    }
    return result;
}

/*メールおよびリスト専用でソケットsからバッファサイズbuffsizeのデータバッファdatabuffに
データを受け取る関数 返す値は取得バイト数 失敗の場合は0を返す*/
int receiveMailData(SOCKET s, char *databuff, int buffsize, int limit)
{
    int totalsize,size,completed;
    static char throwaway[1024],*throwaway3=throwaway+3;
    char *p;
    totalsize=0;
    do {
        size=receiveData(s, databuff+totalsize, buffsize-totalsize);
        totalsize+=size;
        Sleep(10);
        if (1<size && databuff[0]=='-') {
            databuff[0]=0;
            return 0;
        }
        completed=isTail(databuff+totalsize-1,size);
        printf("[size,totalsize= %d %d]\n",size,totalsize);
        p=databuff+totalsize-1;
    } while (!completed && totalsize<limit);
    while (!completed) {
        throwaway[0]=p[-2];
        throwaway[1]=p[-1];
        throwaway[2]=p[0];
        size=receiveData(s, throwaway3, 1000);
        Sleep(10);
        printf(size!=1000?"[size= %d]\n":".",size);
        completed=isTail(throwaway3+size-1,size);
        p=throwaway3+size-1;
    }
    printf("xxx %d\n",totalsize);
    return totalsize;
}


/*loginし,メールリストを表示して終了する*/
int pop3(const char *server,const char *user, const char *pass)
{
    SOCKET sock;
    int loop=1, mailID;
    char buff[128];
  FILE *fp;
    char fname[128];
    int totalsize;

    /*POPサーバへの接続*/
    sock=connectSocket(server,"pop3",110);
    if (sock == INVALID_SOCKET) return 1;
    printf("connecting Socket\n");

    /*サーバから受信*/
    receiveData(sock,rcvBuf,rcvBufSize);
    printf("receiving : %s\n",rcvBuf);

    /*ユーザlogin*/
    sprintf(sndBuf,"USER %s\r\n",user);
    sendData(sock, sndBuf);
    printf("sending   : %s",sndBuf);

    receiveData(sock,rcvBuf,rcvBufSize);
    printf("receiving : %s\n",rcvBuf);

    /*パスワード*/
    sprintf(sndBuf,"PASS %s\r\n",pass);
    sendData(sock, sndBuf);
    printf("sending   : %s",sndBuf);

    receiveData(sock,rcvBuf,rcvBufSize);
    printf("receiving : %s\n",rcvBuf);

    while (loop) {
        printf("\n\n***********   menu   ***********\n");
        printf("   L   : print mail list\n");
        printf("number : save the mail in the mail list\n");
        printf("   Q   : quit\n");
        printf(">");
        gets(buff);
        if (buff[0]=='L' || buff[0]=='l' ) {
            /*メールリストの取得*/
            sprintf(sndBuf,"LIST\r\n");
            sendData(sock, sndBuf);
            printf("sending   : %s",sndBuf);
            Sleep(100);

            totalsize=receiveMailData(sock,rcvBuf,rcvBufSize,(int)(rcvBufSize*0.9));
            printf("receiving : %s\n",rcvBuf);
            printf("[totalsize= %d]\n",totalsize);
            Sleep(100);
            fp=fopen("maillist.txt","wb");
            fwrite(rcvBuf,totalsize,1,fp);
            fclose(fp);
        } else if (buff[0]=='Q' || buff[0]=='q' ) {

            /*終了*/
            sprintf(sndBuf,"QUIT\r\n");
            sendData(sock, sndBuf);
            printf("sending   : %s",sndBuf);
            Sleep(500);

            receiveData(sock,rcvBuf,rcvBufSize);
            printf("receiving : %s\n",rcvBuf);
            Sleep(500);
            loop=0;
        } else if (sscanf(buff,"%d",&mailID)==1) {
            rcvBuf[0]=0;
            sprintf(sndBuf,"RETR %d\r\n", mailID);
            sendData(sock, sndBuf);
            printf("sending   : %s",sndBuf);
            Sleep(100);

            totalsize=receiveMailData(sock,rcvBuf,rcvBufSize,(int)(rcvBufSize*0.9));
            rcvBuf[totalsize]=0;
            printf("receiving : %s\n",rcvBuf);

            if (totalsize!=0) totalsize=mailconv(rcvBuf, totalsize);
            rcvBuf[totalsize]=0;
            printf("[totalsize= %d]\n",totalsize);

            sprintf(fname,"mail%05d.txt",mailID);
            fp=fopen(fname,"w");
            fputs(rcvBuf, fp);
            fclose(fp);
            printf("Completed\n");
        }
    }

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

int main()
{
    makeMineDecodeTable();
    pop3(servername,username,password);
    getchar();
    return 0;
}