TNB Library
TnbPop3Client.h
[詳解]
1#pragma once
15#include "TnbSocket.h"
16#include "TnbMailContent.h"
17#include "TnbMd5Computer.h"
18#include "TnbQueueingReporter.h"
19#include "TnbListQueue.h"
20
21
22
23//TNB Library
24namespace TNB
25{
26
27
28
42class CAbstractMailClient
43{
44public:
45
51 struct IListener
52 {
54 virtual ~IListener(void) {}
59 virtual void OnMailResponse(LPCSTR lpszRes) = 0;
64 virtual void OnMailCommand(LPCSTR lpszCmd) = 0;
65 };
66
67
68 //---------------------------------
69
70
72 CAbstractMailClient(void) : m_socket(false), m_piRep(&m_socket), m_pListener(NULL), m_responseTimeout(5000)
73 {
74 m_queue.SetEnvironment(m_piRep, &m_inner);
75 }
76
78 virtual ~CAbstractMailClient(void)
79 {
80 Disconnect();
81 }
82
87 LPCSTR GetErrorText(void) const
88 {
89 return m_ascErrorText;
90 }
91
96 void SetListener(IListener* pListener = NULL)
97 {
98 m_pListener = pListener;
99 }
100
108 void SetServerName(LPCTSTR lpszName, WORD wPort)
109 {
110 Disconnect();
111 m_socket.SetServer(CSocketAddress(wPort, lpszName));
112 SetReportInterface(&m_socket);
113 }
114
122 void SetReportInterface(IReport* pReport)
123 {
124 Disconnect();
125 m_piRep = (pReport != NULL) ? pReport : m_piRep;
126 m_inner.Empty();
127 m_queue.SetEnvironment(m_piRep, &m_inner);
128 }
129
135 void SetResponseTimeout(DWORD tm)
136 {
137 m_responseTimeout = tm;
138 }
139
147 bool Disconnect(void)
148 {
149 bool r = true;
150 if ( m_piRep->IsConnect() )
151 {
152 CAscii a;
153 r = QuitCommand(a);
154 loop ( i, 10 )
155 {
156 if ( ! m_piRep->IsConnect() )
157 {
158 break;
159 }
160 ::Sleep(100);
161 }
162 m_piRep->Stop();
163 m_inner.Empty();
164 }
165 return r;
166 }
167
174 bool Connect(void)
175 {
176 Disconnect();
177 m_ascErrorText.Empty();
178 if ( ! m_piRep->Start() )
179 {
180 SetErrorText("Server Not Found.");
181 return false;
182 }
183 if ( ! OnConnect() )
184 {
185 m_piRep->Stop();
186 return false;
187 }
188 return true;
189 }
190
191protected:
192
199 virtual bool IsValidResponse(const CAscii& a) const = 0;
200
208 virtual bool QuitCommand(CAscii& _res) = 0;
209
216 virtual bool OnConnect(void) = 0;
217
224 bool Read(CAscii& _res)
225 {
226 bool r = m_inner.Read(_res, m_responseTimeout);
227 if ( r )
228 {
229 TRACE1( "Mail-Command Response = [%s]\n", _res );
230 if ( m_pListener != NULL )
231 {
232 m_pListener->OnMailResponse(_res);
233 }
234 }
235 else
236 {
237 SetErrorText("Timeout.");
238 }
239 return r;
240 }
241
248 bool Write(LPCSTR lpszCmd)
249 {
250 if ( m_piRep != NULL )
251 {
252 CAscii a = lpszCmd;
253 a += "\r\n";
254 LPCSTR lpsz = a;
255 return m_piRep->Send(a.GetLength(), lpsz);
256 }
257 return false;
258 }
259
267 bool Command(CAscii& _res, LPCSTR lpszCmd)
268 {
269 Write(lpszCmd);
270 if ( m_pListener != NULL )
271 {
272 m_pListener->OnMailCommand(lpszCmd);
273 }
274 if ( ! Read(_res) )
275 {
276 return false;
277 }
278 if ( ! IsValidResponse(_res) )
279 {
280 SetErrorText(_res); //受信文字列をエラー文字列とする
281 _res.Empty();
282 return false;
283 }
284 return true;
285 }
286
294 bool Command(LPCSTR lpszCmd)
295 {
296 CAscii s;
297 return Command(s, lpszCmd);
298 }
299
306 bool ReadLines(CAsciiVector& _lines)
307 {
308 _lines.RemoveAll();
309 CAscii res;
310 while ( true )
311 {
312 bool r = m_inner.Read(res, m_responseTimeout);
313 if ( ! r )
314 {
315 SetErrorText("Timeout.");
316 return false;
317 }
318 if ( res == "." )
319 {
320 break;
321 }
322 _lines.Add(res);
323 }
324 return true;
325 }
326
332 bool IsConnect(void)
333 {
334 if ( ! m_piRep->IsConnect() )
335 {
336 m_ascErrorText = "Server Not Connected.";
337 return false;
338 }
339 return true;
340 }
341
346 void SetErrorText(LPCSTR lpsz)
347 {
348 m_ascErrorText = lpsz;
349 }
350
351private:
352 // インナークラス
353 class CInner : public CQueueingReporter::IListener
354 {
355 public:
357 void Empty(void)
358 {
359 m_recvLines.RemoveAll();
360 }
361
367 bool Read(CAscii& _res, DWORD time)
368 {
369 CTickCount tc;
370 do
371 {
372 if ( ! m_recvLines.IsEmpty() )
373 {
374 _res = m_recvLines.Take();
375 return true;
376 }
377 ::Sleep(100);
378 }
379 while ( ! tc.IsPassed(time) );
380 return false;
381 }
382 protected:
383 // イベント
384 virtual bool OnReportEvent(const CReportEvent& ev)
385 {
386 ASSERT( ! ev.HasData() );
387 return true;
388 }
389 // jyy真
390 virtual size_t OnReportData(bool boIsLast, const IConstCollectionT<BYTE>& c)
391 {
392 if ( c.IsEmpty() )
393 {
394 return 0;
395 }
396 BYTE b = c.At(0);
397 if ( b == 0 || b == 0x0A )
398 {
399 return 1;
400 }
401 loop ( i, c.GetSize() )
402 {
403 b = c.At(i);
404 if ( b == 0 || b == 0x0D )
405 {
406 CAscii a;
407 LPSTR lpsz = a.GetBuffer(i + 1);
408 loop ( j, i )
409 {
410 lpsz[j] = c.At(j);
411 }
412 lpsz[i] = 0;
413 a.ReleaseBuffer();
414 m_recvLines.Add(a);
415 return i + 1;
416 }
417 }
418 return 0;
419 }
420 private:
421 CListQueueT<CAscii> m_recvLines;
422 };
423
424 CSocketClient m_socket;
425 IReport* m_piRep;
426 CQueueingReporter m_queue;
427 CInner m_inner;
428 CAscii m_ascErrorText;
429 IListener* m_pListener;
430 DWORD m_responseTimeout;
431};
432
433
434
472class CPop3Client : public CAbstractMailClient
473{
474 DEFSUPER(CAbstractMailClient);
475public:
476
478 enum EKind
479 {
480 POP3,
481 APOP
482 };
483
485 struct TParam
486 {
487 int number;
488 int size;
489 };
490
491 typedef CVectorT<TParam> CParamVector;
492
493
494 //----------------------------------
495
496
501 CPop3Client(EKind kind = POP3) : _super(), m_kind(kind)
502 {
503 }
504
506 virtual ~CPop3Client(void)
507 {
508 Disconnect();
509 }
510
517 CAscii GetOpeningMessage(void) const
518 {
519 return m_ascOpeningMessage;
520 }
521
529 void SetServerName(LPCTSTR lpszName, WORD port = 110)
530 {
531 _super::SetServerName(lpszName, port);
532 }
533
539 void SetUser(LPCTSTR lpszUser, LPCTSTR lpszPassword)
540 {
541 m_ascUser = lpszUser;
542 m_ascPassword = lpszPassword;
543 }
544
549 void SetKind(EKind kind)
550 {
551 m_kind = kind;
552 }
553
560 bool RequestNoop(void)
561 {
562 if ( IsConnect() )
563 {
564 return Command("NOOP");
565 }
566 return false;
567 }
568
577 bool RequestDelete(int number)
578 {
579 if ( IsConnect() )
580 {
581 return Command(CAscii::Fmt("DELE %d", number));
582 }
583 return false;
584 }
585
592 bool CancelDelete(void)
593 {
594 if ( Connect() )
595 {
596 return Command("RSET");
597 }
598 return false;
599 }
600
608 bool GetMailCount(TParam& _param)
609 {
610 _param.number = 0;
611 _param.size = 0;
612 if ( IsConnect() )
613 {
614 CAscii res;
615 if ( Command(res, "STAT") )
616 {
617 m_GetTwoInt(_param, res, 4);
618 return true;
619 }
620 }
621 return false;
622 }
623
631 bool GetMailList(CParamVector& _list)
632 {
633 _list.RemoveAll();
634 if ( IsConnect() )
635 {
636 CAscii res;
637 if ( Command(res, "LIST") )
638 {
639 size_t c = res.ToInt(4);
640 CAsciiVector va;
641 if ( ReadLines(va) )
642 {
643 if ( c == va.GetSize() )
644 {
645 loop ( i, va.GetSize() )
646 {
647 TParam t;
648 m_GetTwoInt(t, va[i], 0);
649 _list.Add(t);
650 }
651 return true;
652 }
653 SetErrorText("invalid list"); // 要求の 数取得できなかった。
654 }
655 }
656 }
657 return false;
658 }
659
668 bool GetMailContent(CAsciiVector& _mail, int number, int line = 0)
669 {
670 _mail.RemoveAll();
671 if ( IsConnect() )
672 {
673 CAscii cmd;
674 if ( line <= 0 )
675 {
676 cmd.Format("RETR %d", number);
677 }
678 else
679 {
680 cmd.Format("TOP %d %d", number, line);
681 }
682 if ( Command(cmd) )
683 {
684 return ReadLines(_mail);
685 }
686 }
687 return false;
688 }
689
698 bool GetUidl(CAscii& _uidl, int number)
699 {
700 _uidl.Empty();
701 if ( IsConnect() )
702 {
703 CAscii cmd;
704 cmd.Format("UIDL %d", number);
705 CAscii res;
706 if ( Command(res, cmd) )
707 {
708 if ( res.ToInt(4) == number )
709 {
710 INT_PTR p = res.ReverseFind(' ');
711 _uidl = res.Mid(p + 1);
712 return true;
713 }
714 SetErrorText("invalid uidl"); // 要求の uidl と異なる。
715 }
716 }
717 return false;
718 }
719
720protected:
721
728 virtual bool IsValidResponse(const CAscii& a) const
729 {
730 return a.Find("+OK") == 0;
731 }
732
740 virtual bool QuitCommand(CAscii& _res)
741 {
742 return Command(_res, "QUIT");
743 }
744
751 virtual bool OnConnect(void)
752 {
753 CAscii s;
754 bool r = Read(s);
755 if ( r && s.GetAt(0) == '+' )
756 {
757 m_ascOpeningMessage = s.Mid(4);
758 if ( m_kind == POP3 )
759 {
760 //== POP3
761 if ( Command("USER " + m_ascUser) )
762 {
763 if ( Command("PASS " + m_ascPassword) )
764 {
765 return true;
766 }
767 }
768 }
769 else
770 {
771 //== APOP
772 INT_PTR is = s.Find('<');
773 INT_PTR ie = s.Find('>', is);
774 if ( is >= 0 && ie > is )
775 {
776 CAscii a = s.Mid(is, ie - is + 1);
777 CAscii h = CMd5Computer::ComputeHash(a + m_ascPassword).GetString();
778 if ( Command(CAscii::Fmt("APOP %s %s", m_ascUser, h)) )
779 {
780 return true;
781 }
782 }
783 }
784 }
785 return false;
786 }
787
788private:
789
790 CAscii m_ascOpeningMessage;
791 CAscii m_ascUser;
792 CAscii m_ascPassword;
793 EKind m_kind;
794
802 void m_GetTwoInt(TParam& _param, const CAscii& asc, int iOffset)
803 {
804 INT_PTR p = asc.ReverseFind(' ');
805 _param.number = asc.ToInt(iOffset);
806 _param.size = asc.ToInt(p + 1);
807 }
808
809};
810
811
812
813}; // TNB
814
815
816
817#if 0
818USER・PASS コマンドによってユーザ認証を行う。
819LIST コマンドでメール一覧を受け取る。
820それから各メールについて、
821RETR コマンドでメールの内容を受け取り、
822DELE コマンドでメールをサーバから削除する。
823QUIT コマンドで通信終了
824
825
826サーバ <wait for connection on TCP port 110>
827クライアント <open connection>
828サーバ +OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>
829--
830クライアント USER メールアカウント名<CRLF>
831サーバ +OK <CRLF>
832クライアント PASS パスワード<CRLF>
833サーバ +OK <CRLF>
834--
835クライアント APOP mrose c4c9334bac560ecc979e58001b3e22fb
836サーバ +OK mrose's maildrop has 2 messages (320 octets)
837--
838
839クライアント STAT
840サーバ +OK 2 320
841クライアント LIST
842サーバ +OK 2 messages (320 octets)
843サーバ 1 120
844サーバ 2 200
845サーバ .
846
847クライアント RETR 1
848サーバ +OK 120 octets
849サーバ <the POP3 server sends message 1>
850サーバ .
851
852クライアント DELE 1
853サーバ +OK message 1 deleted
854クライアント RETR 2
855サーバ +OK 200 octets
856サーバ <the POP3 server sends message 2>
857サーバ .
858クライアント DELE 2
859サーバ +OK message 2 deleted
860
861クライアント QUIT
862サーバ +OK dewey POP3 server signing off (maildrop empty)
863クライアント <close connection>
864サーバ <wait for next connection>
865#endif
866
#define loop(VAR, CNT)
loop構文.
Definition: TnbDef.h:343
キュー型情報管理関係のヘッダ
メールコンテンツ関係のヘッダ
MD5(message-digest algorithm)関係のヘッダ
キューイングレポート関係のヘッダ
ソケットのアクセス関係のヘッダ
リスト式キュー型情報管理テンプレート
Definition: TnbListQueue.h:52
static TResult ComputeHash(size_t size, LPCVOID P)
[計算] ハッシュ計算
キューイングレポートクラス
通信受信イベント管理クラス
Definition: TnbReport.h:70
bool HasData(void) const
[確認] Dataを持っているか
Definition: TnbReport.h:120
Socket共通アドレス管理クラス
Definition: TnbSocket.h:63
Socketクライアントクラス
Definition: TnbSocket.h:841
INT_PTR ReverseFind(TYP t) const
[確認] 検索(後ろから)
Definition: TnbStr.h:575
void ReleaseBuffer(void)
[操作] 割り当てたバッファを開放.
Definition: TnbStr.h:954
size_t GetLength(void) const
[取得] 文字列長
Definition: TnbStr.h:518
static CStrT Fmt(const char *lpszFormat,...)
[作成] 書式付き文字列作成
Definition: TnbStr.h:1206
INT_PTR Find(TYP t, INDEX iFromIndex=0) const
[確認] 検索.
Definition: TnbStr.h:540
TYP GetAt(INDEX index) const
[取得] 一文字取得
Definition: TnbStr.h:504
void Empty(void)
[削除] 空化
Definition: TnbStr.h:197
void Format(const TYP *lpszFormat,...)
[代入] 書式付き文字列代入.
Definition: TnbStr.h:359
CStrT Mid(INDEX iOffset, size_t iSize=INVALID_SIZE) const
[作成] 範囲取得.
Definition: TnbStr.h:766
int ToInt(INDEX iOffset=0, int iBase=10) const
[取得] 数値(int)へ変換
Definition: TnbStr.h:842
TYP * GetBuffer(size_t iLength=0)
[操作] 書き込みバッファ要求.
Definition: TnbStr.h:914
経過時間管理クラス
Definition: TnbTickCount.h:57
bool IsPassed(DWORD dwTime) const
[確認] 経過確認.
Definition: TnbTickCount.h:114
virtual size_t GetSize(void) const
[取得] サイズ取得
Definition: TnbVector.h:368
virtual bool RemoveAll(void)
[削除] 空化
Definition: TnbVector.h:565
virtual INDEX Add(const TYP &t)
[追加] 要素一つ追加.
Definition: TnbVector.h:383
TNB Library
Definition: TnbDoxyTitle.txt:2
CStr GetString(void)
ハッシュ情報(文字列)取得
キューイングレポートのリスナーインターフェース
bool IsEmpty(void) const
[確認] 要素の有無確認.
virtual const TYP & At(INDEX index) const =0
[取得] 要素の参照取得.
virtual size_t GetSize(void) const =0
[取得] 要素数取得.
通信アクセスインターフェース
Definition: TnbReport.h:227