Applied backgroundcolors.patch
[TortoiseGit.git] / src / Utils / HwSMTP.cpp
blobd5824cb4237a75cd8098e6ae8d73c626072ef87b
1 // HwSMTP.cpp: implementation of the CHwSMTP class.
2 //
3 // Schannel/SSPI implementation based on http://www.coastrd.com/c-schannel-smtp
4 //
5 //////////////////////////////////////////////////////////////////////
7 #include "stdafx.h"
8 #include "afxstr.h"
9 #include "HwSMTP.h"
10 #include "SpeedPostEmail.h"
11 #include "Windns.h"
12 #include <Afxmt.h>
13 #include "FormatMessageWrapper.h"
14 #include <atlenc.h>
15 #include "AppUtils.h"
16 #include "PathUtils.h"
17 #include "StringUtils.h"
19 #define IO_BUFFER_SIZE 0x10000
21 #pragma comment(lib, "Dnsapi.lib")
22 #pragma comment(lib, "Rpcrt4.lib")
23 #pragma comment(lib, "Secur32.lib")
25 DWORD dwProtocol = SP_PROT_TLS1; // SP_PROT_TLS1; // SP_PROT_PCT1; SP_PROT_SSL2; SP_PROT_SSL3; 0=default
26 ALG_ID aiKeyExch = 0; // = default; CALG_DH_EPHEM; CALG_RSA_KEYX;
28 SCHANNEL_CRED SchannelCred;
29 PSecurityFunctionTable g_pSSPI;
31 //////////////////////////////////////////////////////////////////////
32 // Construction/Destruction
33 //////////////////////////////////////////////////////////////////////
35 static CString GetGUID()
37 CString sGuid;
38 GUID guid;
39 if (CoCreateGuid(&guid) == S_OK)
41 RPC_WSTR guidStr;
42 if (UuidToString(&guid, &guidStr) == RPC_S_OK)
44 sGuid = (LPTSTR)guidStr;
45 RpcStringFree(&guidStr);
48 return sGuid;
51 CHwSMTP::CHwSMTP () :
52 m_bConnected ( FALSE ),
53 m_nSmtpSrvPort ( 25 ),
54 m_bMustAuth ( TRUE )
56 m_csPartBoundary = L"NextPart_" + GetGUID();
57 m_csMIMEContentType.Format(L"multipart/mixed; boundary=%s", (LPCTSTR)m_csPartBoundary);
58 m_csNoMIMEText = L"This is a multi-part message in MIME format.";
59 //m_csCharSet = L"\r\n\tcharset=\"iso-8859-1\"\r\n";
61 hContext = nullptr;
62 hCreds = nullptr;
63 pbIoBuffer = nullptr;
64 cbIoBufferLength = 0;
66 m_iSecurityLevel = none;
68 SecureZeroMemory(&Sizes, sizeof(SecPkgContext_StreamSizes));
70 AfxSocketInit();
73 CHwSMTP::~CHwSMTP()
77 CString CHwSMTP::GetServerAddress(const CString& in)
79 CString email;
80 CStringUtils::ParseEmailAddress(in, email);
82 int start = email.Find(L'@');
83 return email.Mid(start + 1);
86 BOOL CHwSMTP::SendSpeedEmail
88 LPCTSTR lpszAddrFrom,
89 LPCTSTR lpszAddrTo,
90 LPCTSTR lpszSubject,
91 LPCTSTR lpszBody,
92 LPCTSTR lpszCharSet,
93 CStringArray *pStrAryAttach,
94 LPCTSTR pStrAryCC,
95 LPCTSTR pSend
98 BOOL ret=true;
99 CString To;
100 To += GET_SAFE_STRING(lpszAddrTo);
101 To += L';';
102 To += GET_SAFE_STRING(pStrAryCC);
104 std::map<CString,std::vector<CString>> Address;
106 int start = 0;
107 while( start >= 0 )
109 CString one = To.Tokenize(L";", start).Trim();
110 if(one.IsEmpty())
111 continue;
113 CString addr;
114 addr = GetServerAddress(one);
115 if(addr.IsEmpty())
116 continue;
118 Address[addr].push_back(one);
122 std::map<CString,std::vector<CString>>::iterator itr1 = Address.begin();
123 for( ; itr1 != Address.end(); ++itr1 )
125 PDNS_RECORD pDnsRecord;
126 PDNS_RECORD pNext;
128 DNS_STATUS status =
129 DnsQuery(itr1->first ,
130 DNS_TYPE_MX,DNS_QUERY_STANDARD,
131 nullptr, //Contains DNS server IP address.
132 &pDnsRecord, //Resource record that contains the response.
133 nullptr
135 if (status)
137 m_csLastError.Format(L"DNS query failed %d", status);
138 ret = false;
139 continue;
142 CString to;
143 to.Empty();
144 for (size_t i = 0; i < itr1->second.size(); ++i)
146 to+=itr1->second[i];
147 to += L';';
149 if(to.IsEmpty())
150 continue;
152 pNext=pDnsRecord;
153 while(pNext)
155 if(pNext->wType == DNS_TYPE_MX)
156 if (SendEmail(pNext->Data.MX.pNameExchange, nullptr, nullptr, false,
157 lpszAddrFrom,to,lpszSubject,lpszBody,lpszCharSet,pStrAryAttach,pStrAryCC,
158 25,pSend,lpszAddrTo))
159 break;
160 pNext=pNext->pNext;
162 if (!pNext)
163 ret = false;
165 if (pDnsRecord)
166 DnsRecordListFree(pDnsRecord,DnsFreeRecordList);
169 return ret;
172 static SECURITY_STATUS ClientHandshakeLoop(CSocket * Socket, PCredHandle phCreds, CtxtHandle * phContext, BOOL fDoInitialRead, SecBuffer * pExtraData)
174 SecBufferDesc OutBuffer, InBuffer;
175 SecBuffer InBuffers[2], OutBuffers[1];
176 DWORD dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer;
177 TimeStamp tsExpiry;
178 SECURITY_STATUS scRet;
179 BOOL fDoRead;
181 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY |
182 ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
184 // Allocate data buffer.
185 auto IoBuffer = std::make_unique<UCHAR[]>(IO_BUFFER_SIZE);
186 if (!IoBuffer)
188 // printf("**** Out of memory (1)\n");
189 return SEC_E_INTERNAL_ERROR;
191 cbIoBuffer = 0;
192 fDoRead = fDoInitialRead;
194 // Loop until the handshake is finished or an error occurs.
195 scRet = SEC_I_CONTINUE_NEEDED;
197 while (scRet == SEC_I_CONTINUE_NEEDED || scRet == SEC_E_INCOMPLETE_MESSAGE || scRet == SEC_I_INCOMPLETE_CREDENTIALS)
199 if (0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) // Read data from server.
201 if (fDoRead)
203 cbData = Socket->Receive(IoBuffer.get() + cbIoBuffer, IO_BUFFER_SIZE - cbIoBuffer, 0);
204 if (cbData == SOCKET_ERROR)
206 // printf("**** Error %d reading data from server\n", WSAGetLastError());
207 scRet = SEC_E_INTERNAL_ERROR;
208 break;
210 else if (cbData == 0)
212 // printf("**** Server unexpectedly disconnected\n");
213 scRet = SEC_E_INTERNAL_ERROR;
214 break;
216 // printf("%d bytes of handshake data received\n", cbData);
217 cbIoBuffer += cbData;
219 else
220 fDoRead = TRUE;
223 // Set up the input buffers. Buffer 0 is used to pass in data
224 // received from the server. Schannel will consume some or all
225 // of this. Leftover data (if any) will be placed in buffer 1 and
226 // given a buffer type of SECBUFFER_EXTRA.
227 InBuffers[0].pvBuffer = IoBuffer.get();
228 InBuffers[0].cbBuffer = cbIoBuffer;
229 InBuffers[0].BufferType = SECBUFFER_TOKEN;
231 InBuffers[1].pvBuffer = nullptr;
232 InBuffers[1].cbBuffer = 0;
233 InBuffers[1].BufferType = SECBUFFER_EMPTY;
235 InBuffer.cBuffers = 2;
236 InBuffer.pBuffers = InBuffers;
237 InBuffer.ulVersion = SECBUFFER_VERSION;
239 // Set up the output buffers. These are initialized to nullptr
240 // so as to make it less likely we'll attempt to free random
241 // garbage later.
242 OutBuffers[0].pvBuffer = nullptr;
243 OutBuffers[0].BufferType= SECBUFFER_TOKEN;
244 OutBuffers[0].cbBuffer = 0;
246 OutBuffer.cBuffers = 1;
247 OutBuffer.pBuffers = OutBuffers;
248 OutBuffer.ulVersion = SECBUFFER_VERSION;
250 // Call InitializeSecurityContext.
251 scRet = g_pSSPI->InitializeSecurityContext(phCreds, phContext, nullptr, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &InBuffer, 0, nullptr, &OutBuffer, &dwSSPIOutFlags, &tsExpiry);
253 // If InitializeSecurityContext was successful (or if the error was
254 // one of the special extended ones), send the contends of the output
255 // buffer to the server.
256 if (scRet == SEC_E_OK || scRet == SEC_I_CONTINUE_NEEDED || FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
258 if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != nullptr)
260 cbData = Socket->Send(OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0 );
261 if(cbData == SOCKET_ERROR || cbData == 0)
263 // printf( "**** Error %d sending data to server (2)\n", WSAGetLastError() );
264 g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
265 g_pSSPI->DeleteSecurityContext(phContext);
266 return SEC_E_INTERNAL_ERROR;
268 // printf("%d bytes of handshake data sent\n", cbData);
270 // Free output buffer.
271 g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
272 OutBuffers[0].pvBuffer = nullptr;
276 // If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
277 // then we need to read more data from the server and try again.
278 if (scRet == SEC_E_INCOMPLETE_MESSAGE) continue;
280 // If InitializeSecurityContext returned SEC_E_OK, then the
281 // handshake completed successfully.
282 if (scRet == SEC_E_OK)
284 // If the "extra" buffer contains data, this is encrypted application
285 // protocol layer stuff. It needs to be saved. The application layer
286 // will later decrypt it with DecryptMessage.
287 // printf("Handshake was successful\n");
289 if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
291 pExtraData->pvBuffer = LocalAlloc( LMEM_FIXED, InBuffers[1].cbBuffer );
292 if (pExtraData->pvBuffer == nullptr)
294 // printf("**** Out of memory (2)\n");
295 return SEC_E_INTERNAL_ERROR;
298 MoveMemory(pExtraData->pvBuffer, IoBuffer.get() + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer);
300 pExtraData->cbBuffer = InBuffers[1].cbBuffer;
301 pExtraData->BufferType = SECBUFFER_TOKEN;
303 // printf( "%d bytes of app data was bundled with handshake data\n", pExtraData->cbBuffer );
305 else
307 pExtraData->pvBuffer = nullptr;
308 pExtraData->cbBuffer = 0;
309 pExtraData->BufferType = SECBUFFER_EMPTY;
311 break; // Bail out to quit
314 // Check for fatal error.
315 if (FAILED(scRet))
317 // printf("**** Error 0x%x returned by InitializeSecurityContext (2)\n", scRet);
318 break;
321 // If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
322 // then the server just requested client authentication.
323 if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
325 // Busted. The server has requested client authentication and
326 // the credential we supplied didn't contain a client certificate.
327 // This function will read the list of trusted certificate
328 // authorities ("issuers") that was received from the server
329 // and attempt to find a suitable client certificate that
330 // was issued by one of these. If this function is successful,
331 // then we will connect using the new certificate. Otherwise,
332 // we will attempt to connect anonymously (using our current credentials).
333 //GetNewClientCredentials(phCreds, phContext);
335 // Go around again.
336 fDoRead = FALSE;
337 scRet = SEC_I_CONTINUE_NEEDED;
338 continue;
341 // Copy any leftover data from the "extra" buffer, and go around again.
342 if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
344 MoveMemory(IoBuffer.get(), IoBuffer.get() + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer);
345 cbIoBuffer = InBuffers[1].cbBuffer;
347 else
348 cbIoBuffer = 0;
351 // Delete the security context in the case of a fatal error.
352 if (FAILED(scRet))
353 g_pSSPI->DeleteSecurityContext(phContext);
355 return scRet;
358 static SECURITY_STATUS PerformClientHandshake( CSocket * Socket, PCredHandle phCreds, LPTSTR pszServerName, CtxtHandle * phContext, SecBuffer * pExtraData)
360 SecBufferDesc OutBuffer;
361 SecBuffer OutBuffers[1];
362 DWORD dwSSPIFlags, dwSSPIOutFlags, cbData;
363 TimeStamp tsExpiry;
364 SECURITY_STATUS scRet;
366 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY |
367 ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
369 // Initiate a ClientHello message and generate a token.
370 OutBuffers[0].pvBuffer = nullptr;
371 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
372 OutBuffers[0].cbBuffer = 0;
374 OutBuffer.cBuffers = 1;
375 OutBuffer.pBuffers = OutBuffers;
376 OutBuffer.ulVersion = SECBUFFER_VERSION;
378 scRet = g_pSSPI->InitializeSecurityContext(phCreds, nullptr, pszServerName, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, nullptr, 0, phContext, &OutBuffer, &dwSSPIOutFlags, &tsExpiry);
380 if (scRet != SEC_I_CONTINUE_NEEDED)
382 // printf("**** Error %d returned by InitializeSecurityContext (1)\n", scRet);
383 return scRet;
386 // Send response to server if there is one.
387 if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != nullptr)
389 cbData = Socket->Send(OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
390 if (cbData == SOCKET_ERROR || cbData == 0)
392 // printf("**** Error %d sending data to server (1)\n", WSAGetLastError());
393 g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
394 g_pSSPI->DeleteSecurityContext(phContext);
395 return SEC_E_INTERNAL_ERROR;
397 // printf("%d bytes of handshake data sent\n", cbData);
399 g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer); // Free output buffer.
400 OutBuffers[0].pvBuffer = nullptr;
403 return ClientHandshakeLoop(Socket, phCreds, phContext, TRUE, pExtraData);
406 static SECURITY_STATUS CreateCredentials(PCredHandle phCreds)
408 TimeStamp tsExpiry;
409 SECURITY_STATUS Status;
410 DWORD cSupportedAlgs = 0;
411 ALG_ID rgbSupportedAlgs[16];
413 // Build Schannel credential structure. Currently, this sample only
414 // specifies the protocol to be used (and optionally the certificate,
415 // of course). Real applications may wish to specify other parameters as well.
416 SecureZeroMemory(&SchannelCred, sizeof(SchannelCred));
418 SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
419 SchannelCred.grbitEnabledProtocols = dwProtocol;
421 if (aiKeyExch)
422 rgbSupportedAlgs[cSupportedAlgs++] = aiKeyExch;
424 if (cSupportedAlgs)
426 SchannelCred.cSupportedAlgs = cSupportedAlgs;
427 SchannelCred.palgSupportedAlgs = rgbSupportedAlgs;
430 SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
432 // The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because
433 // this sample verifies the server certificate manually.
434 // Applications that expect to run on WinNT, Win9x, or WinME
435 // should specify this flag and also manually verify the server
436 // certificate. Applications running on newer versions of Windows can
437 // leave off this flag, in which case the InitializeSecurityContext
438 // function will validate the server certificate automatically.
439 SchannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
441 // Create an SSPI credential.
442 Status = g_pSSPI->AcquireCredentialsHandle(nullptr, // Name of principal
443 UNISP_NAME, // Name of package
444 SECPKG_CRED_OUTBOUND, // Flags indicating use
445 nullptr, // Pointer to logon ID
446 &SchannelCred, // Package specific data
447 nullptr, // Pointer to GetKey() func
448 nullptr, // Value to pass to GetKey()
449 phCreds, // (out) Cred Handle
450 &tsExpiry ); // (out) Lifetime (optional)
452 return Status;
455 static DWORD EncryptSend(CSocket * Socket, CtxtHandle * phContext, PBYTE pbIoBuffer, SecPkgContext_StreamSizes Sizes)
456 // http://msdn.microsoft.com/en-us/library/aa375378(VS.85).aspx
457 // The encrypted message is encrypted in place, overwriting the original contents of its buffer.
459 SECURITY_STATUS scRet;
460 SecBufferDesc Message;
461 SecBuffer Buffers[4];
462 DWORD cbMessage;
463 PBYTE pbMessage;
465 pbMessage = pbIoBuffer + Sizes.cbHeader; // Offset by "header size"
466 cbMessage = (DWORD)strlen((char *)pbMessage);
468 // Encrypt the HTTP request.
469 Buffers[0].pvBuffer = pbIoBuffer; // Pointer to buffer 1
470 Buffers[0].cbBuffer = Sizes.cbHeader; // length of header
471 Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; // Type of the buffer
473 Buffers[1].pvBuffer = pbMessage; // Pointer to buffer 2
474 Buffers[1].cbBuffer = cbMessage; // length of the message
475 Buffers[1].BufferType = SECBUFFER_DATA; // Type of the buffer
477 Buffers[2].pvBuffer = pbMessage + cbMessage; // Pointer to buffer 3
478 Buffers[2].cbBuffer = Sizes.cbTrailer; // length of the trailor
479 Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER; // Type of the buffer
481 Buffers[3].pvBuffer = SECBUFFER_EMPTY; // Pointer to buffer 4
482 Buffers[3].cbBuffer = SECBUFFER_EMPTY; // length of buffer 4
483 Buffers[3].BufferType = SECBUFFER_EMPTY; // Type of the buffer 4
485 Message.ulVersion = SECBUFFER_VERSION; // Version number
486 Message.cBuffers = 4; // Number of buffers - must contain four SecBuffer structures.
487 Message.pBuffers = Buffers; // Pointer to array of buffers
489 scRet = g_pSSPI->EncryptMessage(phContext, 0, &Message, 0); // must contain four SecBuffer structures.
490 if (FAILED(scRet))
492 // printf("**** Error 0x%x returned by EncryptMessage\n", scRet);
493 return scRet;
497 // Send the encrypted data to the server.
498 return Socket->Send(pbIoBuffer, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer, 0);
501 static LONG DisconnectFromServer(CSocket * Socket, PCredHandle phCreds, CtxtHandle * phContext)
503 PBYTE pbMessage;
504 DWORD dwType, dwSSPIFlags, dwSSPIOutFlags, cbMessage, cbData, Status;
505 SecBufferDesc OutBuffer;
506 SecBuffer OutBuffers[1];
507 TimeStamp tsExpiry;
509 dwType = SCHANNEL_SHUTDOWN; // Notify schannel that we are about to close the connection.
511 OutBuffers[0].pvBuffer = &dwType;
512 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
513 OutBuffers[0].cbBuffer = sizeof(dwType);
515 OutBuffer.cBuffers = 1;
516 OutBuffer.pBuffers = OutBuffers;
517 OutBuffer.ulVersion = SECBUFFER_VERSION;
519 Status = g_pSSPI->ApplyControlToken(phContext, &OutBuffer);
520 if (FAILED(Status))
522 // printf("**** Error 0x%x returned by ApplyControlToken\n", Status);
523 goto cleanup;
526 // Build an SSL close notify message.
527 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
529 OutBuffers[0].pvBuffer = nullptr;
530 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
531 OutBuffers[0].cbBuffer = 0;
533 OutBuffer.cBuffers = 1;
534 OutBuffer.pBuffers = OutBuffers;
535 OutBuffer.ulVersion = SECBUFFER_VERSION;
537 Status = g_pSSPI->InitializeSecurityContext(phCreds, phContext, nullptr, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, nullptr, 0, phContext, &OutBuffer, &dwSSPIOutFlags, &tsExpiry);
539 if (FAILED(Status))
541 // printf("**** Error 0x%x returned by InitializeSecurityContext\n", Status);
542 goto cleanup;
545 pbMessage = (PBYTE)OutBuffers[0].pvBuffer;
546 cbMessage = OutBuffers[0].cbBuffer;
548 // Send the close notify message to the server.
549 if (pbMessage != nullptr && cbMessage != 0)
551 cbData = Socket->Send(pbMessage, cbMessage, 0);
552 if (cbData == SOCKET_ERROR || cbData == 0)
554 Status = WSAGetLastError();
555 goto cleanup;
557 // printf("Sending Close Notify\n");
558 // printf("%d bytes of handshake data sent\n", cbData);
559 g_pSSPI->FreeContextBuffer(pbMessage); // Free output buffer.
562 cleanup:
563 g_pSSPI->DeleteSecurityContext(phContext); // Free the security context.
564 Socket->Close();
566 return Status;
569 static SECURITY_STATUS ReadDecrypt(CSocket * Socket, PCredHandle phCreds, CtxtHandle * phContext, PBYTE pbIoBuffer, DWORD cbIoBufferLength)
571 // calls recv() - blocking socket read
572 // http://msdn.microsoft.com/en-us/library/ms740121(VS.85).aspx
574 // The encrypted message is decrypted in place, overwriting the original contents of its buffer.
575 // http://msdn.microsoft.com/en-us/library/aa375211(VS.85).aspx
578 SecBuffer ExtraBuffer;
579 SecBuffer * pDataBuffer, * pExtraBuffer;
581 SECURITY_STATUS scRet;
582 SecBufferDesc Message;
583 SecBuffer Buffers[4];
585 DWORD cbIoBuffer, cbData, length;
586 PBYTE buff;
588 // Read data from server until done.
589 cbIoBuffer = 0;
590 scRet = 0;
591 while (TRUE) // Read some data.
593 if (cbIoBuffer == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE) // get the data
595 cbData = Socket->Receive(pbIoBuffer + cbIoBuffer, cbIoBufferLength - cbIoBuffer, 0);
596 if (cbData == SOCKET_ERROR)
598 // printf("**** Error %d reading data from server\n", WSAGetLastError());
599 scRet = SEC_E_INTERNAL_ERROR;
600 break;
602 else if (cbData == 0) // Server disconnected.
604 if (cbIoBuffer)
606 // printf("**** Server unexpectedly disconnected\n");
607 scRet = SEC_E_INTERNAL_ERROR;
608 return scRet;
610 else
611 break; // All Done
613 else // success
615 // printf("%d bytes of (encrypted) application data received\n", cbData);
616 cbIoBuffer += cbData;
620 // Decrypt the received data.
621 Buffers[0].pvBuffer = pbIoBuffer;
622 Buffers[0].cbBuffer = cbIoBuffer;
623 Buffers[0].BufferType = SECBUFFER_DATA; // Initial Type of the buffer 1
624 Buffers[1].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 2
625 Buffers[2].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 3
626 Buffers[3].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 4
628 Message.ulVersion = SECBUFFER_VERSION; // Version number
629 Message.cBuffers = 4; // Number of buffers - must contain four SecBuffer structures.
630 Message.pBuffers = Buffers; // Pointer to array of buffers
632 scRet = g_pSSPI->DecryptMessage(phContext, &Message, 0, nullptr);
633 if (scRet == SEC_I_CONTEXT_EXPIRED)
634 break; // Server signalled end of session
635 // if (scRet == SEC_E_INCOMPLETE_MESSAGE - Input buffer has partial encrypted record, read more
636 if (scRet != SEC_E_OK && scRet != SEC_I_RENEGOTIATE && scRet != SEC_I_CONTEXT_EXPIRED)
637 return scRet;
639 // Locate data and (optional) extra buffers.
640 pDataBuffer = nullptr;
641 pExtraBuffer = nullptr;
642 for (int i = 1; i < 4; ++i)
644 if (pDataBuffer == nullptr && Buffers[i].BufferType == SECBUFFER_DATA)
645 pDataBuffer = &Buffers[i];
646 if (pExtraBuffer == nullptr && Buffers[i].BufferType == SECBUFFER_EXTRA)
647 pExtraBuffer = &Buffers[i];
650 // Display the decrypted data.
651 if (pDataBuffer)
653 length = pDataBuffer->cbBuffer;
654 if (length) // check if last two chars are CR LF
656 buff = (PBYTE)pDataBuffer->pvBuffer;
657 if (buff[length-2] == 13 && buff[length-1] == 10) // Found CRLF
659 buff[length] = 0;
660 break;
665 // Move any "extra" data to the input buffer.
666 if (pExtraBuffer)
668 MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
669 cbIoBuffer = pExtraBuffer->cbBuffer;
671 else
672 cbIoBuffer = 0;
674 // The server wants to perform another handshake sequence.
675 if (scRet == SEC_I_RENEGOTIATE)
677 // printf("Server requested renegotiate!\n");
678 scRet = ClientHandshakeLoop( Socket, phCreds, phContext, FALSE, &ExtraBuffer);
679 if (scRet != SEC_E_OK)
680 return scRet;
682 if (ExtraBuffer.pvBuffer) // Move any "extra" data to the input buffer.
684 MoveMemory(pbIoBuffer, ExtraBuffer.pvBuffer, ExtraBuffer.cbBuffer);
685 cbIoBuffer = ExtraBuffer.cbBuffer;
688 } // Loop till CRLF is found at the end of the data
690 return SEC_E_OK;
693 BOOL CHwSMTP::SendEmail (
694 LPCTSTR lpszSmtpSrvHost,
695 LPCTSTR lpszUserName,
696 LPCTSTR lpszPasswd,
697 BOOL bMustAuth,
698 LPCTSTR lpszAddrFrom,
699 LPCTSTR lpszAddrTo,
700 LPCTSTR lpszSubject,
701 LPCTSTR lpszBody,
702 LPCTSTR lpszCharSet, // ×Ö·û¼¯ÀàÐÍ£¬ÀýÈ磺·±ÌåÖÐÎÄÕâÀïÓ¦ÊäÈë"big5"£¬¼òÌåÖÐÎÄʱÊäÈë"gb2312"
703 CStringArray* pStrAryAttach/*=nullptr*/,
704 LPCTSTR pStrAryCC/*=nullptr*/,
705 UINT nSmtpSrvPort,/*=25*/
706 LPCTSTR pSender,
707 LPCTSTR pToList,
708 DWORD secLevel
711 m_StrAryAttach.RemoveAll();
713 m_StrCC += GET_SAFE_STRING(pStrAryCC);
715 m_csSmtpSrvHost = GET_SAFE_STRING ( lpszSmtpSrvHost );
716 if ( m_csSmtpSrvHost.GetLength() <= 0 )
718 m_csLastError = L"Parameter Error!";
719 return FALSE;
721 m_csUserName = GET_SAFE_STRING ( lpszUserName );
722 m_csPasswd = GET_SAFE_STRING ( lpszPasswd );
723 m_bMustAuth = bMustAuth;
724 if ( m_bMustAuth && m_csUserName.GetLength() <= 0 )
726 m_csLastError = L"Parameter Error!";
727 return FALSE;
730 m_csAddrFrom = GET_SAFE_STRING ( lpszAddrFrom );
731 m_csAddrTo = GET_SAFE_STRING ( lpszAddrTo );
732 // m_csFromName = GET_SAFE_STRING ( lpszFromName );
733 // m_csReceiverName = GET_SAFE_STRING ( lpszReceiverName );
734 m_csSubject = GET_SAFE_STRING ( lpszSubject );
735 m_csBody = GET_SAFE_STRING ( lpszBody );
737 this->m_csSender = GET_SAFE_STRING(pSender);
738 this->m_csToList = GET_SAFE_STRING(pToList);
740 m_nSmtpSrvPort = nSmtpSrvPort;
742 if ( lpszCharSet && lstrlen(lpszCharSet) > 0 )
743 m_csCharSet.Format(L"\r\n\tcharset=\"%s\"\r\n", lpszCharSet);
745 if (
746 m_csAddrFrom.GetLength() <= 0 || m_csAddrTo.GetLength() <= 0
749 m_csLastError = L"Parameter Error!";
750 return FALSE;
753 if ( pStrAryAttach )
755 m_StrAryAttach.Append ( *pStrAryAttach );
757 if ( m_StrAryAttach.GetSize() < 1 )
758 m_csMIMEContentType.Format(L"text/plain; %s", (LPCTSTR)m_csCharSet);
760 // ´´½¨Socket
761 m_SendSock.Close();
762 if ( !m_SendSock.Create () )
764 //int nResult = GetLastError();
765 m_csLastError = L"Create socket failed!";
766 return FALSE;
769 switch (secLevel)
771 case 1:
772 m_iSecurityLevel = want_tls;
773 break;
774 case 2:
775 m_iSecurityLevel = ssl;
776 break;
777 default:
778 m_iSecurityLevel = none;
781 if ( !m_SendSock.Connect ( m_csSmtpSrvHost, m_nSmtpSrvPort ) )
783 m_csLastError.Format(L"Connect to [%s] failed", (LPCTSTR)m_csSmtpSrvHost);
784 return FALSE;
787 if (m_iSecurityLevel == want_tls) {
788 if (!GetResponse("220"))
789 return FALSE;
790 m_bConnected = TRUE;
791 Send(L"STARTTLS\r\n");
792 if (!GetResponse("220"))
793 return FALSE;
794 m_iSecurityLevel = tls_established;
797 BOOL ret = FALSE;
799 SecBuffer ExtraData;
800 SECURITY_STATUS Status;
802 CtxtHandle contextStruct;
803 CredHandle credentialsStruct;
805 if (m_iSecurityLevel >= ssl)
807 g_pSSPI = InitSecurityInterface();
809 contextStruct.dwLower = 0;
810 contextStruct.dwUpper = 0;
812 hCreds = &credentialsStruct;
813 credentialsStruct.dwLower = 0;
814 credentialsStruct.dwUpper = 0;
815 Status = CreateCredentials(hCreds);
816 if (Status != SEC_E_OK)
818 m_csLastError = CFormatMessageWrapper(Status);
819 return FALSE;
822 hContext = &contextStruct;
823 Status = PerformClientHandshake(&m_SendSock, hCreds, m_csSmtpSrvHost.GetBuffer(), hContext, &ExtraData);
824 if (Status != SEC_E_OK)
826 m_csLastError = CFormatMessageWrapper(Status);
827 return FALSE;
830 PCCERT_CONTEXT pRemoteCertContext = nullptr;
831 // Authenticate server's credentials. Get server's certificate.
832 Status = g_pSSPI->QueryContextAttributes(hContext, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext);
833 if (Status)
835 m_csLastError = CFormatMessageWrapper(Status);
836 goto cleanup;
839 git_cert_x509 cert;
840 cert.parent.cert_type = GIT_CERT_X509;
841 cert.data = pRemoteCertContext->pbCertEncoded;
842 cert.len = pRemoteCertContext->cbCertEncoded;
843 if (CAppUtils::Git2CertificateCheck((git_cert*)&cert, 0, CUnicodeUtils::GetUTF8(m_csSmtpSrvHost), nullptr))
845 CertFreeCertificateContext(pRemoteCertContext);
846 m_csLastError = L"Invalid certificate.";
847 goto cleanup;
850 CertFreeCertificateContext(pRemoteCertContext);
852 Status = g_pSSPI->QueryContextAttributes(hContext, SECPKG_ATTR_STREAM_SIZES, &Sizes);
853 if (Status)
855 m_csLastError = CFormatMessageWrapper(Status);
856 goto cleanup;
859 // Create a buffer.
860 cbIoBufferLength = Sizes.cbHeader + Sizes.cbMaximumMessage + Sizes.cbTrailer;
861 pbIoBuffer = (PBYTE)LocalAlloc(LMEM_FIXED, cbIoBufferLength);
862 SecureZeroMemory(pbIoBuffer, cbIoBufferLength);
863 if (pbIoBuffer == nullptr)
865 m_csLastError = L"Could not allocate memory";
866 goto cleanup;
870 if (m_iSecurityLevel <= ssl)
872 if (!GetResponse("220"))
873 goto cleanup;
874 m_bConnected = TRUE;
877 ret = SendEmail();
879 cleanup:
880 if (m_iSecurityLevel >= ssl)
882 if (hContext && hCreds)
883 DisconnectFromServer(&m_SendSock, hCreds, hContext);
884 if (pbIoBuffer)
886 LocalFree(pbIoBuffer);
887 pbIoBuffer = nullptr;
888 cbIoBufferLength = 0;
890 if (hContext)
892 g_pSSPI->DeleteSecurityContext(hContext);
893 hContext = nullptr;
895 if (hCreds)
897 g_pSSPI->FreeCredentialsHandle(hCreds);
898 hCreds = nullptr;
900 g_pSSPI = nullptr;
902 else
903 m_SendSock.Close();
905 return ret;
908 BOOL CHwSMTP::GetResponse(LPCSTR lpszVerifyCode)
910 if (!lpszVerifyCode || strlen(lpszVerifyCode) < 1)
911 return FALSE;
913 SECURITY_STATUS scRet = SEC_E_OK;
915 char szRecvBuf[1024] = {0};
916 int nRet = 0;
917 char szStatusCode[4] = {0};
919 if (m_iSecurityLevel >= ssl)
921 scRet = ReadDecrypt(&m_SendSock, hCreds, hContext, pbIoBuffer, cbIoBufferLength);
922 SecureZeroMemory(szRecvBuf, 1024);
923 memcpy(szRecvBuf, pbIoBuffer+Sizes.cbHeader, 1024);
925 else
926 nRet = m_SendSock.Receive(szRecvBuf, sizeof(szRecvBuf));
927 //TRACE(L"Received : %s\r\n", szRecvBuf);
928 if (nRet == 0 && m_iSecurityLevel == none || m_iSecurityLevel >= ssl && scRet != SEC_E_OK)
930 m_csLastError = L"Receive TCP data failed";
931 return FALSE;
933 memcpy ( szStatusCode, szRecvBuf, 3 );
934 if (strcmp(szStatusCode, lpszVerifyCode) != 0)
936 m_csLastError.Format(L"Received invalid response: %s", (LPCTSTR)CUnicodeUtils::GetUnicode(szRecvBuf));
937 return FALSE;
940 return TRUE;
942 BOOL CHwSMTP::SendBuffer(const char* buff, int size)
944 if(size<0)
945 size=(int)strlen(buff);
946 if ( !m_bConnected )
948 m_csLastError = L"Didn't connect";
949 return FALSE;
952 if (m_iSecurityLevel >= ssl)
954 int sent = 0;
955 while (size - sent > 0)
957 int toSend = min(size - sent, (int)Sizes.cbMaximumMessage);
958 SecureZeroMemory(pbIoBuffer + Sizes.cbHeader, Sizes.cbMaximumMessage);
959 memcpy(pbIoBuffer + Sizes.cbHeader, buff + sent, toSend);
960 DWORD cbData = EncryptSend(&m_SendSock, hContext, pbIoBuffer, Sizes);
961 if (cbData == SOCKET_ERROR || cbData == 0)
962 return FALSE;
963 sent += toSend;
966 else if (m_SendSock.Send ( buff, size ) != size)
968 m_csLastError = L"Socket send data failed";
969 return FALSE;
972 return TRUE;
975 BOOL CHwSMTP::Send(const CString &str )
977 return Send(CUnicodeUtils::GetUTF8(str));
980 BOOL CHwSMTP::Send(const CStringA &str)
982 //TRACE(L"Send: %s\r\n", (LPCTSTR)CUnicodeUtils::GetUnicode(str));
983 return SendBuffer(str, str.GetLength());
986 BOOL CHwSMTP::SendEmail()
988 CStringA hostname;
989 gethostname(CStrBufA(hostname, 64), 64);
991 // make sure helo hostname can be interpreted as a FQDN
992 if (hostname.Find(".") == -1)
993 hostname += ".local";
995 CStringA str;
996 str.Format("HELO %s\r\n", (LPCSTR)hostname);
997 if (!Send(str))
998 return FALSE;
999 if (!GetResponse("250"))
1000 return FALSE;
1002 if ( m_bMustAuth && !auth() )
1003 return FALSE;
1005 if ( !SendHead() )
1006 return FALSE;
1008 if (!SendSubject(CUnicodeUtils::GetUnicode(hostname)))
1009 return FALSE;
1011 if ( !SendBody() )
1012 return FALSE;
1014 if ( !SendAttach() )
1016 return FALSE;
1019 if (!Send(".\r\n"))
1020 return FALSE;
1021 if (!GetResponse("250"))
1022 return FALSE;
1024 if ( HANDLE_IS_VALID(m_SendSock.m_hSocket) )
1025 Send("QUIT\r\n");
1026 m_bConnected = FALSE;
1028 return TRUE;
1031 static CStringA EncodeBase64(const char* source, int len)
1033 int neededLength = Base64EncodeGetRequiredLength(len);
1034 CStringA output;
1035 if (Base64Encode((BYTE*)source, len, CStrBufA(output, neededLength), &neededLength, ATL_BASE64_FLAG_NOCRLF))
1036 output.Truncate(neededLength);
1037 return output;
1040 static CStringA EncodeBase64(const CString& source)
1042 CStringA buf = CUnicodeUtils::GetUTF8(source);
1043 return EncodeBase64(buf, buf.GetLength());
1046 CString CHwSMTP::GetEncodedHeader(const CString& text)
1048 if (CStringUtils::IsPlainReadableASCII(text))
1049 return text;
1051 return L"=?UTF-8?B?" + CUnicodeUtils::GetUnicode(EncodeBase64(text)) + L"?=";
1054 BOOL CHwSMTP::auth()
1056 if (!Send("auth login\r\n"))
1057 return FALSE;
1058 if (!GetResponse("334"))
1059 return FALSE;
1061 if (!Send(EncodeBase64(m_csUserName) + "\r\n"))
1062 return FALSE;
1064 if (!GetResponse("334"))
1066 m_csLastError = L"Authentication UserName failed";
1067 return FALSE;
1070 if (!Send(EncodeBase64(m_csPasswd) + "\r\n"))
1071 return FALSE;
1073 if (!GetResponse("235"))
1075 m_csLastError = L"Authentication Password failed";
1076 return FALSE;
1079 return TRUE;
1082 BOOL CHwSMTP::SendHead()
1084 CString str;
1085 CString addr;
1086 CStringUtils::ParseEmailAddress(m_csAddrFrom, addr);
1088 str.Format(L"MAIL From: <%s>\r\n", (LPCTSTR)addr);
1089 if (!Send(str))
1090 return FALSE;
1092 if (!GetResponse("250"))
1093 return FALSE;
1095 int start=0;
1096 while(start>=0)
1098 CString one = m_csAddrTo.Tokenize(L";", start).Trim();
1099 if(one.IsEmpty())
1100 continue;
1102 CStringUtils::ParseEmailAddress(one, addr);
1104 str.Format(L"RCPT TO: <%s>\r\n", (LPCTSTR)addr);
1105 if (!Send(str))
1106 return FALSE;
1107 if (!GetResponse("250"))
1108 return FALSE;
1111 if (!Send("DATA\r\n"))
1112 return FALSE;
1113 if (!GetResponse("354"))
1114 return FALSE;
1116 return TRUE;
1119 BOOL CHwSMTP::SendSubject(const CString &hostname)
1121 CString csSubject;
1122 csSubject += L"Date: ";
1123 COleDateTime tNow = COleDateTime::GetCurrentTime();
1124 if ( tNow > 1 )
1126 csSubject += FormatDateTime(tNow, L"%a, %d %b %y %H:%M:%S %Z");
1128 csSubject += L"\r\n";
1129 csSubject.AppendFormat(L"From: %s\r\n", (LPCTSTR)m_csAddrFrom);
1131 if (!m_StrCC.IsEmpty())
1132 csSubject.AppendFormat(L"CC: %s\r\n", (LPCTSTR)m_StrCC);
1134 if(m_csSender.IsEmpty())
1135 m_csSender = this->m_csAddrFrom;
1137 csSubject.AppendFormat(L"Sender: %s\r\n", (LPCTSTR)m_csSender);
1139 if(this->m_csToList.IsEmpty())
1140 m_csToList = m_csReceiverName;
1142 csSubject.AppendFormat(L"To: %s\r\n", (LPCTSTR)m_csToList);
1144 csSubject.AppendFormat(L"Subject: %s\r\n", (LPCTSTR)GetEncodedHeader(m_csSubject));
1146 CString m_ListID(GetGUID());
1147 if (m_ListID.IsEmpty())
1149 m_csLastError = L"Could not generate Message-ID";
1150 return FALSE;
1152 csSubject.AppendFormat(L"Message-ID: <%s@%s>\r\n", (LPCTSTR)m_ListID, (LPCTSTR)hostname);
1153 csSubject.AppendFormat(L"X-Mailer: TortoiseGit\r\nMIME-Version: 1.0\r\nContent-Type: %s\r\n\r\n", (LPCTSTR)m_csMIMEContentType);
1155 return Send(csSubject);
1158 BOOL CHwSMTP::SendBody()
1160 CString csBody;
1162 if ( m_StrAryAttach.GetSize() > 0 )
1164 csBody.AppendFormat(L"%s\r\n\r\n", (LPCTSTR)m_csNoMIMEText);
1165 csBody.AppendFormat(L"--%s\r\n", (LPCTSTR)m_csPartBoundary);
1166 csBody.AppendFormat(L"Content-Type: text/plain\r\n%sContent-Transfer-Encoding: UTF-8\r\n\r\n", m_csCharSet);
1169 m_csBody.Replace(L"\n.\n", L"\n..\n");
1170 m_csBody.Replace(L"\n.\r\n", L"\n..\r\n");
1172 csBody += m_csBody;
1173 csBody += L"\r\n";
1175 return Send(csBody);
1178 BOOL CHwSMTP::SendAttach()
1180 int nCountAttach = (int)m_StrAryAttach.GetSize();
1181 if ( nCountAttach < 1 ) return TRUE;
1183 for ( int i=0; i<nCountAttach; i++ )
1185 if ( !SendOnAttach ( m_StrAryAttach.GetAt(i) ) )
1186 return FALSE;
1189 Send(L"--" + m_csPartBoundary + L"--\r\n");
1191 return TRUE;
1194 BOOL CHwSMTP::SendOnAttach(LPCTSTR lpszFileName)
1196 ASSERT ( lpszFileName );
1197 CString csAttach;
1198 CString csShortFileName = CPathUtils::GetFileNameFromPath(lpszFileName);
1200 csAttach.AppendFormat(L"--%s\r\n", (LPCTSTR)m_csPartBoundary);
1201 csAttach.AppendFormat(L"--%s\r\n", (LPCTSTR)m_csPartBoundary);
1202 csAttach.AppendFormat(L"Content-Type: application/octet-stream; file=%s\r\n", (LPCTSTR)csShortFileName);
1203 csAttach.AppendFormat(L"Content-Transfer-Encoding: base64\r\n");
1204 csAttach.AppendFormat(L"Content-Disposition: attachment; filename=%s\r\n\r\n", (LPCTSTR)csShortFileName);
1206 DWORD dwFileSize = hwGetFileAttr(lpszFileName);
1207 if ( dwFileSize > 5*1024*1024 )
1209 m_csLastError.Format(L"File [%s] too big. File size is : %s", lpszFileName, FormatBytes(dwFileSize));
1210 return FALSE;
1212 auto pBuf = std::make_unique<char[]>(dwFileSize + 1);
1213 if (!pBuf)
1214 ::AfxThrowMemoryException();
1216 if (!Send(csAttach))
1217 return FALSE;
1219 CFile file;
1220 CStringA filedata;
1223 if ( !file.Open ( lpszFileName, CFile::modeRead ) )
1225 m_csLastError.Format(L"Open file [%s] failed", lpszFileName);
1226 return FALSE;
1228 UINT nFileLen = file.Read(pBuf.get(), dwFileSize);
1229 filedata = EncodeBase64(pBuf.get(), nFileLen);
1230 filedata += L"\r\n\r\n";
1232 catch (CFileException *e)
1234 e->Delete();
1235 m_csLastError.Format(L"Read file [%s] failed", lpszFileName);
1236 return FALSE;
1239 if (!SendBuffer(filedata))
1240 return FALSE;
1242 return TRUE;
1245 CString CHwSMTP::GetLastErrorText()
1247 return m_csLastError;
1251 CString FormatDateTime (COleDateTime &DateTime, LPCTSTR /*pFormat*/)
1253 // If null, return empty string
1254 if ( DateTime.GetStatus() == COleDateTime::null || DateTime.GetStatus() == COleDateTime::invalid )
1255 return L"";
1257 UDATE ud;
1258 if (S_OK != VarUdateFromDate(DateTime.m_dt, 0, &ud))
1260 return L"";
1263 static TCHAR* weeks[] = { L"Sun", L"Mon", L"Tue", L"Wen", L"Thu", L"Fri", L"Sat" };
1264 static TCHAR* month[] = { L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" };
1266 TIME_ZONE_INFORMATION stTimeZone;
1267 GetTimeZoneInformation(&stTimeZone);
1269 CString strDate;
1270 strDate.Format(L"%s, %d %s %d %02d:%02d:%02d %c%04d"
1271 ,weeks[ud.st.wDayOfWeek],
1272 ud.st.wDay,month[ud.st.wMonth-1],ud.st.wYear,ud.st.wHour,
1273 ud.st.wMinute,ud.st.wSecond,
1274 stTimeZone.Bias > 0 ? L'-' : L'+',
1275 abs(stTimeZone.Bias*10/6)
1277 return strDate;
1280 int hwGetFileAttr(LPCTSTR lpFileName, OUT CFileStatus* pFileStatus/*=nullptr*/)
1282 if ( !lpFileName || lstrlen(lpFileName) < 1 ) return -1;
1284 CFileStatus fileStatus;
1285 fileStatus.m_attribute = 0;
1286 fileStatus.m_size = 0;
1287 memset ( fileStatus.m_szFullName, 0, sizeof(fileStatus.m_szFullName) );
1288 BOOL bRet = FALSE;
1291 if ( CFile::GetStatus(lpFileName,fileStatus) )
1293 bRet = TRUE;
1296 CATCH (CFileException, e)
1298 ASSERT ( FALSE );
1299 bRet = FALSE;
1301 CATCH_ALL(e)
1303 ASSERT ( FALSE );
1304 bRet = FALSE;
1306 END_CATCH_ALL;
1308 if ( pFileStatus )
1310 pFileStatus->m_ctime = fileStatus.m_ctime;
1311 pFileStatus->m_mtime = fileStatus.m_mtime;
1312 pFileStatus->m_atime = fileStatus.m_atime;
1313 pFileStatus->m_size = fileStatus.m_size;
1314 pFileStatus->m_attribute = fileStatus.m_attribute;
1315 lstrcpy ( pFileStatus->m_szFullName, fileStatus.m_szFullName );
1319 return (int)fileStatus.m_size;
1322 CString FormatBytes ( double fBytesNum, BOOL bShowUnit/*=TRUE*/, int nFlag/*=0*/ )
1324 CString csRes;
1325 if ( nFlag == 0 )
1327 if ( fBytesNum >= 1024.0 && fBytesNum < 1024.0*1024.0 )
1328 csRes.Format(L"%.2f%s", fBytesNum / 1024.0, bShowUnit ? L" K" : L"");
1329 else if ( fBytesNum >= 1024.0*1024.0 && fBytesNum < 1024.0*1024.0*1024.0 )
1330 csRes.Format(L"%.2f%s", fBytesNum / (1024.0 * 1024.0), bShowUnit ? L" M" : L"");
1331 else if ( fBytesNum >= 1024.0*1024.0*1024.0 )
1332 csRes.Format(L"%.2f%s", fBytesNum / (1024.0 * 1024.0 * 1024.0), bShowUnit?L" G":L"");
1333 else
1334 csRes.Format(L"%.2f%s", fBytesNum, bShowUnit ? L" B" : L"");
1336 else if ( nFlag == 1 )
1337 csRes.Format(L"%.2f%s", fBytesNum / 1024.0, bShowUnit ? L" K" : L"");
1338 else if ( nFlag == 2 )
1339 csRes.Format(L"%.2f%s", fBytesNum / (1024.0 * 1024.0), bShowUnit ? L" M" : L"");
1340 else if ( nFlag == 3 )
1341 csRes.Format(L"%.2f%s", fBytesNum / (1024.0 * 1024.0 * 1024.0), bShowUnit ? L" G" : L"");
1343 return csRes;