root/FetchMail.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. check_uid_cache
  2. FetchMail

/*
 * Copyright (C) 2002-2003 chik & s.hiranaka
 * For license terms, see the file COPYING in this directory.
 */

#include "stdafx.h"
#include "Pochy.h"
#include "MainFrm.h"
#include "Sock.h"
#include "CodeConvert.h"
#include "fetchmail.h"
#include "Filter.h"
#include "PassPhraseDlg.h"
//#include "_regex.h"                           // 正規表現ライブラリ用
#include "lib.h"
#include "AccountView.h"
#include "mmsystem.h"
#include "md5ify.h"

BOOL check_uid_cache(CString uid, CString path)
{
        CString buf;
        int Num;
        
        CStdioFile read_file;
        if(!read_file.Open(path.GetBuffer(1), CFile::modeRead | CFile::typeText)){
                return FALSE;
        }

        Num = 0;
        while(read_file.ReadString(buf)){
                buf.TrimRight();
                if(buf == uid){
                        return TRUE;
                        break;
                }
        }
        return FALSE;
}

UINT FetchMail(LPVOID pParam)
{
        CHeaderInfo hi;
        CSock pop_socket;
        CStringArray uidl_array;
        CStringArray mail;

        CString user;
        CString pass;
        CString apop;
        CString addr;
        CString ip;
        CString port;
        CString buf;
        CString md5_in;
        CString md5_out;
        CString uid_cache_path;
        CString uid;
        CString last_uid;
        CString message;

        char list[256];
        char retr[256];
        char uidl[256];
        char dele[256];
        char stat[] = "stat\r\n";
        char quit[] = "quit\r\n";
        char *token;

        int how_many_mail = 0;
        int mail_size = 0;
        int size;
        int get_size = 0;
        int start;
        int cnt;
        int delete_mail;

        BOOL fetched = FALSE;

        CPochyApp *app = (CPochyApp *)AfxGetApp();
        CMainFrame *mf = (CMainFrame *)app->m_pMainWnd;

        // 現在選択されているアカウントを取得しておく。
        CString account = (char *)pParam;

        CSummaryView *sv = mf->m_pListV;
        CAccountView *av = mf->m_pAcntV;

        // メインフレームの受信ボタンを無効にする
        mf->UpdateButton(FALSE, account);

        // メール保存先決定のためのフィルタ等の設定
        CFilter filter(app->m_app_path+"\\"+account);

        // account.iniファイル用の準備
        CString path = app->m_app_path+"\\"+account+"\\account.ini";
/*
        DWORD GetPrivateProfileString(
        LPCTSTR lpAppName , LPCTSTR lpKeyName ,
        LPCTSTR lpDefault ,
        LPTSTR lpReturnedString , DWORD nSize ,
        LPCTSTR lpFileName
);
*/
        // getting pop server address from ini file.
        GetPrivateProfileString("pop", "server_addr", "", addr.GetBuffer(BUF_LENGTH), BUF_LENGTH, path);
        addr.ReleaseBuffer();
        // getting pop port num from ini file.
        GetPrivateProfileString("pop", "port", "", port.GetBuffer(BUF_LENGTH), BUF_LENGTH, path);
        port.ReleaseBuffer();

        try{
                if(INVALID_SOCKET == pop_socket.Connect6(addr.GetBuffer(0), port.GetBuffer(0))){
                        message.Format("cannot connect %s:%s", addr.GetBuffer(0), port.GetBuffer(0));
                        throw message.GetBuffer(0);
                }       
                pop_socket.SockRead(buf);
                // preparing for apop.
                if(GetPrivateProfileInt("pop", "apop", 0, path)){
                        md5_in = buf.Mid(buf.Find("<"), buf.Find(">")-buf.Find("<")+1);
                }
                av->SetStatusBarText(buf.GetBuffer(0), account);
                buf.Empty();

                // get user name from ini file.
                GetPrivateProfileString("pop", "user", "", user.GetBuffer(BUF_LENGTH), BUF_LENGTH, path);
                user.ReleaseBuffer();
        
                // get password from ini file.
                GetPrivateProfileString("pop", "pass", "", pass.GetBuffer(BUF_LENGTH), BUF_LENGTH, path);
                pass.ReleaseBuffer();
                if(pass.IsEmpty() && !app->m_pop_passphrase.IsEmpty())
                        pass = app->m_pop_passphrase;
                else if(pass.IsEmpty() && app->m_pop_passphrase.IsEmpty()){
                        // if getting no password from ini file, then show dialog for input password.
                        CPassPhraseDlg passDlg;
                        passDlg.SetFlag(POP);
                        passDlg.DoModal();
                        pass = app->m_pop_passphrase;
                }

                if(GetPrivateProfileInt("pop", "apop", 0, path)){
                        // if using apop, below is done.
                        md5_in += pass;
                        md5_out = MD5Digest((unsigned char *)md5_in.GetBuffer(0));
                        apop = "apop "+user+" "+md5_out+"\r\n";
                        send(pop_socket.m_sock, apop.GetBuffer(0), apop.GetLength(), 0);
                        if(pop_socket.SockRead(buf) == -1)
                                throw "error is occured in pass session";
                        if(buf.Find("-ERR") == 0 || buf.Find("-err") == 0){
                                message.Format("%s", buf.GetBuffer(0));
                                throw message.GetBuffer(0);
                        }
                        buf = apop;
                        av->SetStatusBarText(buf.GetBuffer(0), account);
                        buf.Empty();
                }else{
                        // below is normal authentification using user and pass command.
                        // user
                        user = "user " + user + "\r\n";
                        send(pop_socket.m_sock, user.GetBuffer(10), user.GetLength(), 0);
                        if(pop_socket.SockRead(buf) == -1)
                                throw "error is occured in user session";
                        if(buf.Find("-ERR") == 0 || buf.Find("-err") == 0){
                                message.Format("%s", buf.GetBuffer(0));
                                throw message.GetBuffer(0);
                        }
                        av->SetStatusBarText(buf.GetBuffer(0), account);
                        buf.Empty();

                        // pass
                        pass = "pass "+pass+"\r\n";
                        send(pop_socket.m_sock, pass.GetBuffer(0), pass.GetLength(), 0);
                        if(pop_socket.SockRead(buf) == -1)
                                throw "error is occured in pass session";
                        if(buf.Find("-ERR") == 0 || buf.Find("-err") == 0){
                                message.Format("%s", buf.GetBuffer(0));
                                throw message.GetBuffer(0);
                        }
                        buf = "pass ********";
                        av->SetStatusBarText(buf.GetBuffer(0), account);
                        buf.Empty();
                }

                // stat
                send(pop_socket.m_sock, stat, strlen(stat), 0);
                pop_socket.SockRead(buf);
                if(buf.Find("-ERR") == 0 || buf.Find("-err") == 0){
                        message.Format("%s", buf.GetBuffer(0));
                        throw message.GetBuffer(0);
                }
                av->SetStatusBarText(buf.GetBuffer(0), account);
                token = strtok(buf.GetBuffer(0), " ");
                token = strtok(NULL, " ");
                how_many_mail = atoi(token);
                buf.ReleaseBuffer();
                buf.Empty();

                start = 0;

                // .iniファイルから前回の最終取得メールのuidを取得
                GetPrivateProfileString("pop", "last_uid", "", last_uid.GetBuffer(BUF_LENGTH), BUF_LENGTH, path);
                last_uid.ReleaseBuffer();
                // check which mail is already received
                if(!last_uid.IsEmpty()){
                        // uidlコマンドの送出
                        send(pop_socket.m_sock, "uidl\r\n", strlen("uidl\r\n"), 0);
                        pop_socket.SockRead(buf);
                        buf.Empty();
                        // uidのリストをメモリへ格納する
                        while(buf != "\x2E"){  // 改行コードがループ中で削除されるため
                                buf.Empty();
                                buf.ReleaseBuffer();
                                pop_socket.SockRead(buf);
                                buf.TrimRight();  // 改行コードの削除
                                token = strtok(buf.GetBuffer(0), " ");
                                token = strtok(NULL, " ");
                                uidl_array.Add(token);
                        }
                        buf.Empty();
                        buf.ReleaseBuffer();
                        // uidlでとって来たリストの何番目にlast_uidがあるか確認
                        cnt = 0;
                        while(cnt < uidl_array.GetSize()){
                                if(last_uid == uidl_array.GetAt(cnt)){
                                        start = cnt+1;
                                        break;
                                }
                                cnt++;
                        }
                }

                // path of already fetched mail uid list file.
                uid_cache_path = app->m_app_path+"\\"+account+"\\uid";

                // if no mail in server spool, delete uid file.
                if(how_many_mail == 0)
                        DeleteFile(uid_cache_path.GetBuffer(0));

                // decide whether or not deleting mail.
                delete_mail = GetPrivateProfileInt("pop", "delete_mail", 0, path);

                // below is loop for getting mail.
                for(int i = start; i < how_many_mail; i++){
                        fetched = TRUE;
                        if(start == 0){
                                // getting uid of currently fetched mail.
                                wsprintf(uidl, "uidl %d\r\n", i+1);
                                send(pop_socket.m_sock, uidl, strlen(uidl), 0);
                                pop_socket.SockRead(buf);
                                buf.TrimRight();
                                uid = strtok(buf.GetBuffer(0), " ");
                                uid = strtok(NULL, " ");
                                uid = strtok(NULL, " ");        
                                buf.Empty();
                                if(check_uid_cache(uid, uid_cache_path)){
                                        buf.Format("%d/%d checking which is already received", i+1, how_many_mail);
                                        av->SetStatusBarText(buf.GetBuffer(0), account);
                                        buf.Empty();
                                        if(i+1 == how_many_mail){
                                                WritePrivateProfileString("pop", "last_uid", uid, path);
                                        }
                                        uid.Empty();
                                        continue;
                                }
                        }

                        // getting size of mail (may be no need)
                        wsprintf(list, "list %d\r\n", i+1);
                        send(pop_socket.m_sock, list, strlen(list), 0);
                        pop_socket.SockRead(buf);
                        token = strtok(buf.GetBuffer(0), " ");
                        token = strtok(NULL, " ");
                        token = strtok(NULL, " ");
                        mail_size = atoi(token);
                        buf.ReleaseBuffer();
                        buf.Empty();

                        // sending retr command
                        wsprintf(retr, "retr %d\r\n", i+1);
                        send(pop_socket.m_sock, retr , strlen(retr), 0);
                        pop_socket.SockRead(buf);
                        buf.Empty();
#if 1
                        // retrieving mail
                        get_size = 0;
                        buf.Empty();
                        BOOL is_header = TRUE;
                        while(1){
                                buf.Empty();
                                if((size = pop_socket.SockRead(buf)) == -1)
                                        throw "while retrieving mail, accident is occured !";
                                get_size += size;
                                if(buf == "\x2E\x0D\x0A") break; // if".\r\n" show up, break loop.
                                if(buf == "\x0D\x0A" && is_header){
                                        hi.DoIt(mail);
                                        buf.TrimRight('\x0A');
                                        buf.TrimRight('\x0D');
                                        buf += "\x0D\x0A";
                                        mail.Add(buf);

                                        // display some info about current fetching mail on statusbar.
                                        message.Format("%d/%d %s", i+1, how_many_mail, hi.GetSubject());
                                        av->SetStatusBarText(message.GetBuffer(0), account);
                                        is_header = FALSE;
                                        continue;
                                }
                                buf.TrimRight('\x0A');
                                buf.TrimRight('\x0D');
                                buf += "\x0D\x0A";
                                if(buf.Find(".") == 0){
                                        buf.Delete(0, 1);
                                }
                                mail.Add(buf);
                                message.Format("%d/%d %d/%d %s", i+1, how_many_mail, get_size, mail_size, hi.GetSubject());
                                av->SetStatusBarText(message.GetBuffer(0), account);
                        }
                        if(g_cstra_getsize(mail) == 0){
                                throw "recived empty mail, may be some socket problem !";
                        }
#endif
                        // プログレスバーを進行させる
                        av->SetStatusBarProgress(i+1-start, how_many_mail-start, account);

                        // 取得したメールを保存するパス->フィルタの実装はここをいじる
                        CString mail_path = filter.GetPath(hi);

                        // メールの保存先をstatusbarに表示する
                        if(mail_path.IsEmpty()){
                                message += " ------> inbox";
                                av->SetStatusBarText(message.GetBuffer(0), account);
                                message.Empty();
                        }else{
                                message += " ------> "+mail_path.Mid(mail_path.ReverseFind('\\'));
                                av->SetStatusBarText(message.GetBuffer(0), account);
                                message.Empty();
                        }
#if 1
                        // 取得したメールを適切な場所へ保存
                        // 現在listviewに表示している内容に追加があった場合は画面を更新
                        if(mail_path.IsEmpty()){
                                sv->SaveMail(app->m_app_path+"\\"+account+"\\inbox", mail, SMRY_STATUS_NEW, SMRY_COLUMN_FROM);
                        }else{
                                sv->SaveMail(mail_path, mail, SMRY_STATUS_NEW, SMRY_COLUMN_FROM);
                                mail_path.Empty();
                        }
#endif
                        // lastuidを.iniとuidキャッシュファイルに格納
                        if(uid.IsEmpty()){
                                wsprintf(uidl, "uidl %d\r\n", i+1);
                                send(pop_socket.m_sock, uidl, strlen(uidl), 0);
                                pop_socket.SockRead(buf);
                                buf.TrimRight();
                                uid = strtok(buf.GetBuffer(0), " ");
                                uid = strtok(NULL, " ");
                                uid = strtok(NULL, " ");
                                buf.Empty();
                        }

                        if(!delete_mail){
                                WritePrivateProfileString("pop", "last_uid", uid, path);
                        }

                        CStdioFile UidCacheWrite; // uidキャッシュ保存用
                        if(!UidCacheWrite.Open(uid_cache_path.GetBuffer(1), 
                                                                 CFile::modeWrite |
                                                                 CFile::modeCreate |
                                                                 CFile::modeNoTruncate |
                                                                 CFile::typeBinary))
                        {
                                throw "CStdioFile: error";
                        }
                        UidCacheWrite.SeekToEnd();
                        uid += "\r\n";
                        UidCacheWrite.WriteString(uid.GetBuffer(0));
                        UidCacheWrite.Close();
                        uid.ReleaseBuffer();
                        uid.Empty();
                        buf.Empty();
                        mail.RemoveAll();

                        // deleコマンドの送出
                        if(delete_mail == 1){
                                wsprintf(dele, "dele %d\r\n", i+1);
                                send(pop_socket.m_sock, dele , strlen(dele), 0);
                                pop_socket.SockRead(buf);
                                buf.Empty();
                        }
                }

                // プログレスバーのポジションを0に戻す
                av->SetStatusBarProgress(0, 100, account);
                // uidキャッシュ用ファイルの削除
                if(how_many_mail == 0)
                        DeleteFile(uid_cache_path.GetBuffer(0));
                // quitコマンドの送出
                send(pop_socket.m_sock, quit, strlen(quit), 0);
                pop_socket.SockRead(buf);
                buf.Empty();
                // socketを閉じる
                pop_socket.DisConnect();
                if(fetched && g_is_there(app->m_app_path+"\\success.wav"))
                        PlaySound(app->m_app_path+"\\success.wav", NULL, SND_FILENAME);
        }
        catch(char* errorstr){
                // quitコマンドの送出
                send(pop_socket.m_sock, "quit\r\n", strlen("quit\r\n"), 0);
                // socketを閉じる
                pop_socket.DisConnect();
                // プログレスバーのポジションを0に戻す
                mf->MessageBox(errorstr, account+" - pop error");
                av->SetStatusBarProgress(0, 100, account);
                av->SetStatusBarText("", account);
                mf->UpdateButton(TRUE, account);
                av->SetFetchStatus(account.GetBuffer(0), FALSE);
                return FALSE;
        }

        // メインフレームの受信ボタンを有効にする
        mf->UpdateButton(TRUE, account);

        av->SetFetchStatus(account.GetBuffer(0), FALSE);
        av->SetStatusBarText(buf.GetBuffer(0), account);
        av->SetStatusBarProgress(0, 100, account);
        buf.Empty();

        user.ReleaseBuffer();
        pass.ReleaseBuffer();
        addr.ReleaseBuffer();
        buf.ReleaseBuffer();

        return TRUE;
}

/* [<][>][^][v][top][bottom][index][help] */