1 // HwSMTP.cpp: implementation of the CHwSMTP class.
3 // Schannel/SSPI implementation based on http://www.coastrd.com/c-schannel-smtp
5 //////////////////////////////////////////////////////////////////////
10 #include "SpeedPostEmail.h"
13 #include "FormatMessageWrapper.h"
17 #define IO_BUFFER_SIZE 0x10000
19 #pragma comment(lib, "Secur32.lib")
21 DWORD dwProtocol
= SP_PROT_TLS1
; // SP_PROT_TLS1; // SP_PROT_PCT1; SP_PROT_SSL2; SP_PROT_SSL3; 0=default
22 ALG_ID aiKeyExch
= 0; // = default; CALG_DH_EPHEM; CALG_RSA_KEYX;
24 SCHANNEL_CRED SchannelCred
;
25 PSecurityFunctionTable g_pSSPI
;
27 //////////////////////////////////////////////////////////////////////
28 // Construction/Destruction
29 //////////////////////////////////////////////////////////////////////
32 m_bConnected ( FALSE
),
33 m_nSmtpSrvPort ( 25 ),
36 m_csPartBoundary
= _T( "WC_MAIL_PaRt_BoUnDaRy_05151998" );
37 m_csMIMEContentType
= FormatString ( _T( "multipart/mixed; boundary=%s" ), m_csPartBoundary
);
38 m_csNoMIMEText
= _T( "This is a multi-part message in MIME format." );
39 //m_csCharSet = _T("\r\n\tcharset=\"iso-8859-1\"\r\n");
46 m_iSecurityLevel
= none
;
48 SecureZeroMemory(&Sizes
, sizeof(SecPkgContext_StreamSizes
));
57 void CHwSMTP::GetNameAddress(CString
&in
, CString
&name
,CString
&address
)
60 start
=in
.Find(_T('<'));
63 if(start
>=0 && end
>=0)
66 address
=in
.Mid(start
+1,end
-start
-1);
72 CString
CHwSMTP::GetServerAddress(CString
&email
)
77 start
= email
.Find(_T("<"));
78 end
= email
.Find(_T(">"));
80 if(start
>=0 && end
>=0)
82 str
=email
.Mid(start
+1,end
-start
-1);
89 start
= str
.Find(_T('@'));
90 return str
.Mid(start
+1);
94 BOOL
CHwSMTP::SendSpeedEmail
101 CStringArray
*pStrAryAttach
,
109 To
+= GET_SAFE_STRING(lpszAddrTo
);
111 To
+= GET_SAFE_STRING(pStrAryCC
);
113 std::map
<CString
,std::vector
<CString
>> Address
;
118 CString one
= To
.Tokenize(_T(";"),start
);
124 addr
= GetServerAddress(one
);
128 Address
[addr
].push_back(one
);
132 std::map
<CString
,std::vector
<CString
>>::iterator itr1
= Address
.begin();
133 for( ; itr1
!= Address
.end(); ++itr1
)
135 PDNS_RECORD pDnsRecord
;
139 DnsQuery(itr1
->first
,
140 DNS_TYPE_MX
,DNS_QUERY_STANDARD
,
141 NULL
, //Contains DNS server IP address.
142 &pDnsRecord
, //Resource record that contains the response.
147 m_csLastError
.Format(_T("DNS query failed %d"), status
);
154 for (size_t i
= 0; i
< itr1
->second
.size(); ++i
)
165 if(pNext
->wType
== DNS_TYPE_MX
)
166 if(SendEmail(pNext
->Data
.MX
.pNameExchange
,NULL
,NULL
,false,
167 lpszAddrFrom
,to
,lpszSubject
,lpszBody
,lpszCharSet
,pStrAryAttach
,pStrAryCC
,
168 25,pSend
,lpszAddrTo
))
176 DnsRecordListFree(pDnsRecord
,DnsFreeRecordList
);
182 static SECURITY_STATUS
ClientHandshakeLoop(CSocket
* Socket
, PCredHandle phCreds
, CtxtHandle
* phContext
, BOOL fDoInitialRead
, SecBuffer
* pExtraData
)
184 SecBufferDesc OutBuffer
, InBuffer
;
185 SecBuffer InBuffers
[2], OutBuffers
[1];
186 DWORD dwSSPIFlags
, dwSSPIOutFlags
, cbData
, cbIoBuffer
;
188 SECURITY_STATUS scRet
;
192 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
| ISC_REQ_REPLAY_DETECT
| ISC_REQ_CONFIDENTIALITY
|
193 ISC_RET_EXTENDED_ERROR
| ISC_REQ_ALLOCATE_MEMORY
| ISC_REQ_STREAM
;
195 // Allocate data buffer.
196 IoBuffer
= new UCHAR
[IO_BUFFER_SIZE
];
197 if (IoBuffer
== nullptr)
199 // printf("**** Out of memory (1)\n");
200 return SEC_E_INTERNAL_ERROR
;
203 fDoRead
= fDoInitialRead
;
205 // Loop until the handshake is finished or an error occurs.
206 scRet
= SEC_I_CONTINUE_NEEDED
;
208 while (scRet
== SEC_I_CONTINUE_NEEDED
|| scRet
== SEC_E_INCOMPLETE_MESSAGE
|| scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
210 if (0 == cbIoBuffer
|| scRet
== SEC_E_INCOMPLETE_MESSAGE
) // Read data from server.
214 cbData
= Socket
->Receive(IoBuffer
+ cbIoBuffer
, IO_BUFFER_SIZE
- cbIoBuffer
, 0);
215 if (cbData
== SOCKET_ERROR
)
217 // printf("**** Error %d reading data from server\n", WSAGetLastError());
218 scRet
= SEC_E_INTERNAL_ERROR
;
221 else if (cbData
== 0)
223 // printf("**** Server unexpectedly disconnected\n");
224 scRet
= SEC_E_INTERNAL_ERROR
;
227 // printf("%d bytes of handshake data received\n", cbData);
228 cbIoBuffer
+= cbData
;
234 // Set up the input buffers. Buffer 0 is used to pass in data
235 // received from the server. Schannel will consume some or all
236 // of this. Leftover data (if any) will be placed in buffer 1 and
237 // given a buffer type of SECBUFFER_EXTRA.
238 InBuffers
[0].pvBuffer
= IoBuffer
;
239 InBuffers
[0].cbBuffer
= cbIoBuffer
;
240 InBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
242 InBuffers
[1].pvBuffer
= nullptr;
243 InBuffers
[1].cbBuffer
= 0;
244 InBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
246 InBuffer
.cBuffers
= 2;
247 InBuffer
.pBuffers
= InBuffers
;
248 InBuffer
.ulVersion
= SECBUFFER_VERSION
;
250 // Set up the output buffers. These are initialized to NULL
251 // so as to make it less likely we'll attempt to free random
253 OutBuffers
[0].pvBuffer
= nullptr;
254 OutBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
255 OutBuffers
[0].cbBuffer
= 0;
257 OutBuffer
.cBuffers
= 1;
258 OutBuffer
.pBuffers
= OutBuffers
;
259 OutBuffer
.ulVersion
= SECBUFFER_VERSION
;
261 // Call InitializeSecurityContext.
262 scRet
= g_pSSPI
->InitializeSecurityContext(phCreds
, phContext
, nullptr, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, &InBuffer
, 0, nullptr, &OutBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
264 // If InitializeSecurityContext was successful (or if the error was
265 // one of the special extended ones), send the contends of the output
266 // buffer to the server.
267 if (scRet
== SEC_E_OK
|| scRet
== SEC_I_CONTINUE_NEEDED
|| FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
269 if (OutBuffers
[0].cbBuffer
!= 0 && OutBuffers
[0].pvBuffer
!= nullptr)
271 cbData
= Socket
->Send(OutBuffers
[0].pvBuffer
, OutBuffers
[0].cbBuffer
, 0 );
272 if(cbData
== SOCKET_ERROR
|| cbData
== 0)
274 // printf( "**** Error %d sending data to server (2)\n", WSAGetLastError() );
275 g_pSSPI
->FreeContextBuffer(OutBuffers
[0].pvBuffer
);
276 g_pSSPI
->DeleteSecurityContext(phContext
);
277 return SEC_E_INTERNAL_ERROR
;
279 // printf("%d bytes of handshake data sent\n", cbData);
281 // Free output buffer.
282 g_pSSPI
->FreeContextBuffer(OutBuffers
[0].pvBuffer
);
283 OutBuffers
[0].pvBuffer
= nullptr;
287 // If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
288 // then we need to read more data from the server and try again.
289 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
) continue;
291 // If InitializeSecurityContext returned SEC_E_OK, then the
292 // handshake completed successfully.
293 if (scRet
== SEC_E_OK
)
295 // If the "extra" buffer contains data, this is encrypted application
296 // protocol layer stuff. It needs to be saved. The application layer
297 // will later decrypt it with DecryptMessage.
298 // printf("Handshake was successful\n");
300 if (InBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
302 pExtraData
->pvBuffer
= LocalAlloc( LMEM_FIXED
, InBuffers
[1].cbBuffer
);
303 if (pExtraData
->pvBuffer
== nullptr)
305 // printf("**** Out of memory (2)\n");
306 return SEC_E_INTERNAL_ERROR
;
309 MoveMemory(pExtraData
->pvBuffer
, IoBuffer
+ (cbIoBuffer
- InBuffers
[1].cbBuffer
), InBuffers
[1].cbBuffer
);
311 pExtraData
->cbBuffer
= InBuffers
[1].cbBuffer
;
312 pExtraData
->BufferType
= SECBUFFER_TOKEN
;
314 // printf( "%d bytes of app data was bundled with handshake data\n", pExtraData->cbBuffer );
318 pExtraData
->pvBuffer
= nullptr;
319 pExtraData
->cbBuffer
= 0;
320 pExtraData
->BufferType
= SECBUFFER_EMPTY
;
322 break; // Bail out to quit
325 // Check for fatal error.
328 // printf("**** Error 0x%x returned by InitializeSecurityContext (2)\n", scRet);
332 // If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
333 // then the server just requested client authentication.
334 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
336 // Busted. The server has requested client authentication and
337 // the credential we supplied didn't contain a client certificate.
338 // This function will read the list of trusted certificate
339 // authorities ("issuers") that was received from the server
340 // and attempt to find a suitable client certificate that
341 // was issued by one of these. If this function is successful,
342 // then we will connect using the new certificate. Otherwise,
343 // we will attempt to connect anonymously (using our current credentials).
344 //GetNewClientCredentials(phCreds, phContext);
348 scRet
= SEC_I_CONTINUE_NEEDED
;
352 // Copy any leftover data from the "extra" buffer, and go around again.
353 if ( InBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
355 MoveMemory(IoBuffer
, IoBuffer
+ (cbIoBuffer
- InBuffers
[1].cbBuffer
), InBuffers
[1].cbBuffer
);
356 cbIoBuffer
= InBuffers
[1].cbBuffer
;
362 // Delete the security context in the case of a fatal error.
364 g_pSSPI
->DeleteSecurityContext(phContext
);
370 static SECURITY_STATUS
PerformClientHandshake( CSocket
* Socket
, PCredHandle phCreds
, LPTSTR pszServerName
, CtxtHandle
* phContext
, SecBuffer
* pExtraData
)
372 SecBufferDesc OutBuffer
;
373 SecBuffer OutBuffers
[1];
374 DWORD dwSSPIFlags
, dwSSPIOutFlags
, cbData
;
376 SECURITY_STATUS scRet
;
378 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
| ISC_REQ_REPLAY_DETECT
| ISC_REQ_CONFIDENTIALITY
|
379 ISC_RET_EXTENDED_ERROR
| ISC_REQ_ALLOCATE_MEMORY
| ISC_REQ_STREAM
;
381 // Initiate a ClientHello message and generate a token.
382 OutBuffers
[0].pvBuffer
= nullptr;
383 OutBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
384 OutBuffers
[0].cbBuffer
= 0;
386 OutBuffer
.cBuffers
= 1;
387 OutBuffer
.pBuffers
= OutBuffers
;
388 OutBuffer
.ulVersion
= SECBUFFER_VERSION
;
390 scRet
= g_pSSPI
->InitializeSecurityContext(phCreds
, nullptr, pszServerName
, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, nullptr, 0, phContext
, &OutBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
392 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
394 // printf("**** Error %d returned by InitializeSecurityContext (1)\n", scRet);
398 // Send response to server if there is one.
399 if (OutBuffers
[0].cbBuffer
!= 0 && OutBuffers
[0].pvBuffer
!= nullptr)
401 cbData
= Socket
->Send(OutBuffers
[0].pvBuffer
, OutBuffers
[0].cbBuffer
, 0);
402 if (cbData
== SOCKET_ERROR
|| cbData
== 0)
404 // printf("**** Error %d sending data to server (1)\n", WSAGetLastError());
405 g_pSSPI
->FreeContextBuffer(OutBuffers
[0].pvBuffer
);
406 g_pSSPI
->DeleteSecurityContext(phContext
);
407 return SEC_E_INTERNAL_ERROR
;
409 // printf("%d bytes of handshake data sent\n", cbData);
411 g_pSSPI
->FreeContextBuffer(OutBuffers
[0].pvBuffer
); // Free output buffer.
412 OutBuffers
[0].pvBuffer
= nullptr;
415 return ClientHandshakeLoop(Socket
, phCreds
, phContext
, TRUE
, pExtraData
);
418 static SECURITY_STATUS
CreateCredentials(PCredHandle phCreds
)
421 SECURITY_STATUS Status
;
422 DWORD cSupportedAlgs
= 0;
423 ALG_ID rgbSupportedAlgs
[16];
425 // Build Schannel credential structure. Currently, this sample only
426 // specifies the protocol to be used (and optionally the certificate,
427 // of course). Real applications may wish to specify other parameters as well.
428 SecureZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
430 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
431 SchannelCred
.grbitEnabledProtocols
= dwProtocol
;
434 rgbSupportedAlgs
[cSupportedAlgs
++] = aiKeyExch
;
438 SchannelCred
.cSupportedAlgs
= cSupportedAlgs
;
439 SchannelCred
.palgSupportedAlgs
= rgbSupportedAlgs
;
442 SchannelCred
.dwFlags
|= SCH_CRED_NO_DEFAULT_CREDS
;
444 // The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because
445 // this sample verifies the server certificate manually.
446 // Applications that expect to run on WinNT, Win9x, or WinME
447 // should specify this flag and also manually verify the server
448 // certificate. Applications running on newer versions of Windows can
449 // leave off this flag, in which case the InitializeSecurityContext
450 // function will validate the server certificate automatically.
451 SchannelCred
.dwFlags
|= SCH_CRED_MANUAL_CRED_VALIDATION
;
453 // Create an SSPI credential.
454 Status
= g_pSSPI
->AcquireCredentialsHandle(nullptr, // Name of principal
455 UNISP_NAME
, // Name of package
456 SECPKG_CRED_OUTBOUND
, // Flags indicating use
457 nullptr, // Pointer to logon ID
458 &SchannelCred
, // Package specific data
459 nullptr, // Pointer to GetKey() func
460 nullptr, // Value to pass to GetKey()
461 phCreds
, // (out) Cred Handle
462 &tsExpiry
); // (out) Lifetime (optional)
467 static DWORD
EncryptSend(CSocket
* Socket
, CtxtHandle
* phContext
, PBYTE pbIoBuffer
, SecPkgContext_StreamSizes Sizes
)
468 // http://msdn.microsoft.com/en-us/library/aa375378(VS.85).aspx
469 // The encrypted message is encrypted in place, overwriting the original contents of its buffer.
471 SECURITY_STATUS scRet
;
472 SecBufferDesc Message
;
473 SecBuffer Buffers
[4];
477 pbMessage
= pbIoBuffer
+ Sizes
.cbHeader
; // Offset by "header size"
478 cbMessage
= (DWORD
)strlen((char *)pbMessage
);
480 // Encrypt the HTTP request.
481 Buffers
[0].pvBuffer
= pbIoBuffer
; // Pointer to buffer 1
482 Buffers
[0].cbBuffer
= Sizes
.cbHeader
; // length of header
483 Buffers
[0].BufferType
= SECBUFFER_STREAM_HEADER
; // Type of the buffer
485 Buffers
[1].pvBuffer
= pbMessage
; // Pointer to buffer 2
486 Buffers
[1].cbBuffer
= cbMessage
; // length of the message
487 Buffers
[1].BufferType
= SECBUFFER_DATA
; // Type of the buffer
489 Buffers
[2].pvBuffer
= pbMessage
+ cbMessage
; // Pointer to buffer 3
490 Buffers
[2].cbBuffer
= Sizes
.cbTrailer
; // length of the trailor
491 Buffers
[2].BufferType
= SECBUFFER_STREAM_TRAILER
; // Type of the buffer
493 Buffers
[3].pvBuffer
= SECBUFFER_EMPTY
; // Pointer to buffer 4
494 Buffers
[3].cbBuffer
= SECBUFFER_EMPTY
; // length of buffer 4
495 Buffers
[3].BufferType
= SECBUFFER_EMPTY
; // Type of the buffer 4
497 Message
.ulVersion
= SECBUFFER_VERSION
; // Version number
498 Message
.cBuffers
= 4; // Number of buffers - must contain four SecBuffer structures.
499 Message
.pBuffers
= Buffers
; // Pointer to array of buffers
501 scRet
= g_pSSPI
->EncryptMessage(phContext
, 0, &Message
, 0); // must contain four SecBuffer structures.
504 // printf("**** Error 0x%x returned by EncryptMessage\n", scRet);
509 // Send the encrypted data to the server.
510 return Socket
->Send(pbIoBuffer
, Buffers
[0].cbBuffer
+ Buffers
[1].cbBuffer
+ Buffers
[2].cbBuffer
, 0);
513 static LONG
DisconnectFromServer(CSocket
* Socket
, PCredHandle phCreds
, CtxtHandle
* phContext
)
516 DWORD dwType
, dwSSPIFlags
, dwSSPIOutFlags
, cbMessage
, cbData
, Status
;
517 SecBufferDesc OutBuffer
;
518 SecBuffer OutBuffers
[1];
521 dwType
= SCHANNEL_SHUTDOWN
; // Notify schannel that we are about to close the connection.
523 OutBuffers
[0].pvBuffer
= &dwType
;
524 OutBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
525 OutBuffers
[0].cbBuffer
= sizeof(dwType
);
527 OutBuffer
.cBuffers
= 1;
528 OutBuffer
.pBuffers
= OutBuffers
;
529 OutBuffer
.ulVersion
= SECBUFFER_VERSION
;
531 Status
= g_pSSPI
->ApplyControlToken(phContext
, &OutBuffer
);
534 // printf("**** Error 0x%x returned by ApplyControlToken\n", Status);
538 // Build an SSL close notify message.
539 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
| ISC_REQ_REPLAY_DETECT
| ISC_REQ_CONFIDENTIALITY
| ISC_RET_EXTENDED_ERROR
| ISC_REQ_ALLOCATE_MEMORY
| ISC_REQ_STREAM
;
541 OutBuffers
[0].pvBuffer
= nullptr;
542 OutBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
543 OutBuffers
[0].cbBuffer
= 0;
545 OutBuffer
.cBuffers
= 1;
546 OutBuffer
.pBuffers
= OutBuffers
;
547 OutBuffer
.ulVersion
= SECBUFFER_VERSION
;
549 Status
= g_pSSPI
->InitializeSecurityContext(phCreds
, phContext
, nullptr, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, nullptr, 0, phContext
, &OutBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
553 // printf("**** Error 0x%x returned by InitializeSecurityContext\n", Status);
557 pbMessage
= (PBYTE
)OutBuffers
[0].pvBuffer
;
558 cbMessage
= OutBuffers
[0].cbBuffer
;
560 // Send the close notify message to the server.
561 if (pbMessage
!= nullptr && cbMessage
!= 0)
563 cbData
= Socket
->Send(pbMessage
, cbMessage
, 0);
564 if (cbData
== SOCKET_ERROR
|| cbData
== 0)
566 Status
= WSAGetLastError();
569 // printf("Sending Close Notify\n");
570 // printf("%d bytes of handshake data sent\n", cbData);
571 g_pSSPI
->FreeContextBuffer(pbMessage
); // Free output buffer.
575 g_pSSPI
->DeleteSecurityContext(phContext
); // Free the security context.
581 static SECURITY_STATUS
ReadDecrypt(CSocket
* Socket
, PCredHandle phCreds
, CtxtHandle
* phContext
, PBYTE pbIoBuffer
, DWORD cbIoBufferLength
)
583 // calls recv() - blocking socket read
584 // http://msdn.microsoft.com/en-us/library/ms740121(VS.85).aspx
586 // The encrypted message is decrypted in place, overwriting the original contents of its buffer.
587 // http://msdn.microsoft.com/en-us/library/aa375211(VS.85).aspx
590 SecBuffer ExtraBuffer
;
591 SecBuffer
* pDataBuffer
, * pExtraBuffer
;
593 SECURITY_STATUS scRet
;
594 SecBufferDesc Message
;
595 SecBuffer Buffers
[4];
597 DWORD cbIoBuffer
, cbData
, length
;
600 // Read data from server until done.
603 while (TRUE
) // Read some data.
605 if (cbIoBuffer
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
) // get the data
607 cbData
= Socket
->Receive(pbIoBuffer
+ cbIoBuffer
, cbIoBufferLength
- cbIoBuffer
, 0);
608 if (cbData
== SOCKET_ERROR
)
610 // printf("**** Error %d reading data from server\n", WSAGetLastError());
611 scRet
= SEC_E_INTERNAL_ERROR
;
614 else if (cbData
== 0) // Server disconnected.
618 // printf("**** Server unexpectedly disconnected\n");
619 scRet
= SEC_E_INTERNAL_ERROR
;
627 // printf("%d bytes of (encrypted) application data received\n", cbData);
628 cbIoBuffer
+= cbData
;
632 // Decrypt the received data.
633 Buffers
[0].pvBuffer
= pbIoBuffer
;
634 Buffers
[0].cbBuffer
= cbIoBuffer
;
635 Buffers
[0].BufferType
= SECBUFFER_DATA
; // Initial Type of the buffer 1
636 Buffers
[1].BufferType
= SECBUFFER_EMPTY
; // Initial Type of the buffer 2
637 Buffers
[2].BufferType
= SECBUFFER_EMPTY
; // Initial Type of the buffer 3
638 Buffers
[3].BufferType
= SECBUFFER_EMPTY
; // Initial Type of the buffer 4
640 Message
.ulVersion
= SECBUFFER_VERSION
; // Version number
641 Message
.cBuffers
= 4; // Number of buffers - must contain four SecBuffer structures.
642 Message
.pBuffers
= Buffers
; // Pointer to array of buffers
644 scRet
= g_pSSPI
->DecryptMessage(phContext
, &Message
, 0, nullptr);
645 if (scRet
== SEC_I_CONTEXT_EXPIRED
)
646 break; // Server signalled end of session
647 // if (scRet == SEC_E_INCOMPLETE_MESSAGE - Input buffer has partial encrypted record, read more
648 if (scRet
!= SEC_E_OK
&& scRet
!= SEC_I_RENEGOTIATE
&& scRet
!= SEC_I_CONTEXT_EXPIRED
)
651 // Locate data and (optional) extra buffers.
652 pDataBuffer
= nullptr;
653 pExtraBuffer
= nullptr;
654 for (int i
= 1; i
< 4; ++i
)
656 if (pDataBuffer
== nullptr && Buffers
[i
].BufferType
== SECBUFFER_DATA
)
657 pDataBuffer
= &Buffers
[i
];
658 if (pExtraBuffer
== nullptr && Buffers
[i
].BufferType
== SECBUFFER_EXTRA
)
659 pExtraBuffer
= &Buffers
[i
];
662 // Display the decrypted data.
665 length
= pDataBuffer
->cbBuffer
;
666 if (length
) // check if last two chars are CR LF
668 buff
= (PBYTE
)pDataBuffer
->pvBuffer
;
669 if (buff
[length
-2] == 13 && buff
[length
-1] == 10) // Found CRLF
677 // Move any "extra" data to the input buffer.
680 MoveMemory(pbIoBuffer
, pExtraBuffer
->pvBuffer
, pExtraBuffer
->cbBuffer
);
681 cbIoBuffer
= pExtraBuffer
->cbBuffer
;
686 // The server wants to perform another handshake sequence.
687 if (scRet
== SEC_I_RENEGOTIATE
)
689 // printf("Server requested renegotiate!\n");
690 scRet
= ClientHandshakeLoop( Socket
, phCreds
, phContext
, FALSE
, &ExtraBuffer
);
691 if (scRet
!= SEC_E_OK
)
694 if (ExtraBuffer
.pvBuffer
) // Move any "extra" data to the input buffer.
696 MoveMemory(pbIoBuffer
, ExtraBuffer
.pvBuffer
, ExtraBuffer
.cbBuffer
);
697 cbIoBuffer
= ExtraBuffer
.cbBuffer
;
700 } // Loop till CRLF is found at the end of the data
705 BOOL
CHwSMTP::SendEmail (
706 LPCTSTR lpszSmtpSrvHost
,
707 LPCTSTR lpszUserName
,
710 LPCTSTR lpszAddrFrom
,
714 LPCTSTR lpszCharSet
, // ×Ö·û¼¯ÀàÐÍ£¬ÀýÈ磺·±ÌåÖÐÎÄÕâÀïÓ¦ÊäÈë"big5"£¬¼òÌåÖÐÎÄʱÊäÈë"gb2312"
715 CStringArray
*pStrAryAttach
/*=NULL*/,
716 LPCTSTR pStrAryCC
/*=NULL*/,
717 UINT nSmtpSrvPort
,/*=25*/
723 TRACE ( _T("·¢ËÍÓʼþ£º%s, %s\n"), lpszAddrTo
, lpszBody
);
724 m_StrAryAttach
.RemoveAll();
726 m_StrCC
+= GET_SAFE_STRING(pStrAryCC
);
728 m_csSmtpSrvHost
= GET_SAFE_STRING ( lpszSmtpSrvHost
);
729 if ( m_csSmtpSrvHost
.GetLength() <= 0 )
731 m_csLastError
.Format ( _T("Parameter Error!") );
734 m_csUserName
= GET_SAFE_STRING ( lpszUserName
);
735 m_csPasswd
= GET_SAFE_STRING ( lpszPasswd
);
736 m_bMustAuth
= bMustAuth
;
737 if ( m_bMustAuth
&& m_csUserName
.GetLength() <= 0 )
739 m_csLastError
.Format ( _T("Parameter Error!") );
743 m_csAddrFrom
= GET_SAFE_STRING ( lpszAddrFrom
);
744 m_csAddrTo
= GET_SAFE_STRING ( lpszAddrTo
);
745 // m_csFromName = GET_SAFE_STRING ( lpszFromName );
746 // m_csReceiverName = GET_SAFE_STRING ( lpszReceiverName );
747 m_csSubject
= GET_SAFE_STRING ( lpszSubject
);
748 m_csBody
= GET_SAFE_STRING ( lpszBody
);
750 this->m_csSender
= GET_SAFE_STRING(pSender
);
751 this->m_csToList
= GET_SAFE_STRING(pToList
);
753 m_nSmtpSrvPort
= nSmtpSrvPort
;
755 if ( lpszCharSet
&& lstrlen(lpszCharSet
) > 0 )
756 m_csCharSet
.Format ( _T("\r\n\tcharset=\"%s\"\r\n"), lpszCharSet
);
759 m_csAddrFrom
.GetLength() <= 0 || m_csAddrTo
.GetLength() <= 0
762 m_csLastError
.Format ( _T("Parameter Error!") );
768 m_StrAryAttach
.Append ( *pStrAryAttach
);
770 if ( m_StrAryAttach
.GetSize() < 1 )
771 m_csMIMEContentType
= FormatString ( _T( "text/plain; %s" ), m_csCharSet
);
775 if ( !m_SendSock
.Create () )
777 //int nResult = GetLastError();
778 m_csLastError
.Format ( _T("Create socket failed!") );
785 m_iSecurityLevel
= want_tls
;
788 m_iSecurityLevel
= ssl
;
791 m_iSecurityLevel
= none
;
795 if ( !m_SendSock
.Connect ( m_csSmtpSrvHost
, m_nSmtpSrvPort
) )
797 m_csLastError
.Format ( _T("Connect to [ %s ] failed"), m_csSmtpSrvHost
);
798 TRACE ( _T("%d\n"), GetLastError() );
802 if (m_iSecurityLevel
== want_tls
) {
803 if (!GetResponse(_T("220")))
807 if (!GetResponse(_T("220")))
809 m_iSecurityLevel
= tls_established
;
815 SECURITY_STATUS Status
;
817 CtxtHandle contextStruct
;
818 CredHandle credentialsStruct
;
820 if (m_iSecurityLevel
>= ssl
)
822 g_pSSPI
= InitSecurityInterface();
824 contextStruct
.dwLower
= 0;
825 contextStruct
.dwUpper
= 0;
827 hCreds
= &credentialsStruct
;
828 credentialsStruct
.dwLower
= 0;
829 credentialsStruct
.dwUpper
= 0;
830 Status
= CreateCredentials(hCreds
);
831 if (Status
!= SEC_E_OK
)
833 m_csLastError
= CFormatMessageWrapper(Status
);
837 hContext
= &contextStruct
;
838 Status
= PerformClientHandshake(&m_SendSock
, hCreds
, m_csSmtpSrvHost
.GetBuffer(), hContext
, &ExtraData
);
839 if (Status
!= SEC_E_OK
)
841 m_csLastError
= CFormatMessageWrapper(Status
);
845 PCCERT_CONTEXT pRemoteCertContext
= nullptr;
846 // Authenticate server's credentials. Get server's certificate.
847 Status
= g_pSSPI
->QueryContextAttributes(hContext
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (PVOID
)&pRemoteCertContext
);
850 m_csLastError
= CFormatMessageWrapper(Status
);
855 cert
.cert_type
= GIT_CERT_X509
;
856 cert
.data
= pRemoteCertContext
->pbCertEncoded
;
857 cert
.len
= pRemoteCertContext
->cbCertEncoded
;
858 if (CAppUtils::Git2CertificateCheck((git_cert
*)&cert
, 0, CUnicodeUtils::GetUTF8(m_csSmtpSrvHost
), nullptr))
860 CertFreeCertificateContext(pRemoteCertContext
);
861 m_csLastError
= _T("Invalid certificate.");
865 CertFreeCertificateContext(pRemoteCertContext
);
867 Status
= g_pSSPI
->QueryContextAttributes(hContext
, SECPKG_ATTR_STREAM_SIZES
, &Sizes
);
870 m_csLastError
= CFormatMessageWrapper(Status
);
875 cbIoBufferLength
= Sizes
.cbHeader
+ Sizes
.cbMaximumMessage
+ Sizes
.cbTrailer
;
876 pbIoBuffer
= (PBYTE
)LocalAlloc(LMEM_FIXED
, cbIoBufferLength
);
877 SecureZeroMemory(pbIoBuffer
, cbIoBufferLength
);
878 if (pbIoBuffer
== nullptr)
880 m_csLastError
= _T("Could not allocate memory");
885 if (m_iSecurityLevel
<= ssl
)
887 if (!GetResponse(_T("220")))
895 if (m_iSecurityLevel
>= ssl
)
897 if (hContext
&& hCreds
)
898 DisconnectFromServer(&m_SendSock
, hCreds
, hContext
);
901 LocalFree(pbIoBuffer
);
902 pbIoBuffer
= nullptr;
903 cbIoBufferLength
= 0;
907 g_pSSPI
->DeleteSecurityContext(hContext
);
912 g_pSSPI
->FreeCredentialsHandle(hCreds
);
923 BOOL
CHwSMTP::GetResponse ( LPCTSTR lpszVerifyCode
, int *pnCode
/*=NULL*/)
925 if ( !lpszVerifyCode
|| lstrlen(lpszVerifyCode
) < 1 )
928 SECURITY_STATUS scRet
= SEC_E_OK
;
930 char szRecvBuf
[1024] = {0};
932 char szStatusCode
[4] = {0};
934 if (m_iSecurityLevel
>= ssl
)
936 scRet
= ReadDecrypt(&m_SendSock
, hCreds
, hContext
, pbIoBuffer
, cbIoBufferLength
);
937 SecureZeroMemory(szRecvBuf
, 1024);
938 memcpy(szRecvBuf
, pbIoBuffer
+Sizes
.cbHeader
, 1024);
941 nRet
= m_SendSock
.Receive(szRecvBuf
, sizeof(szRecvBuf
));
942 TRACE ( _T("Received : %s\r\n"), szRecvBuf
);
943 if (nRet
== 0 && m_iSecurityLevel
== none
|| m_iSecurityLevel
>= ssl
&& scRet
!= SEC_E_OK
)
945 m_csLastError
.Format ( _T("Receive TCP data failed") );
948 // TRACE ( _T("ÊÕµ½·þÎñÆ÷»ØÓ¦£º%s\n"), szRecvBuf );
950 memcpy ( szStatusCode
, szRecvBuf
, 3 );
951 if ( pnCode
) (*pnCode
) = atoi ( szStatusCode
);
953 if ( strcmp ( szStatusCode
, CMultiByteString(lpszVerifyCode
).GetBuffer() ) != 0 )
955 m_csLastError
.Format ( _T("Received invalid response : %s"), GetCompatibleString(szRecvBuf
,FALSE
) );
961 BOOL
CHwSMTP::SendBuffer(char *buff
,int size
)
964 size
=(int)strlen(buff
);
967 m_csLastError
.Format ( _T("Didn't connect") );
971 if (m_iSecurityLevel
>= ssl
)
974 while (size
- sent
> 0)
976 int toSend
= min(size
- sent
, (int)Sizes
.cbMaximumMessage
);
977 SecureZeroMemory(pbIoBuffer
+ Sizes
.cbHeader
, Sizes
.cbMaximumMessage
);
978 memcpy(pbIoBuffer
+ Sizes
.cbHeader
, buff
+ sent
, toSend
);
979 DWORD cbData
= EncryptSend(&m_SendSock
, hContext
, pbIoBuffer
, Sizes
);
980 if (cbData
== SOCKET_ERROR
|| cbData
== 0)
985 else if (m_SendSock
.Send ( buff
, size
) != size
)
987 m_csLastError
.Format ( _T("Socket send data failed") );
993 // ÀûÓÃsocket·¢ËÍÊý¾Ý£¬Êý¾Ý³¤¶È²»Äܳ¬¹ý10M
994 BOOL
CHwSMTP::Send(const CString
&str
)
996 CMultiByteString
cbsData ( str
);
998 TRACE ( _T("Send : %s\r\n"), cbsData
.GetBuffer() );
999 return SendBuffer(cbsData
.GetBuffer(), cbsData
.GetLength());
1002 BOOL
CHwSMTP::SendEmail()
1005 char szLocalHostName
[64] = {0};
1006 gethostname ( (char*)szLocalHostName
, sizeof(szLocalHostName
) );
1008 // make sure helo hostname can be interpreted as a FQDN
1009 CString
hostname(GetCompatibleString(szLocalHostName
, FALSE
));
1010 if (hostname
.Find(_T(".")) == -1)
1011 hostname
+= _T(".local");
1015 str
.Format(_T("HELO %s\r\n"), hostname
);
1020 if ( !GetResponse ( _T("250") ) )
1025 if ( m_bMustAuth
&& !auth() )
1035 if (!SendSubject(hostname
))
1045 if ( !SendAttach() )
1050 if ( !Send ( CString(_T(".\r\n") ) ) ) return FALSE
;
1051 if ( !GetResponse ( _T("250") ) )
1055 if ( HANDLE_IS_VALID(m_SendSock
.m_hSocket
) )
1056 Send ( CString(_T("QUIT\r\n")) );
1057 m_bConnected
= FALSE
;
1062 static CStringA
EncodeBase64(const char * source
, int len
)
1064 int neededLength
= Base64EncodeGetRequiredLength(len
);
1066 if (!Base64Encode((BYTE
*)source
, len
, output
.GetBufferSetLength(neededLength
), &neededLength
))
1068 output
.ReleaseBuffer(0);
1071 output
.ReleaseBuffer(neededLength
);
1075 BOOL
CHwSMTP::auth()
1077 int nResponseCode
= 0;
1078 if ( !Send ( CString(_T("auth login\r\n")) ) ) return FALSE
;
1079 if ( !GetResponse ( _T("334"), &nResponseCode
) ) return FALSE
;
1080 if ( nResponseCode
!= 334 ) // ²»ÐèÒªÑéÖ¤Óû§ÃûºÍÃÜÂë
1083 CMultiByteString
cbsUserName ( m_csUserName
), cbsPasswd ( m_csPasswd
);
1084 CString csBase64_UserName
= GetCompatibleString(EncodeBase64(cbsUserName
.GetBuffer(), cbsUserName
.GetLength()).GetBuffer(0), FALSE
);
1085 CString csBase64_Passwd
= GetCompatibleString(EncodeBase64(cbsPasswd
.GetBuffer(), cbsPasswd
.GetLength()).GetBuffer(0), FALSE
);
1088 str
.Format( _T("%s\r\n"), csBase64_UserName
);
1089 if ( !Send ( str
) )
1092 if ( !GetResponse ( _T("334") ) )
1094 m_csLastError
.Format ( _T("Authentication UserName failed") );
1098 str
.Format(_T("%s\r\n"), csBase64_Passwd
);
1099 if ( !Send ( str
) )
1102 if ( !GetResponse ( _T("235") ) )
1104 m_csLastError
.Format ( _T("Authentication Password failed") );
1111 BOOL
CHwSMTP::SendHead()
1115 GetNameAddress(m_csAddrFrom
,name
,addr
);
1117 str
.Format( _T("MAIL From: <%s>\r\n"), addr
);
1118 if ( !Send ( str
) ) return FALSE
;
1120 if ( !GetResponse ( _T("250") ) ) return FALSE
;
1125 CString one
=m_csAddrTo
.Tokenize(_T(";"),start
);
1131 GetNameAddress(one
,name
,addr
);
1133 str
.Format(_T("RCPT TO: <%s>\r\n"), addr
);
1134 if ( !Send ( str
) ) return FALSE
;
1135 if ( !GetResponse ( _T("250") ) ) return FALSE
;
1138 if ( !Send ( CString(_T("DATA\r\n") ) ) ) return FALSE
;
1139 if ( !GetResponse ( CString(_T("354") )) ) return FALSE
;
1144 BOOL
CHwSMTP::SendSubject(const CString
&hostname
)
1147 csSubject
+= _T("Date: ");
1148 COleDateTime tNow
= COleDateTime::GetCurrentTime();
1151 csSubject
+= FormatDateTime (tNow
, _T("%a, %d %b %y %H:%M:%S %Z"));
1153 csSubject
+= _T("\r\n");
1154 csSubject
+= FormatString ( _T("From: %s\r\n"), this->m_csAddrFrom
);
1156 if (!m_StrCC
.IsEmpty())
1157 csSubject
+= FormatString ( _T("CC: %s\r\n"), this->m_StrCC
);
1159 if(m_csSender
.IsEmpty())
1160 m_csSender
= this->m_csAddrFrom
;
1162 csSubject
+= FormatString ( _T("Sender: %s\r\n"), this->m_csSender
);
1164 if(this->m_csToList
.IsEmpty())
1165 m_csToList
= m_csReceiverName
;
1167 csSubject
+= FormatString ( _T("To: %s\r\n"), this->m_csToList
);
1171 csSubject
+= FormatString ( _T("Subject: %s\r\n"), m_csSubject
);
1175 HRESULT hr
= CoCreateGuid(&guid
);
1179 if (UuidToString(&guid
, &guidStr
) == RPC_S_OK
)
1181 m_ListID
= (LPTSTR
)guidStr
;
1182 RpcStringFree(&guidStr
);
1185 if (m_ListID
.IsEmpty())
1187 m_csLastError
= _T("Could not generate Message-ID");
1190 csSubject
+= FormatString( _T("Message-ID: <%s@%s>\r\n"), m_ListID
, hostname
);
1192 csSubject
+= FormatString ( _T("X-Mailer: TortoiseGit\r\nMIME-Version: 1.0\r\nContent-Type: %s\r\n\r\n"),
1193 m_csMIMEContentType
);
1195 return Send ( csSubject
);
1198 BOOL
CHwSMTP::SendBody()
1200 CString csBody
, csTemp
;
1202 if ( m_StrAryAttach
.GetSize() > 0 )
1204 csTemp
.Format ( _T("%s\r\n\r\n"), m_csNoMIMEText
);
1207 csTemp
.Format ( _T("--%s\r\n"), m_csPartBoundary
);
1210 csTemp
.Format ( _T("Content-Type: text/plain\r\n%sContent-Transfer-Encoding: UTF-8\r\n\r\n"),
1215 //csTemp.Format ( _T("%s\r\n"), m_csBody );
1217 csBody
+= _T("\r\n");
1219 return Send ( csBody
);
1222 BOOL
CHwSMTP::SendAttach()
1224 int nCountAttach
= (int)m_StrAryAttach
.GetSize();
1225 if ( nCountAttach
< 1 ) return TRUE
;
1227 for ( int i
=0; i
<nCountAttach
; i
++ )
1229 if ( !SendOnAttach ( m_StrAryAttach
.GetAt(i
) ) )
1233 Send(L
"--" + m_csPartBoundary
+ L
"--\r\n");
1238 BOOL
CHwSMTP::SendOnAttach(LPCTSTR lpszFileName
)
1240 ASSERT ( lpszFileName
);
1241 CString csAttach
, csTemp
;
1243 csTemp
= lpszFileName
;
1244 CString csShortFileName
= csTemp
.GetBuffer(0) + csTemp
.ReverseFind ( '\\' );
1245 csShortFileName
.TrimLeft ( _T("\\") );
1247 csTemp
.Format ( _T("--%s\r\n"), m_csPartBoundary
);
1250 csTemp
.Format ( _T("Content-Type: application/octet-stream; file=%s\r\n"), csShortFileName
);
1253 csTemp
.Format ( _T("Content-Transfer-Encoding: base64\r\n") );
1256 csTemp
.Format ( _T("Content-Disposition: attachment; filename=%s\r\n\r\n"), csShortFileName
);
1259 DWORD dwFileSize
= hwGetFileAttr(lpszFileName
);
1260 if ( dwFileSize
> 5*1024*1024 )
1262 m_csLastError
.Format ( _T("File [%s] too big. File size is : %s"), lpszFileName
, FormatBytes(dwFileSize
) );
1265 char *pBuf
= new char[dwFileSize
+1];
1268 ::AfxThrowMemoryException ();
1272 if(!Send ( csAttach
))
1282 if ( !file
.Open ( lpszFileName
, CFile::modeRead
) )
1284 m_csLastError
.Format ( _T("Open file [%s] failed"), lpszFileName
);
1288 UINT nFileLen
= file
.Read ( pBuf
, dwFileSize
);
1289 filedata
= EncodeBase64(pBuf
, nFileLen
);
1290 filedata
+= _T("\r\n\r\n");
1292 catch (CFileException
*e
)
1295 m_csLastError
.Format ( _T("Read file [%s] failed"), lpszFileName
);
1300 if(!SendBuffer( filedata
.GetBuffer() ))
1309 //return Send ( csAttach );
1312 CString
CHwSMTP::GetLastErrorText()
1314 return m_csLastError
;
1318 // ½«×Ö·û´® lpszOrg ת»»Îª¶à×Ö½ÚµÄ×Ö·û´®£¬Èç¹û»¹ÒªÊ¹Óöà×Ö·û´®µÄ³¤¶È£¬¿ÉÒÔÓÃÒÔÏ·½Ê½À´Ê¹ÓÃÕâ¸öÀࣺ
1319 // CMultiByteString MultiByteString(_T("UNICODE×Ö·û´®"));
1320 // printf ( "ANSI ×Ö·û´®Îª£º %s£¬ ×Ö·û¸öÊýΪ£º %d £¬ ³¤¶ÈΪ£º %d×Ö½Ú\n", MultiByteString.GetBuffer(), MultiByteString.GetLength(), MultiByteString.GetSize() );
1322 CMultiByteString::CMultiByteString( LPCTSTR lpszOrg
, int nOrgStringEncodeType
/*=STRING_IS_SOFTCODE*/, OUT
char *pOutBuf
/*=NULL*/, int nOutBufSize
/*=0*/ )
1324 m_bNewBuffer
= FALSE
;
1327 m_nCharactersNumber
= 0;
1328 if ( !lpszOrg
) return;
1330 BOOL bOrgIsUnicode
= FALSE
;
1331 if ( nOrgStringEncodeType
== STRING_IS_MULTICHARS
) bOrgIsUnicode
= FALSE
;
1332 else if ( nOrgStringEncodeType
== STRING_IS_UNICODE
) bOrgIsUnicode
= TRUE
;
1336 bOrgIsUnicode
= TRUE
;
1338 bOrgIsUnicode
= FALSE
;
1342 if ( bOrgIsUnicode
)
1344 m_nCharactersNumber
= (int)wcslen((WCHAR
*)lpszOrg
);
1345 m_nDataSize
= (m_nCharactersNumber
+ 1) * sizeof(WCHAR
);
1349 m_nCharactersNumber
= (int)strlen((char*)lpszOrg
);
1350 m_nDataSize
= (m_nCharactersNumber
+ 1) * sizeof(char);
1353 if ( pOutBuf
&& nOutBufSize
> 0 )
1355 m_pszData
= pOutBuf
;
1356 m_nDataSize
= nOutBufSize
;
1360 m_pszData
= (char*)new BYTE
[m_nDataSize
];
1363 ::AfxThrowMemoryException ();
1366 m_bNewBuffer
= TRUE
;
1368 memset ( m_pszData
, 0, m_nDataSize
);
1370 if ( bOrgIsUnicode
)
1372 m_nCharactersNumber
= WideCharToMultiByte(CP_UTF8
, 0, (LPCWSTR
)lpszOrg
, m_nCharactersNumber
, (LPSTR
)m_pszData
, m_nDataSize
/ sizeof(char) - 1, NULL
, NULL
);
1373 if ( m_nCharactersNumber
< 1 ) m_nCharactersNumber
= (int)strlen ( m_pszData
);
1377 m_nCharactersNumber
= __min ( m_nCharactersNumber
, (int)(m_nDataSize
/sizeof(char)-1) );
1378 strncpy_s(m_pszData
, m_nCharactersNumber
, (const char*)lpszOrg
, m_nCharactersNumber
);
1379 m_nCharactersNumber
= (int)strlen ( m_pszData
);
1381 m_nDataSize
= ( m_nCharactersNumber
+ 1 ) * sizeof(char);
1384 CMultiByteString::~CMultiByteString ()
1386 if ( m_bNewBuffer
&& m_pszData
)
1392 CString
GetCompatibleString ( LPVOID lpszOrg
, BOOL bOrgIsUnicode
, int nOrgLength
/*=-1*/ )
1394 if ( !lpszOrg
) return _T("");
1399 if ( bOrgIsUnicode
)
1401 if ( nOrgLength
> 0 )
1403 WCHAR
*szRet
= new WCHAR
[nOrgLength
+1];
1404 if ( !szRet
) return _T("");
1405 memset ( szRet
, 0, (nOrgLength
+1)*sizeof(WCHAR
) );
1406 memcpy ( szRet
, lpszOrg
, nOrgLength
*sizeof(WCHAR
) );
1407 CString csRet
= szRet
;
1411 else if ( nOrgLength
== 0 )
1414 return (LPCTSTR
)lpszOrg
;
1417 if ( nOrgLength
< 0 )
1418 nOrgLength
= (int)strlen((const char*)lpszOrg
);
1419 int nWideCount
= nOrgLength
+ 1;
1420 WCHAR
*wchar
= new WCHAR
[nWideCount
];
1421 if ( !wchar
) return _T("");
1422 memset ( wchar
, 0, nWideCount
*sizeof(WCHAR
) );
1423 ::MultiByteToWideChar(CP_UTF8
, 0, (LPCSTR
)lpszOrg
, nOrgLength
, wchar
, nWideCount
);
1424 CString csRet
= wchar
;
1428 if ( !bOrgIsUnicode
)
1430 if ( nOrgLength
> 0 )
1432 char *szRet
= new char[nOrgLength
+1];
1433 if ( !szRet
) return _T("");
1434 memset ( szRet
, 0, (nOrgLength
+1)*sizeof(char) );
1435 memcpy ( szRet
, lpszOrg
, nOrgLength
*sizeof(char) );
1436 CString csRet
= szRet
;
1440 else if ( nOrgLength
== 0 )
1443 return (LPCTSTR
)lpszOrg
;
1446 if ( nOrgLength
< 0 )
1447 nOrgLength
= (int)wcslen((WCHAR
*)lpszOrg
);
1448 int nMultiByteCount
= nOrgLength
+ 1;
1449 char *szMultiByte
= new char[nMultiByteCount
];
1450 if ( !szMultiByte
) return _T("");
1451 memset ( szMultiByte
, 0, nMultiByteCount
*sizeof(char) );
1452 ::WideCharToMultiByte(CP_UTF8
, 0, (LPCWSTR
)lpszOrg
, nOrgLength
, (LPSTR
)szMultiByte
, nMultiByteCount
, NULL
, NULL
);
1453 CString csRet
= szMultiByte
;
1454 delete[] szMultiByte
;
1467 CString
FormatDateTime (COleDateTime
&DateTime
, LPCTSTR
/*pFormat*/)
1469 // If null, return empty string
1470 if ( DateTime
.GetStatus() == COleDateTime::null
|| DateTime
.GetStatus() == COleDateTime::invalid
)
1474 if (S_OK
!= VarUdateFromDate(DateTime
.m_dt
, 0, &ud
))
1479 TCHAR
*weeks
[]={_T("Sun"),_T("Mon"),_T("Tue"),_T("Wen"),_T("Thu"),_T("Fri"),_T("Sat")};
1480 TCHAR
*month
[]={_T("Jan"),_T("Feb"),_T("Mar"),_T("Apr"),
1481 _T("May"),_T("Jun"),_T("Jul"),_T("Aug"),
1482 _T("Sep"),_T("Oct"),_T("Nov"),_T("Dec")};
1484 TIME_ZONE_INFORMATION stTimeZone
;
1485 GetTimeZoneInformation(&stTimeZone
);
1488 strDate
.Format(_T("%s, %d %s %d %02d:%02d:%02d %c%04d")
1489 ,weeks
[ud
.st
.wDayOfWeek
],
1490 ud
.st
.wDay
,month
[ud
.st
.wMonth
-1],ud
.st
.wYear
,ud
.st
.wHour
,
1491 ud
.st
.wMinute
,ud
.st
.wSecond
,
1492 stTimeZone
.Bias
>0?_T('-'):_T('+'),
1493 abs(stTimeZone
.Bias
*10/6)
1498 CString
FormatString ( LPCTSTR lpszStr
, ... )
1501 for ( int nBufCount
= 1024; nBufCount
<5*1024*1024; nBufCount
+= 1024 )
1503 buf
= new TCHAR
[nBufCount
];
1506 ::AfxThrowMemoryException ();
1509 memset ( buf
, 0, nBufCount
*sizeof(TCHAR
) );
1512 va_start (va
, lpszStr
);
1513 int nLen
= _vsnprintf_hw((TCHAR
*)buf
, nBufCount
, _TRUNCATE
, lpszStr
, va
);
1515 if ( nLen
<= (int)(nBufCount
-sizeof(TCHAR
)) )
1517 delete[] buf
; buf
= NULL
;
1524 CString csMsg
= buf
;
1525 delete[] buf
; buf
= NULL
;
1529 int hwGetFileAttr ( LPCTSTR lpFileName
, OUT CFileStatus
*pFileStatus
/*=NULL*/ )
1531 if ( !lpFileName
|| lstrlen(lpFileName
) < 1 ) return -1;
1533 CFileStatus fileStatus
;
1534 fileStatus
.m_attribute
= 0;
1535 fileStatus
.m_size
= 0;
1536 memset ( fileStatus
.m_szFullName
, 0, sizeof(fileStatus
.m_szFullName
) );
1540 if ( CFile::GetStatus(lpFileName
,fileStatus
) )
1545 CATCH (CFileException
, e
)
1559 pFileStatus
->m_ctime
= fileStatus
.m_ctime
;
1560 pFileStatus
->m_mtime
= fileStatus
.m_mtime
;
1561 pFileStatus
->m_atime
= fileStatus
.m_atime
;
1562 pFileStatus
->m_size
= fileStatus
.m_size
;
1563 pFileStatus
->m_attribute
= fileStatus
.m_attribute
;
1564 lstrcpy ( pFileStatus
->m_szFullName
, fileStatus
.m_szFullName
);
1568 return (int)fileStatus
.m_size
;
1571 CString
FormatBytes ( double fBytesNum
, BOOL bShowUnit
/*=TRUE*/, int nFlag
/*=0*/ )
1576 if ( fBytesNum
>= 1024.0 && fBytesNum
< 1024.0*1024.0 )
1577 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ 1024.0, bShowUnit
?_T(" K"):_T("") );
1578 else if ( fBytesNum
>= 1024.0*1024.0 && fBytesNum
< 1024.0*1024.0*1024.0 )
1579 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ (1024.0*1024.0), bShowUnit
?_T(" M"):_T("") );
1580 else if ( fBytesNum
>= 1024.0*1024.0*1024.0 )
1581 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ (1024.0*1024.0*1024.0), bShowUnit
?_T(" G"):_T("") );
1583 csRes
.Format ( _T("%.2f%s"), fBytesNum
, bShowUnit
?_T(" B"):_T("") );
1585 else if ( nFlag
== 1 )
1587 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ 1024.0, bShowUnit
?_T(" K"):_T("") );
1589 else if ( nFlag
== 2 )
1591 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ (1024.0*1024.0), bShowUnit
?_T(" M"):_T("") );
1593 else if ( nFlag
== 3 )
1595 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ (1024.0*1024.0*1024.0), bShowUnit
?_T(" G"):_T("") );