root/FetchMail.cpp
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- check_uid_cache
- 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;
}