Pop3.cpp
上传用户:liuzhunlcd
上传日期:2007-01-12
资源大小:54k
文件大小:30k
源码类别:

Email客户端

开发平台:

Visual C++

  1. /*
  2. Module : POP3.CPP
  3. Purpose: Implementation for a MFC class encapsulation of the POP3 protocol
  4. Created: PJN / 04-05-1998
  5. History: PJN / 27-06-1998
  6. 1) Fixed a potential buffer overflow problem in Delete
  7. and Retrieve functions when a large message number was
  8. specified.
  9. 2) Improve the ReadResponse code by a) passing the 
  10. readability check onto the scoket class and b) Sleeping
  11. for 100 ms prior to looping around waiting for new 
  12. response data
  13. 3) Classes are now fully Unicode compliant. Unicode
  14. build configurations are also now included.
  15. 4) Now supports the TOP POP3 command which can be
  16. issued using the GetHeader function.
  17. PJN / 04-01-1999 
  18. 1) Properly UNICODE enabled the code
  19. PJN / 22-02-1999 
  20. 1) Improved the reading of responses back from the server by implementing
  21. a growable receive buffer
  22. 2) timeout is now 60 seconds when building for debug
  23. 3) Code now yields its time slice while waiting for a timeout
  24. 4) Made most functions virtual to help end user usage
  25. PJN / 25-03-1999
  26. 1) Fixed memory leak in the CPop3Connection::ReadReturnResponse function.
  27. 2) Now sleeps for 250 ms instead of yielding the time slice. This helps 
  28. reduce CPU usage when waiting for data to arrive in the socket
  29. PJN / 15-06-1999 
  30. 1) Added functions to return the message body, header or a particular
  31. header field of a message
  32. 2) Tidied up some of the TRACE messages which the code generates
  33. PJN / 16-06-1999 
  34. 1) Fixed a bug in the GetHeaderItem function which was causing a header
  35. item which appeared at the begining of the header fields to fail to be 
  36. parsed incorrectly.
  37. PJN / 27-06-1999 
  38. 1) Fixed a bug in the GetHeaderItem function when a header spanned 
  39. multiple lines as is allowed by RFC 822
  40. PJN / 29-06-1999 
  41. 1) Another improvement to GetHeaderItem. When will it end <g>. Originally 
  42. this was reported as a bug but upon further investigation it turns out that
  43. the message which was causing the problems had embedded tabs in the header. 
  44. This is discouraged by the RFC which refers to mail headers (RFC 822). 
  45. The code has been enhanced to handle this case. Thanks to Chris Bishop 
  46. for spotting this.
  47. 2) Fix for a bug in GetHeaderItem where I accidently was using "=" instead of
  48. "==". Thanks to Angelini Fabio for spotting this.
  49. PJN / 05-07-1999 
  50. 1) Addition of the following functions:
  51. i)   CPop3Message::GetReplyTo
  52. ii)  CPop3Message::GetRawBody   
  53. iii) CPop3Message::GetSubject 
  54. iv)  CPop3Message::GetFrom
  55. v)   CPop3Message::GetDate
  56. 2) GetHeaderItem function now uses case insensitive searching
  57. 3) GetHeaderItem now allows you to search for the "n'th" header of a specified type
  58. PJN / 24-08-1999 
  59. 1) Fixed a bug whereby the function GetHeader was sometimes failing when it
  60. was called when the message was retrieved using the "TOP" command.
  61. PJN / 27-03-2000 
  62. 1) Fixed a problem where GetBody and GetRawBody will fail if you call it for a 
  63. CPop3Message object that doesn't have a message in it (i.e m_pszMessage == NULL). 
  64. This was previously causing a goof old access violation in GetRawBody.
  65. PJN / 20-09-2000 
  66. 1) Fixed a bug in CPop3Connection::UIDL when message id's were returned
  67. with embedded dots in them. Thanks to Konstantin Vasserman for finding and resolving
  68. this problem.
  69. PJN / 25-03-2001 
  70. 1) Updated copyright information
  71. PJN / 27-07-2001 
  72. 1) Added two methods namely: GetTo() and GetCC() to the CPop3Message class
  73. PJN / 11-08-2001 
  74. 1) Fixed a bug in CPop3Connection::ReadResponse where determining the terminator
  75. was failing when an embedded NULL was in the message.
  76. PJN / 27-09-2001 
  77. 1) Fixed a bug in CPop3Connection::ReadResponse when handling disconnection errors
  78. 2) Improved the error handling in Delete, GetMessageSize & GetMessageID methods
  79. PJN / 29-09-2001 
  80. 1) Further improved the error handling in CPop3Connection::ReadResponse
  81. Copyright (c) 1998 - 2001 by PJ Naughter.  (Web: www.naughter.com, Email: pjna@naughter.com)
  82. All rights reserved.
  83. Copyright / Usage Details:
  84. You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) 
  85. when your product is released in binary form. You are allowed to modify the source code in any way you want 
  86. except you cannot modify the copyright details at the top of each module. If you want to distribute source 
  87. code with your application, then you are only allowed to distribute versions released by the author. This is 
  88. to maintain a single distribution point for the source code. 
  89. */
  90. // AV / 15-01-2002
  91. // CPop3Message::GetBoundary() added
  92. //  CPop3Message::GetContentType() added
  93. //  CPop3Connection::~CPop3Connection() changed
  94. //////////////// Includes ////////////////////////////////////////////
  95. #include "stdafx.h"
  96. #include "pop3.h"
  97. //////////////// Macros //////////////////////////////////////////////
  98. #ifdef _DEBUG
  99. #define new DEBUG_NEW
  100. #undef THIS_FILE
  101. static char THIS_FILE[] = __FILE__;
  102. #endif
  103. //////////////// Implementation //////////////////////////////////////
  104. CPop3Message::CPop3Message()
  105. {
  106. m_pszMessage = NULL;
  107. }
  108. CPop3Message::~CPop3Message()
  109. {
  110. if (m_pszMessage)
  111. {
  112. delete [] m_pszMessage;
  113. m_pszMessage = NULL;
  114. }
  115. }
  116. CString CPop3Message::GetHeaderItem(const CString& sName, int nItem) const
  117. {
  118. //Value which will be returned by this function
  119. CString sField;
  120. //Get the message header (add an extra "rn" at the
  121. //begining to aid in the parsing)  
  122. CString sHeader(_T("rn"));
  123. sHeader += GetHeader();
  124. CString sUpCaseHeader(sHeader);
  125. sUpCaseHeader.MakeUpper();
  126. CString sUpCaseName(sName);
  127. sUpCaseName.MakeUpper();
  128. //Find the specified line in the header
  129. CString sFind(CString(_T("rn")) + sUpCaseName + _T(":"));
  130. int nFindLength = sFind.GetLength();
  131. int nFindStart = sUpCaseHeader.Find(sFind);
  132. int nFind = nFindStart;
  133. for (int i=0; i<nItem; i++) 
  134. {
  135. //Get ready for the next loop around
  136. sUpCaseHeader = sUpCaseHeader.Right(sUpCaseHeader.GetLength() - nFind - nFindLength);
  137. nFind = sUpCaseHeader.Find(sFind);
  138. if (nFind == -1)
  139. return _T(""); //Not found
  140. else
  141. nFindStart += (nFind + nFindLength);
  142. }
  143. if (nFindStart != -1)
  144. nFindStart += (3 + sName.GetLength());
  145. if (nFindStart != -1)
  146. {
  147. BOOL bFoundEnd = FALSE;
  148. int i = nFindStart;
  149. int nLength = sHeader.GetLength();
  150. do
  151. {
  152. //Examine the current 3 characters
  153. TCHAR c1 = _T('');
  154. if (i < nLength)
  155. c1 = sHeader[i];
  156. TCHAR c2 = _T('');
  157. if (i < (nLength-1))
  158. c2 = sHeader[i+1];
  159. TCHAR c3 = _T('');
  160. if (i < (nLength-2))
  161. c3 = sHeader[i+2];
  162. //Have we found the terminator
  163. if ((c1 == _T('')) ||
  164. ((c1 == _T('r')) && (c2 == _T('n')) && (c3 != _T(' ')) && c3 != _T('t')))
  165. {
  166. bFoundEnd = TRUE;
  167. }
  168. else
  169. {
  170. //Move onto the next character
  171. ++i;
  172. }
  173. }
  174. while (!bFoundEnd);
  175. sField = sHeader.Mid(nFindStart, i - nFindStart);
  176. //Remove any embedded "rn" sequences from the field
  177. int nEOL = sField.Find(_T("rn"));
  178. while (nEOL != -1)
  179. {
  180. sField = sField.Left(nEOL) + sField.Right(sField.GetLength() - nEOL - 2);
  181. nEOL = sField.Find(_T("rn"));
  182. }
  183. //Replace any embedded "t" sequences with spaces
  184. int nTab = sField.Find(_T('t'));
  185. while (nTab != -1)
  186. {
  187. sField = sField.Left(nTab) + _T(' ') + sField.Right(sField.GetLength() - nTab - 1);
  188. nTab = sField.Find(_T('t'));
  189. }
  190. //Remove any leading or trailing white space from the Field Body
  191. sField.TrimLeft();
  192. sField.TrimRight();
  193. }
  194. return sField;
  195. }
  196. CString CPop3Message::GetHeader() const
  197. {
  198. //Value which will be returned by this function
  199. CString sHeader;
  200. //Find the divider between the header and body
  201. CString sMessage(m_pszMessage);
  202. int nFind = sMessage.Find(_T("rnrn"));
  203. if (nFind != -1)
  204. sHeader = sMessage.Left(nFind);
  205. else
  206. {
  207. //No divider, then assume all the text is the header
  208. sHeader = sMessage;
  209. }
  210. return sHeader;
  211. }
  212. LPCSTR CPop3Message::GetRawBody() const
  213. {
  214. if (m_pszMessage == NULL)
  215. return NULL;
  216. char* pszStartBody = strstr(m_pszMessage, "rnrn");
  217. if (pszStartBody == NULL) 
  218. return NULL;
  219. else 
  220. return pszStartBody + 4;
  221. }
  222. CString CPop3Message::GetBody() const
  223. {
  224. CString sBody;
  225. LPCSTR pszBody = GetRawBody();
  226. if (pszBody)
  227. sBody = pszBody;
  228. return sBody;
  229. }
  230. CString CPop3Message::GetReplyTo() const
  231. {
  232. CString sRet = GetHeaderItem("Reply-To");
  233. if (sRet.IsEmpty())
  234. {
  235. sRet = GetFrom();
  236. if (sRet.IsEmpty())
  237. {
  238. sRet = GetHeaderItem(_T("Sender"));
  239. if (sRet.IsEmpty())
  240. sRet = GetHeaderItem(_T("Return-Path"));
  241. }
  242. }
  243. return sRet;
  244. }
  245. CString CPop3Message::GetContentType() const
  246. {
  247. CString sRet = GetHeaderItem("Content-Type");
  248. if(sRet.IsEmpty())
  249. return sRet;
  250. INT iPos = sRet.Find(";");
  251. if(iPos == -1)
  252. return sRet;
  253. sRet = sRet.Left(iPos);
  254. return sRet;
  255. }
  256. CString CPop3Message::GetBoundary() const
  257. {
  258. CString sRet = GetHeaderItem("Content-Type");
  259. if(sRet.IsEmpty())
  260. return sRet;
  261. INT iPos = sRet.Find(""");
  262. if(iPos == -1)
  263. {
  264. sRet.Empty();
  265. return sRet;
  266. }
  267. if(iPos == sRet.GetLength())
  268. {
  269. sRet.Empty();
  270. return sRet;
  271. }
  272. sRet = sRet.Right(sRet.GetLength() - iPos - 1);
  273. iPos = sRet.Find(""");
  274. if(iPos == -1)
  275. {
  276. sRet.Empty();
  277. return sRet;
  278. }
  279. sRet = sRet.Left(iPos);
  280. return sRet;
  281. }
  282. CPop3Socket::CPop3Socket()
  283. {
  284. m_hSocket = INVALID_SOCKET; //default to an invalid scoket descriptor
  285. }
  286. CPop3Socket::~CPop3Socket()
  287. {
  288. Close();
  289. }
  290. BOOL CPop3Socket::Create()
  291. {
  292. m_hSocket = socket(AF_INET, SOCK_STREAM, 0);
  293. return (m_hSocket != INVALID_SOCKET);
  294. }
  295. BOOL CPop3Socket::Connect(LPCTSTR pszHostAddress, int nPort)
  296. {
  297. //For correct operation of the T2A macro, see MFC Tech Note 59
  298. USES_CONVERSION;
  299. //must have been created first
  300. ASSERT(m_hSocket != INVALID_SOCKET);
  301. //Determine if the address is in dotted notation  
  302. SOCKADDR_IN sockAddr;
  303. ZeroMemory(&sockAddr, sizeof(sockAddr));
  304. sockAddr.sin_family = AF_INET;
  305. sockAddr.sin_port = htons((u_short)nPort);
  306. char* pszAsciiHostAddress = T2A((LPTSTR) pszHostAddress);
  307. sockAddr.sin_addr.s_addr = inet_addr(pszAsciiHostAddress);
  308. //If the address is not dotted notation, then do a DNS 
  309. //lookup of it.
  310. if (sockAddr.sin_addr.s_addr == INADDR_NONE)
  311. {
  312. LPHOSTENT lphost;
  313. lphost = gethostbyname(pszAsciiHostAddress);
  314. if (lphost != NULL)
  315. sockAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr;
  316. else
  317. {
  318. WSASetLastError(WSAEINVAL); 
  319. return FALSE;
  320. }
  321. }
  322. //Call the protected version which takes an address 
  323. //in the form of a standard C style struct.
  324. return Connect((SOCKADDR*)&sockAddr, sizeof(sockAddr));
  325. }
  326. BOOL CPop3Socket::Connect(const SOCKADDR* lpSockAddr, int nSockAddrLen)
  327. {
  328. return (connect(m_hSocket, lpSockAddr, nSockAddrLen) != SOCKET_ERROR);
  329. }
  330. BOOL CPop3Socket::Send(LPCSTR pszBuf, int nBuf)
  331. {
  332. //must have been created first
  333. ASSERT(m_hSocket != INVALID_SOCKET);
  334. return (send(m_hSocket, pszBuf, nBuf, 0) != SOCKET_ERROR);
  335. }
  336. int CPop3Socket::Receive(LPSTR pszBuf, int nBuf)
  337. {
  338. //must have been created first
  339. ASSERT(m_hSocket != INVALID_SOCKET);
  340. return recv(m_hSocket, pszBuf, nBuf, 0); 
  341. }
  342. void CPop3Socket::Close()
  343. {
  344. if (m_hSocket != INVALID_SOCKET)
  345. {
  346. VERIFY(SOCKET_ERROR != closesocket(m_hSocket));
  347. m_hSocket = INVALID_SOCKET;
  348. }
  349. }
  350. BOOL CPop3Socket::IsReadible(BOOL& bReadible)
  351. {
  352. timeval timeout = {0, 0};
  353. fd_set fds;
  354. FD_ZERO(&fds);
  355. FD_SET(m_hSocket, &fds);
  356. int nStatus = select(0, &fds, NULL, NULL, &timeout);
  357. if (nStatus == SOCKET_ERROR)
  358. {
  359. return FALSE;
  360. }
  361. else
  362. {
  363. bReadible = !(nStatus == 0);
  364. return TRUE;
  365. }
  366. }
  367. CPop3Connection::CPop3Connection()
  368. {
  369. m_nNumberOfMails = 0;
  370. m_bListRetrieved = FALSE;
  371. m_bStatRetrieved = FALSE;
  372. m_bUIDLRetrieved = FALSE;
  373. m_msgSizes.RemoveAll();
  374. m_bConnected = FALSE;
  375. #ifdef _DEBUG
  376. m_dwTimeout = 60000; //default timeout of 60 seconds when debugging
  377. #else
  378. m_dwTimeout = 2000;  //default timeout of 2 seconds for normal release code
  379. #endif
  380. }
  381. CPop3Connection::~CPop3Connection()
  382. {
  383. //if (m_bConnected)
  384. // Disconnect();
  385. //free up our socket
  386. m_Pop.Close();
  387. }
  388. BOOL CPop3Connection::Connect(LPCTSTR pszHostName, LPCTSTR pszUser, LPCTSTR pszPassword, int nPort)
  389. {
  390. //For correct operation of the T2A macro, see MFC Tech Note 59
  391. USES_CONVERSION;
  392. //Create the socket
  393. if (!m_Pop.Create())
  394. {
  395. TRACE(_T("Failed to create client socket, GetLastError:%dn"), GetLastError());
  396. return FALSE;
  397. }
  398. //Connect to the POP3 Host
  399. if (!m_Pop.Connect(pszHostName, nPort))
  400. {
  401. TRACE(_T("Could not connect to the POP3 mailbox, GetLastError:%dn"), GetLastError());
  402. return FALSE;
  403. }
  404. else
  405. {
  406. //We're now connected !!
  407. m_bConnected = TRUE;
  408. //check the response
  409. if (!ReadCommandResponse())
  410. {
  411. TRACE(_T("Failed while connected to read a command response from the POP3 servern"));
  412. Disconnect();
  413. return FALSE;
  414. }
  415. //Send the POP3 username and check the response
  416. char sBuf[128];
  417. char* pszAsciiUser = T2A((LPTSTR) pszUser);
  418. ASSERT(strlen(pszAsciiUser) < 100); 
  419. sprintf(sBuf, "USER %srn", pszAsciiUser);
  420. int nCmdLength = strlen(sBuf);
  421. if (!m_Pop.Send(sBuf, nCmdLength))
  422. {
  423. TRACE(_T("Failed to send the USER command to the POP3 servern"));
  424. Disconnect();
  425. return FALSE;
  426. }
  427. if (!ReadCommandResponse())
  428. {
  429. TRACE(_T("Failed while connected to read a USER command response from the POP3 servern"));
  430. Disconnect();
  431. return FALSE;
  432. //Send the POP3 password and check the response
  433. char* pszAsciiPassword = T2A((LPTSTR) pszPassword);
  434. ASSERT(strlen(pszAsciiPassword) < 100);
  435. sprintf(sBuf, "PASS %srn", pszAsciiPassword);
  436. nCmdLength = strlen(sBuf);
  437. if (!m_Pop.Send(sBuf, nCmdLength))
  438. {
  439. TRACE(_T("Failed to send the PASS command to the POP3 servern"));
  440. Disconnect();
  441. return FALSE;
  442. }
  443. if (!ReadCommandResponse())
  444. {
  445. TRACE(_T("Failed while connected to read a PASS command response from the POP3 servern"));
  446. Disconnect();
  447. return FALSE;
  448. }
  449. return TRUE;
  450. }
  451. }
  452. BOOL CPop3Connection::Disconnect()
  453. {    
  454. BOOL bSuccess = FALSE;
  455. //disconnect from the POP3 server if connected 
  456. if (m_bConnected)
  457. {
  458. char sBuf[10];
  459. strcpy(sBuf, "QUITrn");
  460. int nCmdLength = strlen(sBuf);
  461. if (!m_Pop.Send(sBuf, nCmdLength))
  462. TRACE(_T("Failed to send the QUIT command to the POP3 servern"));
  463. //Check the reponse
  464. bSuccess = ReadCommandResponse();
  465. //Reset all the state variables
  466. m_bConnected = FALSE;
  467. m_bListRetrieved = FALSE;
  468. m_bStatRetrieved = FALSE;
  469. m_bUIDLRetrieved = FALSE;
  470. }
  471. else
  472. TRACE(_T("CPop3Connection, Already disconnectedn"));
  473. //free up our socket
  474. m_Pop.Close();
  475. return bSuccess;
  476. }
  477. BOOL CPop3Connection::Delete(int nMsg)
  478. {
  479. BOOL bSuccess = TRUE;
  480. //Must be connected to perform a delete
  481. ASSERT(m_bConnected);
  482. //if we haven't executed the LIST command then do it now
  483. if (!m_bListRetrieved)
  484. bSuccess = List();
  485. //Handle the error if necessary  
  486. if (!bSuccess)
  487. return FALSE;
  488. //Send the DELE command along with the message ID
  489. char sBuf[20];
  490. sprintf(sBuf, "DELE %drn", nMsg);
  491. int nCmdLength = strlen(sBuf);
  492. if (!m_Pop.Send(sBuf, nCmdLength))
  493. {
  494. TRACE(_T("Failed to send the DELE command to the POP3 servern"));
  495. return FALSE;
  496. }
  497. return ReadCommandResponse();
  498. }
  499. BOOL CPop3Connection::Statistics(int& nNumberOfMails, int& nTotalMailSize)
  500. {
  501. //Must be connected to perform a "STAT"
  502. ASSERT(m_bConnected);
  503. //Send the STAT command
  504. char sBuf[10];
  505. strcpy(sBuf, "STATrn");
  506. int nCmdLength = strlen(sBuf);
  507. if (!m_Pop.Send(sBuf, nCmdLength))
  508. {
  509. TRACE(_T("Failed to send the STAT command to the POP3 servern"));
  510. return FALSE;
  511. }
  512. return ReadStatResponse(nNumberOfMails, nTotalMailSize);
  513. }
  514. BOOL CPop3Connection::GetMessageSize(int nMsg, DWORD& dwSize)
  515. {
  516. BOOL bSuccess = TRUE;
  517. //if we haven't executed the LIST command then do it now
  518. if (!m_bListRetrieved)
  519. bSuccess = List();
  520. //Handle the error if necessary  
  521. if (!bSuccess)
  522. return FALSE;
  523. //nMsg must be in the correct range
  524. ASSERT((nMsg > 0) && (nMsg <= m_msgSizes.GetSize()));
  525. //retrieve the size from the message size array
  526. dwSize = m_msgSizes.GetAt(nMsg - 1);
  527. return bSuccess;
  528. }
  529. BOOL CPop3Connection::GetMessageID(int nMsg, CString& sID)
  530. {
  531. BOOL bSuccess = TRUE;
  532. //if we haven't executed the UIDL command then do it now
  533. if (!m_bUIDLRetrieved)
  534. bSuccess = UIDL();
  535. //Handle the error if necessary  
  536. if (!bSuccess)
  537. return FALSE;
  538. //nMsg must be in the correct range
  539. ASSERT((nMsg > 0) && (nMsg <= m_msgIDs.GetSize()));
  540. //retrieve the size from the message size array
  541. sID = m_msgIDs.GetAt(nMsg - 1);
  542. return bSuccess;
  543. }
  544. BOOL CPop3Connection::List()
  545. {
  546. //Must be connected to perform a "LIST"
  547. ASSERT(m_bConnected);
  548. //if we haven't executed the STAT command then do it now
  549. int nNumberOfMails = m_nNumberOfMails;
  550. int nTotalMailSize;
  551. if (!m_bStatRetrieved)
  552. {
  553. if (!Statistics(nNumberOfMails, nTotalMailSize))
  554. return FALSE;
  555. else
  556. m_bStatRetrieved = TRUE;
  557. }
  558. //Send the LIST command
  559. char sBuf[10];
  560. strcpy(sBuf, "LISTrn");
  561. int nCmdLength = strlen(sBuf);
  562. if (!m_Pop.Send(sBuf, nCmdLength))
  563. {
  564. TRACE(_T("Failed to send the LIST command to the POP3 servern"));
  565. return FALSE;
  566. }
  567. //And check the response
  568. m_bListRetrieved = ReadListResponse(nNumberOfMails);
  569. return m_bListRetrieved;
  570. }
  571. BOOL CPop3Connection::UIDL()
  572. {
  573. //Must be connected to perform a "UIDL"
  574. ASSERT(m_bConnected);
  575. //if we haven't executed the STAT command then do it now
  576. int nNumberOfMails = m_nNumberOfMails;
  577. int nTotalMailSize;
  578. if (!m_bStatRetrieved)
  579. {
  580. if (!Statistics(nNumberOfMails, nTotalMailSize))
  581. return FALSE;
  582. else
  583. m_bStatRetrieved = TRUE;
  584. }
  585. //Send the UIDL command
  586. char sBuf[10];
  587. strcpy(sBuf, "UIDLrn");
  588. int nCmdLength = strlen(sBuf);
  589. if (!m_Pop.Send(sBuf, nCmdLength))
  590. {
  591. TRACE(_T("Failed to send the UIDL command to the POP3 servern"));
  592. return FALSE;
  593. }
  594. //And check the response
  595. m_bUIDLRetrieved = ReadUIDLResponse(nNumberOfMails);
  596. return m_bUIDLRetrieved;
  597. }
  598. BOOL CPop3Connection::Reset()
  599. {
  600. //Must be connected to perform a "RSET"
  601. ASSERT(m_bConnected);
  602. //Send the RSET command
  603. char sBuf[10];
  604. strcpy(sBuf, "RSETrn");
  605. int nCmdLength = strlen(sBuf);
  606. if (!m_Pop.Send(sBuf, nCmdLength))
  607. {
  608. TRACE(_T("Failed to send the RSET command to the POP3 servern"));
  609. return FALSE;
  610. }
  611. //And check the command
  612. return ReadCommandResponse();
  613. }
  614. BOOL CPop3Connection::Noop()
  615. {
  616. //Must be connected to perform a "NOOP"
  617. ASSERT(m_bConnected);
  618. //Send the NOOP command
  619. char sBuf[10];
  620. strcpy(sBuf, "NOOPrn");
  621. int nCmdLength = strlen(sBuf);
  622. if (!m_Pop.Send(sBuf, nCmdLength))
  623. {
  624. TRACE(_T("Failed to send the NOOP command to the POP3 servern"));
  625. return FALSE;
  626. }
  627. //And check the response
  628. return ReadCommandResponse();
  629. }
  630. BOOL CPop3Connection::Retrieve(int nMsg, CPop3Message& message)
  631. {
  632. //Must be connected to retrieve a message
  633. ASSERT(m_bConnected);
  634. //work out the size of the message to retrieve
  635. DWORD dwSize;
  636. if (GetMessageSize(nMsg, dwSize))
  637. {
  638. //Send the RETR command
  639. char sBuf[20];
  640. sprintf(sBuf, "RETR %drn", nMsg); 
  641. int nCmdLength = strlen(sBuf);
  642. if (!m_Pop.Send(sBuf, nCmdLength))
  643. {
  644. TRACE(_T("Failed to send the RETR command to the POP3 servern"));
  645. return FALSE;
  646. }
  647. //And check the command
  648. return ReadReturnResponse(message, dwSize);
  649. }
  650. else
  651. return FALSE;
  652. }
  653. BOOL CPop3Connection::GetMessageHeader(int nMsg, CPop3Message& message)
  654. {
  655. // Must be connected to retrieve a message
  656. ASSERT(m_bConnected);
  657. // make sure the message actually exists
  658. DWORD dwSize;
  659. if (GetMessageSize(nMsg, dwSize))
  660. {
  661. // Send the TOP command
  662. char sBuf[16];
  663. sprintf(sBuf, "TOP %d 0rn", nMsg);
  664. int nCmdLength = strlen(sBuf);
  665. if (!m_Pop.Send(sBuf, nCmdLength))
  666. {
  667. TRACE(_T("Failed to send the TOP command to the POP3 servern"));
  668. return FALSE;
  669. }
  670. // And check the command
  671. return ReadReturnResponse(message, dwSize);
  672. }
  673. else
  674. return FALSE;
  675. }
  676. BOOL CPop3Connection::ReadCommandResponse()
  677. {
  678. LPSTR pszOverFlowBuffer = NULL;
  679. char sBuf[1000];
  680. BOOL bSuccess = ReadResponse(sBuf, 1000, "rn", &pszOverFlowBuffer);
  681. if (pszOverFlowBuffer)
  682. delete [] pszOverFlowBuffer;
  683. return bSuccess;
  684. }
  685. LPSTR CPop3Connection::GetFirstCharInResponse(LPSTR pszData) const
  686. {
  687. while ((*pszData != 'n') && *pszData)
  688. ++pszData;
  689. //skip over the "n" onto the next line
  690. if (*pszData)
  691. ++pszData;
  692. return pszData;
  693. }
  694. BOOL CPop3Connection::ReadResponse(LPSTR pszBuffer, int nInitialBufSize, LPSTR pszTerminator, LPSTR* ppszOverFlowBuffer, int nGrowBy)
  695. {
  696. ASSERT(ppszOverFlowBuffer);   //Must have a valid string pointer
  697. ASSERT(*ppszOverFlowBuffer == NULL); //Initially it must point to a NULL string
  698. //must have been created first
  699. ASSERT(m_bConnected);
  700. int nTerminatorLen = strlen(pszTerminator);
  701. //The local variables which will receive the data
  702. LPSTR pszRecvBuffer = pszBuffer;
  703. int nBufSize = nInitialBufSize;
  704. //retrieve the reponse using until we
  705. //get the terminator or a timeout occurs
  706. BOOL bFoundTerminator = FALSE;
  707. int nReceived = 0;
  708. DWORD dwStartTicks = ::GetTickCount();
  709. while (!bFoundTerminator)
  710. {
  711. //Has the timeout occured
  712. if ((::GetTickCount() - dwStartTicks) > m_dwTimeout)
  713. {
  714. pszRecvBuffer[nReceived] = '';
  715. SetLastError(WSAETIMEDOUT);
  716. m_sLastCommandResponse = pszRecvBuffer; //Hive away the last command reponse
  717. return FALSE;
  718. }
  719. //check the socket for readability
  720. BOOL bReadible;
  721. if (!m_Pop.IsReadible(bReadible))
  722. {
  723. pszRecvBuffer[nReceived] = '';
  724. m_sLastCommandResponse = pszRecvBuffer; //Hive away the last command reponse
  725. return FALSE;
  726. }
  727. else if (!bReadible) //no data to receive, just loop around
  728. {
  729. Sleep(250); //Sleep for a while before we loop around again
  730. continue;
  731. }
  732. //receive the data from the socket
  733. int nBufRemaining = nBufSize-nReceived-1; //Allows allow one space for the NULL terminator
  734. if (nBufRemaining<0)
  735. nBufRemaining = 0;
  736. int nData = m_Pop.Receive(pszRecvBuffer+nReceived, nBufRemaining);
  737. //Reset the idle timeout if data was received
  738. if (nData > 0)
  739. {
  740. dwStartTicks = ::GetTickCount();
  741. //Increment the count of data received
  742. nReceived += nData;     
  743. }
  744. //If an error occurred receiving the data
  745. if (nData < 1)
  746. {
  747. //NULL terminate the data received
  748. if (pszRecvBuffer)
  749. pszBuffer[nReceived] = '';
  750. m_sLastCommandResponse = pszRecvBuffer; //Hive away the last command reponse
  751. return FALSE; 
  752. }
  753. else
  754. {
  755. //NULL terminate the data received
  756. if (pszRecvBuffer)
  757. pszRecvBuffer[nReceived] = '';
  758. if (nBufRemaining-nData == 0) //No space left in the current buffer
  759. {
  760. //Allocate the new receive buffer
  761. nBufSize += nGrowBy; //Grow the buffer by the specified amount
  762. LPSTR pszNewBuf = new char[nBufSize];
  763. //copy the old contents over to the new buffer and assign 
  764. //the new buffer to the local variable used for retreiving 
  765. //from the socket
  766. if (pszRecvBuffer)
  767. strcpy(pszNewBuf, pszRecvBuffer);
  768. pszRecvBuffer = pszNewBuf;
  769. //delete the old buffer if it was allocated
  770. if (*ppszOverFlowBuffer)
  771. delete [] *ppszOverFlowBuffer;
  772. //Remember the overflow buffer for the next time around
  773. *ppszOverFlowBuffer = pszNewBuf;
  774. }
  775. }
  776. //Check to see if the terminator character(s) have been found
  777. bFoundTerminator = (strncmp(&pszRecvBuffer[nReceived - nTerminatorLen], pszTerminator, nTerminatorLen) == 0);
  778. }
  779. //Remove the terminator from the response data
  780. pszRecvBuffer[nReceived - nTerminatorLen] = '';
  781. //determine if the response is an error
  782. BOOL bSuccess = (strnicmp(pszRecvBuffer,"+OK", 3) == 0);
  783. if (!bSuccess)
  784. {
  785. SetLastError(WSAEPROTONOSUPPORT);
  786. m_sLastCommandResponse = pszRecvBuffer; //Hive away the last command reponse
  787. }
  788. return bSuccess;
  789. }
  790. BOOL CPop3Connection::ReadReturnResponse(CPop3Message& message, DWORD dwSize)
  791. {
  792. //Must be connected to perform a "RETR"
  793. ASSERT(m_bConnected);
  794. //Retrieve the message body
  795. LPSTR pszOverFlowBuffer = NULL;
  796. int nSize = dwSize + 100;
  797. char* sBuf = new char[nSize];
  798. char* sMessageBuf = sBuf;
  799. if (!ReadResponse(sBuf, nSize, "rn.rn", &pszOverFlowBuffer, 32000))
  800. {
  801. delete [] sBuf;
  802. if (pszOverFlowBuffer)
  803. delete [] pszOverFlowBuffer;
  804. TRACE(_T("Error retrieving the RETR response"));
  805. return FALSE;
  806. }
  807. if (pszOverFlowBuffer)
  808. sMessageBuf = pszOverFlowBuffer;
  809. //determine if the response is an error
  810. if (strnicmp(sMessageBuf,"+OK", 3) != 0)
  811. {
  812. delete [] sBuf;
  813. if (pszOverFlowBuffer)
  814. delete [] pszOverFlowBuffer;
  815. SetLastError(WSAEPROTONOSUPPORT);
  816. TRACE(_T("POP3 server did not respond correctly to the RETR responsen"));
  817. return FALSE;
  818. }
  819. else
  820. {  
  821. //remove the first line which contains the +OK from the message
  822. char* pszFirst = GetFirstCharInResponse(sMessageBuf);
  823. VERIFY(pszFirst);
  824. //transfer the message contents to the message class
  825. int nMessageSize = sMessageBuf - pszFirst + strlen(sMessageBuf);
  826. // Do we already have memory allocated? If so, destroy it!
  827. if (message.m_pszMessage)
  828. {
  829. delete [] message.m_pszMessage;
  830. message.m_pszMessage = NULL;
  831. }
  832. message.m_pszMessage = new char[nMessageSize + 1];
  833. memcpy(message.m_pszMessage, pszFirst, nMessageSize);
  834. message.m_pszMessage[nMessageSize] = '';
  835. }
  836. delete [] sBuf;
  837. if (pszOverFlowBuffer)
  838. delete [] pszOverFlowBuffer;
  839. return TRUE; 
  840. }
  841. BOOL CPop3Connection::ReadListResponse(int nNumberOfMails)
  842. {
  843. //Must be connected to perform a "LIST"
  844. ASSERT(m_bConnected);
  845. //retrieve the reponse
  846. LPSTR pszOverFlowBuffer = NULL;
  847. int nSize = 14 * nNumberOfMails + 100;
  848. char* sBuf = new char[nSize];
  849. char* sMessageBuf = sBuf;
  850. if (!ReadResponse(sBuf, nSize, "rn.rn", &pszOverFlowBuffer))
  851. {
  852. delete [] sBuf;
  853. if (pszOverFlowBuffer)
  854. delete [] pszOverFlowBuffer;
  855. TRACE(_T("Error retrieving the LIST response from the POP3 server"));
  856. return FALSE;
  857. }
  858. if (pszOverFlowBuffer)
  859. sMessageBuf = pszOverFlowBuffer;
  860. //determine if the response is an error
  861. if (strnicmp(sMessageBuf,"+OK", 3) != 0)
  862. {
  863. delete [] sBuf;
  864. if (pszOverFlowBuffer)
  865. delete [] pszOverFlowBuffer;
  866. SetLastError(WSAEPROTONOSUPPORT);
  867. TRACE(_T("POP3 server did not respond correctly to the LIST responsen"));
  868. return FALSE;
  869. }
  870. else
  871. {  
  872. //Retrieve the message sizes and put them
  873. //into the m_msgSizes array
  874. m_msgSizes.RemoveAll();
  875. m_msgSizes.SetSize(0, nNumberOfMails);
  876. //then parse the LIST response
  877. char* pszSize = GetFirstCharInResponse(sMessageBuf);
  878. VERIFY(pszSize);
  879. for (; *pszSize != '.'; pszSize++)
  880. if (*pszSize == 't' || *pszSize == ' ')
  881. m_msgSizes.Add(atoi(pszSize));
  882. }
  883. delete [] sBuf;
  884. if (pszOverFlowBuffer)
  885. delete [] pszOverFlowBuffer;
  886. return TRUE; 
  887. }
  888. BOOL CPop3Connection::ReadUIDLResponse(int nNumberOfMails)
  889. {
  890. //Must be connected to perform a "UIDL"
  891. ASSERT(m_bConnected);
  892. //retrieve the reponse
  893. LPSTR pszOverFlowBuffer = NULL;
  894. int nSize = 14 * nNumberOfMails + 100;
  895. char* sBuf = new char[nSize];
  896. char* sMessageBuf = sBuf;
  897. if (!ReadResponse(sBuf, nSize, "rn.rn", &pszOverFlowBuffer))
  898. {
  899. delete [] sBuf;
  900. if (pszOverFlowBuffer)
  901. delete [] pszOverFlowBuffer;
  902. TRACE(_T("Error retrieving the UIDL response from the POP3 server"));
  903. return FALSE;
  904. }
  905. if (pszOverFlowBuffer)
  906. sMessageBuf = pszOverFlowBuffer;
  907. //determine if the response is an error
  908. if (strnicmp(sMessageBuf,"+OK", 3) != 0)
  909. {
  910. delete [] sBuf;
  911. if (pszOverFlowBuffer)
  912. delete [] pszOverFlowBuffer;
  913. SetLastError(WSAEPROTONOSUPPORT);
  914. TRACE(_T("POP3 server did not respond correctly to the UIDL responsen"));
  915. return FALSE;
  916. }
  917. else
  918. {  
  919. //Retrieve the message ID's and put them
  920. //into the m_msgIDs array
  921. m_msgIDs.RemoveAll();
  922. m_msgIDs.SetSize(0, nNumberOfMails);
  923. //then parse the UIDL response
  924. char* pszSize = GetFirstCharInResponse(sMessageBuf);
  925. VERIFY(pszSize);
  926. for (int iCount=0; iCount<nNumberOfMails; iCount++)
  927. {
  928. char* pszBegin = pszSize;
  929. while (*pszSize != 'n' && *pszSize != '')
  930. ++pszSize;
  931. char sMsg[15];
  932. char sID[1000];
  933. *pszSize = '';
  934. sscanf(pszBegin, "%s %s", sMsg, sID);
  935. m_msgIDs.Add(CString(sID));
  936. pszSize++;
  937. }
  938. }
  939. delete [] sBuf;
  940. if (pszOverFlowBuffer)
  941. delete [] pszOverFlowBuffer;
  942. return TRUE; 
  943. }
  944. BOOL CPop3Connection::ReadStatResponse(int& nNumberOfMails, int& nTotalMailSize)
  945. {
  946. //Must be connected to perform a "STAT"
  947. ASSERT(m_bConnected);
  948. //retrieve the reponse
  949. LPSTR pszOverFlowBuffer = NULL;
  950. char sBuf[100];
  951. char* sMessageBuf = sBuf;
  952. if (!ReadResponse(sBuf, 100, "rn", &pszOverFlowBuffer))
  953. {
  954. if (pszOverFlowBuffer)
  955. delete [] pszOverFlowBuffer;
  956. TRACE(_T("Error retrieving the STAT response from the POP3 server"));
  957. return FALSE;
  958. }
  959. if (pszOverFlowBuffer)
  960. sMessageBuf = pszOverFlowBuffer;
  961. //determine if the response is an error
  962. if (strncmp(sMessageBuf,"+OK", 3) != 0)
  963. {
  964. TRACE(_T("POP3 server did not respond correctly to the STAT responsen"));
  965. return FALSE;
  966. }
  967. else
  968. {    
  969. //Parse out the Number of Mails and Total mail size values
  970. BOOL bGetNumber = TRUE;
  971. for (char* pszNum=sMessageBuf; *pszNum!=''; pszNum++)
  972. {
  973. if (*pszNum=='t' || *pszNum==' ')
  974. {
  975. if (bGetNumber)
  976. {
  977. nNumberOfMails = atoi(pszNum);
  978. m_nNumberOfMails = nNumberOfMails;
  979. bGetNumber = FALSE;
  980. }
  981. else
  982. {
  983. nTotalMailSize = atoi(pszNum);
  984. return TRUE;
  985. }
  986. }
  987. }
  988. }
  989. if (pszOverFlowBuffer)
  990. delete [] pszOverFlowBuffer;
  991. return FALSE; 
  992. }