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"
16 #define IO_BUFFER_SIZE 0x10000
18 #pragma comment(lib, "Secur32.lib")
20 DWORD dwProtocol
= SP_PROT_TLS1
; // SP_PROT_TLS1; // SP_PROT_PCT1; SP_PROT_SSL2; SP_PROT_SSL3; 0=default
21 ALG_ID aiKeyExch
= 0; // = default; CALG_DH_EPHEM; CALG_RSA_KEYX;
23 SCHANNEL_CRED SchannelCred
;
24 PSecurityFunctionTable g_pSSPI
;
26 //////////////////////////////////////////////////////////////////////
27 // Construction/Destruction
28 //////////////////////////////////////////////////////////////////////
31 m_bConnected ( FALSE
),
32 m_nSmtpSrvPort ( 25 ),
35 m_csPartBoundary
= _T( "WC_MAIL_PaRt_BoUnDaRy_05151998" );
36 m_csMIMEContentType
= FormatString ( _T( "multipart/mixed; boundary=%s" ), m_csPartBoundary
);
37 m_csNoMIMEText
= _T( "This is a multi-part message in MIME format." );
38 //m_csCharSet = _T("\r\n\tcharset=\"iso-8859-1\"\r\n");
45 m_iSecurityLevel
= none
;
47 SecureZeroMemory(&Sizes
, sizeof(SecPkgContext_StreamSizes
));
56 void CHwSMTP::GetNameAddress(CString
&in
, CString
&name
,CString
&address
)
59 start
=in
.Find(_T('<'));
62 if(start
>=0 && end
>=0)
65 address
=in
.Mid(start
+1,end
-start
-1);
71 CString
CHwSMTP::GetServerAddress(CString
&email
)
76 start
= email
.Find(_T("<"));
77 end
= email
.Find(_T(">"));
79 if(start
>=0 && end
>=0)
81 str
=email
.Mid(start
+1,end
-start
-1);
88 start
= str
.Find(_T('@'));
89 return str
.Mid(start
+1);
93 BOOL
CHwSMTP::SendSpeedEmail
100 CStringArray
*pStrAryAttach
,
108 To
+= GET_SAFE_STRING(lpszAddrTo
);
110 To
+= GET_SAFE_STRING(pStrAryCC
);
112 std::map
<CString
,std::vector
<CString
>> Address
;
117 CString one
= To
.Tokenize(_T(";"),start
);
123 addr
= GetServerAddress(one
);
127 Address
[addr
].push_back(one
);
131 std::map
<CString
,std::vector
<CString
>>::iterator itr1
= Address
.begin();
132 for( ; itr1
!= Address
.end(); ++itr1
)
134 PDNS_RECORD pDnsRecord
;
138 DnsQuery(itr1
->first
,
139 DNS_TYPE_MX
,DNS_QUERY_STANDARD
,
140 NULL
, //Contains DNS server IP address.
141 &pDnsRecord
, //Resource record that contains the response.
146 m_csLastError
.Format(_T("DNS query failed %d"), status
);
153 for (size_t i
= 0; i
< itr1
->second
.size(); ++i
)
164 if(pNext
->wType
== DNS_TYPE_MX
)
165 if(SendEmail(pNext
->Data
.MX
.pNameExchange
,NULL
,NULL
,false,
166 lpszAddrFrom
,to
,lpszSubject
,lpszBody
,lpszCharSet
,pStrAryAttach
,pStrAryCC
,
167 25,pSend
,lpszAddrTo
))
175 DnsRecordListFree(pDnsRecord
,DnsFreeRecordList
);
181 static SECURITY_STATUS
ClientHandshakeLoop(CSocket
* Socket
, PCredHandle phCreds
, CtxtHandle
* phContext
, BOOL fDoInitialRead
, SecBuffer
* pExtraData
)
183 SecBufferDesc OutBuffer
, InBuffer
;
184 SecBuffer InBuffers
[2], OutBuffers
[1];
185 DWORD dwSSPIFlags
, dwSSPIOutFlags
, cbData
, cbIoBuffer
;
187 SECURITY_STATUS scRet
;
191 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
| ISC_REQ_REPLAY_DETECT
| ISC_REQ_CONFIDENTIALITY
|
192 ISC_RET_EXTENDED_ERROR
| ISC_REQ_ALLOCATE_MEMORY
| ISC_REQ_STREAM
;
194 // Allocate data buffer.
195 IoBuffer
= new UCHAR
[IO_BUFFER_SIZE
];
196 if (IoBuffer
== nullptr)
198 // printf("**** Out of memory (1)\n");
199 return SEC_E_INTERNAL_ERROR
;
202 fDoRead
= fDoInitialRead
;
204 // Loop until the handshake is finished or an error occurs.
205 scRet
= SEC_I_CONTINUE_NEEDED
;
207 while (scRet
== SEC_I_CONTINUE_NEEDED
|| scRet
== SEC_E_INCOMPLETE_MESSAGE
|| scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
209 if (0 == cbIoBuffer
|| scRet
== SEC_E_INCOMPLETE_MESSAGE
) // Read data from server.
213 cbData
= Socket
->Receive(IoBuffer
+ cbIoBuffer
, IO_BUFFER_SIZE
- cbIoBuffer
, 0);
214 if (cbData
== SOCKET_ERROR
)
216 // printf("**** Error %d reading data from server\n", WSAGetLastError());
217 scRet
= SEC_E_INTERNAL_ERROR
;
220 else if (cbData
== 0)
222 // printf("**** Server unexpectedly disconnected\n");
223 scRet
= SEC_E_INTERNAL_ERROR
;
226 // printf("%d bytes of handshake data received\n", cbData);
227 cbIoBuffer
+= cbData
;
233 // Set up the input buffers. Buffer 0 is used to pass in data
234 // received from the server. Schannel will consume some or all
235 // of this. Leftover data (if any) will be placed in buffer 1 and
236 // given a buffer type of SECBUFFER_EXTRA.
237 InBuffers
[0].pvBuffer
= IoBuffer
;
238 InBuffers
[0].cbBuffer
= cbIoBuffer
;
239 InBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
241 InBuffers
[1].pvBuffer
= nullptr;
242 InBuffers
[1].cbBuffer
= 0;
243 InBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
245 InBuffer
.cBuffers
= 2;
246 InBuffer
.pBuffers
= InBuffers
;
247 InBuffer
.ulVersion
= SECBUFFER_VERSION
;
249 // Set up the output buffers. These are initialized to NULL
250 // so as to make it less likely we'll attempt to free random
252 OutBuffers
[0].pvBuffer
= nullptr;
253 OutBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
254 OutBuffers
[0].cbBuffer
= 0;
256 OutBuffer
.cBuffers
= 1;
257 OutBuffer
.pBuffers
= OutBuffers
;
258 OutBuffer
.ulVersion
= SECBUFFER_VERSION
;
260 // Call InitializeSecurityContext.
261 scRet
= g_pSSPI
->InitializeSecurityContext(phCreds
, phContext
, nullptr, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, &InBuffer
, 0, nullptr, &OutBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
263 // If InitializeSecurityContext was successful (or if the error was
264 // one of the special extended ones), send the contends of the output
265 // buffer to the server.
266 if (scRet
== SEC_E_OK
|| scRet
== SEC_I_CONTINUE_NEEDED
|| FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
268 if (OutBuffers
[0].cbBuffer
!= 0 && OutBuffers
[0].pvBuffer
!= nullptr)
270 cbData
= Socket
->Send(OutBuffers
[0].pvBuffer
, OutBuffers
[0].cbBuffer
, 0 );
271 if(cbData
== SOCKET_ERROR
|| cbData
== 0)
273 // printf( "**** Error %d sending data to server (2)\n", WSAGetLastError() );
274 g_pSSPI
->FreeContextBuffer(OutBuffers
[0].pvBuffer
);
275 g_pSSPI
->DeleteSecurityContext(phContext
);
276 return SEC_E_INTERNAL_ERROR
;
278 // printf("%d bytes of handshake data sent\n", cbData);
280 // Free output buffer.
281 g_pSSPI
->FreeContextBuffer(OutBuffers
[0].pvBuffer
);
282 OutBuffers
[0].pvBuffer
= nullptr;
286 // If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
287 // then we need to read more data from the server and try again.
288 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
) continue;
290 // If InitializeSecurityContext returned SEC_E_OK, then the
291 // handshake completed successfully.
292 if (scRet
== SEC_E_OK
)
294 // If the "extra" buffer contains data, this is encrypted application
295 // protocol layer stuff. It needs to be saved. The application layer
296 // will later decrypt it with DecryptMessage.
297 // printf("Handshake was successful\n");
299 if (InBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
301 pExtraData
->pvBuffer
= LocalAlloc( LMEM_FIXED
, InBuffers
[1].cbBuffer
);
302 if (pExtraData
->pvBuffer
== nullptr)
304 // printf("**** Out of memory (2)\n");
305 return SEC_E_INTERNAL_ERROR
;
308 MoveMemory(pExtraData
->pvBuffer
, IoBuffer
+ (cbIoBuffer
- InBuffers
[1].cbBuffer
), InBuffers
[1].cbBuffer
);
310 pExtraData
->cbBuffer
= InBuffers
[1].cbBuffer
;
311 pExtraData
->BufferType
= SECBUFFER_TOKEN
;
313 // printf( "%d bytes of app data was bundled with handshake data\n", pExtraData->cbBuffer );
317 pExtraData
->pvBuffer
= nullptr;
318 pExtraData
->cbBuffer
= 0;
319 pExtraData
->BufferType
= SECBUFFER_EMPTY
;
321 break; // Bail out to quit
324 // Check for fatal error.
327 // printf("**** Error 0x%x returned by InitializeSecurityContext (2)\n", scRet);
331 // If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
332 // then the server just requested client authentication.
333 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
335 // Busted. The server has requested client authentication and
336 // the credential we supplied didn't contain a client certificate.
337 // This function will read the list of trusted certificate
338 // authorities ("issuers") that was received from the server
339 // and attempt to find a suitable client certificate that
340 // was issued by one of these. If this function is successful,
341 // then we will connect using the new certificate. Otherwise,
342 // we will attempt to connect anonymously (using our current credentials).
343 //GetNewClientCredentials(phCreds, phContext);
347 scRet
= SEC_I_CONTINUE_NEEDED
;
351 // Copy any leftover data from the "extra" buffer, and go around again.
352 if ( InBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
354 MoveMemory(IoBuffer
, IoBuffer
+ (cbIoBuffer
- InBuffers
[1].cbBuffer
), InBuffers
[1].cbBuffer
);
355 cbIoBuffer
= InBuffers
[1].cbBuffer
;
361 // Delete the security context in the case of a fatal error.
363 g_pSSPI
->DeleteSecurityContext(phContext
);
369 static SECURITY_STATUS
PerformClientHandshake( CSocket
* Socket
, PCredHandle phCreds
, LPTSTR pszServerName
, CtxtHandle
* phContext
, SecBuffer
* pExtraData
)
371 SecBufferDesc OutBuffer
;
372 SecBuffer OutBuffers
[1];
373 DWORD dwSSPIFlags
, dwSSPIOutFlags
, cbData
;
375 SECURITY_STATUS scRet
;
377 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
| ISC_REQ_REPLAY_DETECT
| ISC_REQ_CONFIDENTIALITY
|
378 ISC_RET_EXTENDED_ERROR
| ISC_REQ_ALLOCATE_MEMORY
| ISC_REQ_STREAM
;
380 // Initiate a ClientHello message and generate a token.
381 OutBuffers
[0].pvBuffer
= nullptr;
382 OutBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
383 OutBuffers
[0].cbBuffer
= 0;
385 OutBuffer
.cBuffers
= 1;
386 OutBuffer
.pBuffers
= OutBuffers
;
387 OutBuffer
.ulVersion
= SECBUFFER_VERSION
;
389 scRet
= g_pSSPI
->InitializeSecurityContext(phCreds
, nullptr, pszServerName
, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, nullptr, 0, phContext
, &OutBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
391 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
393 // printf("**** Error %d returned by InitializeSecurityContext (1)\n", scRet);
397 // Send response to server if there is one.
398 if (OutBuffers
[0].cbBuffer
!= 0 && OutBuffers
[0].pvBuffer
!= nullptr)
400 cbData
= Socket
->Send(OutBuffers
[0].pvBuffer
, OutBuffers
[0].cbBuffer
, 0);
401 if (cbData
== SOCKET_ERROR
|| cbData
== 0)
403 // printf("**** Error %d sending data to server (1)\n", WSAGetLastError());
404 g_pSSPI
->FreeContextBuffer(OutBuffers
[0].pvBuffer
);
405 g_pSSPI
->DeleteSecurityContext(phContext
);
406 return SEC_E_INTERNAL_ERROR
;
408 // printf("%d bytes of handshake data sent\n", cbData);
410 g_pSSPI
->FreeContextBuffer(OutBuffers
[0].pvBuffer
); // Free output buffer.
411 OutBuffers
[0].pvBuffer
= nullptr;
414 return ClientHandshakeLoop(Socket
, phCreds
, phContext
, TRUE
, pExtraData
);
417 static SECURITY_STATUS
CreateCredentials(PCredHandle phCreds
)
420 SECURITY_STATUS Status
;
421 DWORD cSupportedAlgs
= 0;
422 ALG_ID rgbSupportedAlgs
[16];
424 // Build Schannel credential structure. Currently, this sample only
425 // specifies the protocol to be used (and optionally the certificate,
426 // of course). Real applications may wish to specify other parameters as well.
427 SecureZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
429 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
430 SchannelCred
.grbitEnabledProtocols
= dwProtocol
;
433 rgbSupportedAlgs
[cSupportedAlgs
++] = aiKeyExch
;
437 SchannelCred
.cSupportedAlgs
= cSupportedAlgs
;
438 SchannelCred
.palgSupportedAlgs
= rgbSupportedAlgs
;
441 SchannelCred
.dwFlags
|= SCH_CRED_NO_DEFAULT_CREDS
;
443 // The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because
444 // this sample verifies the server certificate manually.
445 // Applications that expect to run on WinNT, Win9x, or WinME
446 // should specify this flag and also manually verify the server
447 // certificate. Applications running on newer versions of Windows can
448 // leave off this flag, in which case the InitializeSecurityContext
449 // function will validate the server certificate automatically.
450 SchannelCred
.dwFlags
|= SCH_CRED_MANUAL_CRED_VALIDATION
;
452 // Create an SSPI credential.
453 Status
= g_pSSPI
->AcquireCredentialsHandle(nullptr, // Name of principal
454 UNISP_NAME
, // Name of package
455 SECPKG_CRED_OUTBOUND
, // Flags indicating use
456 nullptr, // Pointer to logon ID
457 &SchannelCred
, // Package specific data
458 nullptr, // Pointer to GetKey() func
459 nullptr, // Value to pass to GetKey()
460 phCreds
, // (out) Cred Handle
461 &tsExpiry
); // (out) Lifetime (optional)
466 static DWORD
VerifyServerCertificate(PCCERT_CONTEXT pServerCert
, PTSTR pwszServerName
, DWORD dwCertFlags
)
468 HTTPSPolicyCallbackData polHttps
;
469 CERT_CHAIN_POLICY_PARA PolicyPara
;
470 CERT_CHAIN_POLICY_STATUS PolicyStatus
;
471 CERT_CHAIN_PARA ChainPara
;
472 PCCERT_CHAIN_CONTEXT pChainContext
= nullptr;
474 LPSTR rgszUsages
[] = { szOID_PKIX_KP_SERVER_AUTH
, szOID_SERVER_GATED_CRYPTO
, szOID_SGC_NETSCAPE
};
476 DWORD cUsages
= sizeof(rgszUsages
) / sizeof(LPSTR
);
478 if (pServerCert
== nullptr || pwszServerName
== nullptr)
480 Status
= (DWORD
)SEC_E_WRONG_PRINCIPAL
;
484 // Build certificate chain.
485 SecureZeroMemory(&ChainPara
, sizeof(ChainPara
));
486 ChainPara
.cbSize
= sizeof(ChainPara
);
487 ChainPara
.RequestedUsage
.dwType
= USAGE_MATCH_TYPE_OR
;
488 ChainPara
.RequestedUsage
.Usage
.cUsageIdentifier
= cUsages
;
489 ChainPara
.RequestedUsage
.Usage
.rgpszUsageIdentifier
= rgszUsages
;
491 if (!CertGetCertificateChain(nullptr, pServerCert
, nullptr, pServerCert
->hCertStore
, &ChainPara
, 0, nullptr, &pChainContext
))
493 Status
= GetLastError();
494 // printf("Error 0x%x returned by CertGetCertificateChain!\n", Status);
498 // Validate certificate chain.
499 SecureZeroMemory(&polHttps
, sizeof(HTTPSPolicyCallbackData
));
500 polHttps
.cbStruct
= sizeof(HTTPSPolicyCallbackData
);
501 polHttps
.dwAuthType
= AUTHTYPE_SERVER
;
502 polHttps
.fdwChecks
= dwCertFlags
;
503 polHttps
.pwszServerName
= pwszServerName
;
505 SecureZeroMemory(&PolicyPara
, sizeof(PolicyPara
));
506 PolicyPara
.cbSize
= sizeof(PolicyPara
);
507 PolicyPara
.pvExtraPolicyPara
= &polHttps
;
509 SecureZeroMemory(&PolicyStatus
, sizeof(PolicyStatus
));
510 PolicyStatus
.cbSize
= sizeof(PolicyStatus
);
512 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL
, pChainContext
, &PolicyPara
, &PolicyStatus
))
514 Status
= GetLastError();
518 if (PolicyStatus
.dwError
)
520 Status
= PolicyStatus
.dwError
;
528 CertFreeCertificateChain(pChainContext
);
533 static DWORD
EncryptSend(CSocket
* Socket
, CtxtHandle
* phContext
, PBYTE pbIoBuffer
, SecPkgContext_StreamSizes Sizes
)
534 // http://msdn.microsoft.com/en-us/library/aa375378(VS.85).aspx
535 // The encrypted message is encrypted in place, overwriting the original contents of its buffer.
537 SECURITY_STATUS scRet
;
538 SecBufferDesc Message
;
539 SecBuffer Buffers
[4];
543 pbMessage
= pbIoBuffer
+ Sizes
.cbHeader
; // Offset by "header size"
544 cbMessage
= (DWORD
)strlen((char *)pbMessage
);
546 // Encrypt the HTTP request.
547 Buffers
[0].pvBuffer
= pbIoBuffer
; // Pointer to buffer 1
548 Buffers
[0].cbBuffer
= Sizes
.cbHeader
; // length of header
549 Buffers
[0].BufferType
= SECBUFFER_STREAM_HEADER
; // Type of the buffer
551 Buffers
[1].pvBuffer
= pbMessage
; // Pointer to buffer 2
552 Buffers
[1].cbBuffer
= cbMessage
; // length of the message
553 Buffers
[1].BufferType
= SECBUFFER_DATA
; // Type of the buffer
555 Buffers
[2].pvBuffer
= pbMessage
+ cbMessage
; // Pointer to buffer 3
556 Buffers
[2].cbBuffer
= Sizes
.cbTrailer
; // length of the trailor
557 Buffers
[2].BufferType
= SECBUFFER_STREAM_TRAILER
; // Type of the buffer
559 Buffers
[3].pvBuffer
= SECBUFFER_EMPTY
; // Pointer to buffer 4
560 Buffers
[3].cbBuffer
= SECBUFFER_EMPTY
; // length of buffer 4
561 Buffers
[3].BufferType
= SECBUFFER_EMPTY
; // Type of the buffer 4
563 Message
.ulVersion
= SECBUFFER_VERSION
; // Version number
564 Message
.cBuffers
= 4; // Number of buffers - must contain four SecBuffer structures.
565 Message
.pBuffers
= Buffers
; // Pointer to array of buffers
567 scRet
= g_pSSPI
->EncryptMessage(phContext
, 0, &Message
, 0); // must contain four SecBuffer structures.
570 // printf("**** Error 0x%x returned by EncryptMessage\n", scRet);
575 // Send the encrypted data to the server.
576 return Socket
->Send(pbIoBuffer
, Buffers
[0].cbBuffer
+ Buffers
[1].cbBuffer
+ Buffers
[2].cbBuffer
, 0);
579 static LONG
DisconnectFromServer(CSocket
* Socket
, PCredHandle phCreds
, CtxtHandle
* phContext
)
582 DWORD dwType
, dwSSPIFlags
, dwSSPIOutFlags
, cbMessage
, cbData
, Status
;
583 SecBufferDesc OutBuffer
;
584 SecBuffer OutBuffers
[1];
587 dwType
= SCHANNEL_SHUTDOWN
; // Notify schannel that we are about to close the connection.
589 OutBuffers
[0].pvBuffer
= &dwType
;
590 OutBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
591 OutBuffers
[0].cbBuffer
= sizeof(dwType
);
593 OutBuffer
.cBuffers
= 1;
594 OutBuffer
.pBuffers
= OutBuffers
;
595 OutBuffer
.ulVersion
= SECBUFFER_VERSION
;
597 Status
= g_pSSPI
->ApplyControlToken(phContext
, &OutBuffer
);
600 // printf("**** Error 0x%x returned by ApplyControlToken\n", Status);
604 // Build an SSL close notify message.
605 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
| ISC_REQ_REPLAY_DETECT
| ISC_REQ_CONFIDENTIALITY
| ISC_RET_EXTENDED_ERROR
| ISC_REQ_ALLOCATE_MEMORY
| ISC_REQ_STREAM
;
607 OutBuffers
[0].pvBuffer
= nullptr;
608 OutBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
609 OutBuffers
[0].cbBuffer
= 0;
611 OutBuffer
.cBuffers
= 1;
612 OutBuffer
.pBuffers
= OutBuffers
;
613 OutBuffer
.ulVersion
= SECBUFFER_VERSION
;
615 Status
= g_pSSPI
->InitializeSecurityContext(phCreds
, phContext
, nullptr, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, nullptr, 0, phContext
, &OutBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
619 // printf("**** Error 0x%x returned by InitializeSecurityContext\n", Status);
623 pbMessage
= (PBYTE
)OutBuffers
[0].pvBuffer
;
624 cbMessage
= OutBuffers
[0].cbBuffer
;
626 // Send the close notify message to the server.
627 if (pbMessage
!= nullptr && cbMessage
!= 0)
629 cbData
= Socket
->Send(pbMessage
, cbMessage
, 0);
630 if (cbData
== SOCKET_ERROR
|| cbData
== 0)
632 Status
= WSAGetLastError();
635 // printf("Sending Close Notify\n");
636 // printf("%d bytes of handshake data sent\n", cbData);
637 g_pSSPI
->FreeContextBuffer(pbMessage
); // Free output buffer.
641 g_pSSPI
->DeleteSecurityContext(phContext
); // Free the security context.
647 static SECURITY_STATUS
ReadDecrypt(CSocket
* Socket
, PCredHandle phCreds
, CtxtHandle
* phContext
, PBYTE pbIoBuffer
, DWORD cbIoBufferLength
)
649 // calls recv() - blocking socket read
650 // http://msdn.microsoft.com/en-us/library/ms740121(VS.85).aspx
652 // The encrypted message is decrypted in place, overwriting the original contents of its buffer.
653 // http://msdn.microsoft.com/en-us/library/aa375211(VS.85).aspx
656 SecBuffer ExtraBuffer
;
657 SecBuffer
* pDataBuffer
, * pExtraBuffer
;
659 SECURITY_STATUS scRet
;
660 SecBufferDesc Message
;
661 SecBuffer Buffers
[4];
663 DWORD cbIoBuffer
, cbData
, length
;
666 // Read data from server until done.
669 while (TRUE
) // Read some data.
671 if (cbIoBuffer
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
) // get the data
673 cbData
= Socket
->Receive(pbIoBuffer
+ cbIoBuffer
, cbIoBufferLength
- cbIoBuffer
, 0);
674 if (cbData
== SOCKET_ERROR
)
676 // printf("**** Error %d reading data from server\n", WSAGetLastError());
677 scRet
= SEC_E_INTERNAL_ERROR
;
680 else if (cbData
== 0) // Server disconnected.
684 // printf("**** Server unexpectedly disconnected\n");
685 scRet
= SEC_E_INTERNAL_ERROR
;
693 // printf("%d bytes of (encrypted) application data received\n", cbData);
694 cbIoBuffer
+= cbData
;
698 // Decrypt the received data.
699 Buffers
[0].pvBuffer
= pbIoBuffer
;
700 Buffers
[0].cbBuffer
= cbIoBuffer
;
701 Buffers
[0].BufferType
= SECBUFFER_DATA
; // Initial Type of the buffer 1
702 Buffers
[1].BufferType
= SECBUFFER_EMPTY
; // Initial Type of the buffer 2
703 Buffers
[2].BufferType
= SECBUFFER_EMPTY
; // Initial Type of the buffer 3
704 Buffers
[3].BufferType
= SECBUFFER_EMPTY
; // Initial Type of the buffer 4
706 Message
.ulVersion
= SECBUFFER_VERSION
; // Version number
707 Message
.cBuffers
= 4; // Number of buffers - must contain four SecBuffer structures.
708 Message
.pBuffers
= Buffers
; // Pointer to array of buffers
710 scRet
= g_pSSPI
->DecryptMessage(phContext
, &Message
, 0, nullptr);
711 if (scRet
== SEC_I_CONTEXT_EXPIRED
)
712 break; // Server signalled end of session
713 // if (scRet == SEC_E_INCOMPLETE_MESSAGE - Input buffer has partial encrypted record, read more
714 if (scRet
!= SEC_E_OK
&& scRet
!= SEC_I_RENEGOTIATE
&& scRet
!= SEC_I_CONTEXT_EXPIRED
)
717 // Locate data and (optional) extra buffers.
718 pDataBuffer
= nullptr;
719 pExtraBuffer
= nullptr;
720 for (int i
= 1; i
< 4; ++i
)
722 if (pDataBuffer
== nullptr && Buffers
[i
].BufferType
== SECBUFFER_DATA
)
723 pDataBuffer
= &Buffers
[i
];
724 if (pExtraBuffer
== nullptr && Buffers
[i
].BufferType
== SECBUFFER_EXTRA
)
725 pExtraBuffer
= &Buffers
[i
];
728 // Display the decrypted data.
731 length
= pDataBuffer
->cbBuffer
;
732 if (length
) // check if last two chars are CR LF
734 buff
= (PBYTE
)pDataBuffer
->pvBuffer
;
735 if (buff
[length
-2] == 13 && buff
[length
-1] == 10) // Found CRLF
743 // Move any "extra" data to the input buffer.
746 MoveMemory(pbIoBuffer
, pExtraBuffer
->pvBuffer
, pExtraBuffer
->cbBuffer
);
747 cbIoBuffer
= pExtraBuffer
->cbBuffer
;
752 // The server wants to perform another handshake sequence.
753 if (scRet
== SEC_I_RENEGOTIATE
)
755 // printf("Server requested renegotiate!\n");
756 scRet
= ClientHandshakeLoop( Socket
, phCreds
, phContext
, FALSE
, &ExtraBuffer
);
757 if (scRet
!= SEC_E_OK
)
760 if (ExtraBuffer
.pvBuffer
) // Move any "extra" data to the input buffer.
762 MoveMemory(pbIoBuffer
, ExtraBuffer
.pvBuffer
, ExtraBuffer
.cbBuffer
);
763 cbIoBuffer
= ExtraBuffer
.cbBuffer
;
766 } // Loop till CRLF is found at the end of the data
771 BOOL
CHwSMTP::SendEmail (
772 LPCTSTR lpszSmtpSrvHost
,
773 LPCTSTR lpszUserName
,
776 LPCTSTR lpszAddrFrom
,
780 LPCTSTR lpszCharSet
, // ×Ö·û¼¯ÀàÐÍ£¬ÀýÈ磺·±ÌåÖÐÎÄÕâÀïÓ¦ÊäÈë"big5"£¬¼òÌåÖÐÎÄʱÊäÈë"gb2312"
781 CStringArray
*pStrAryAttach
/*=NULL*/,
782 LPCTSTR pStrAryCC
/*=NULL*/,
783 UINT nSmtpSrvPort
,/*=25*/
789 TRACE ( _T("·¢ËÍÓʼþ£º%s, %s\n"), lpszAddrTo
, lpszBody
);
790 m_StrAryAttach
.RemoveAll();
792 m_StrCC
+= GET_SAFE_STRING(pStrAryCC
);
794 m_csSmtpSrvHost
= GET_SAFE_STRING ( lpszSmtpSrvHost
);
795 if ( m_csSmtpSrvHost
.GetLength() <= 0 )
797 m_csLastError
.Format ( _T("Parameter Error!") );
800 m_csUserName
= GET_SAFE_STRING ( lpszUserName
);
801 m_csPasswd
= GET_SAFE_STRING ( lpszPasswd
);
802 m_bMustAuth
= bMustAuth
;
803 if ( m_bMustAuth
&& m_csUserName
.GetLength() <= 0 )
805 m_csLastError
.Format ( _T("Parameter Error!") );
809 m_csAddrFrom
= GET_SAFE_STRING ( lpszAddrFrom
);
810 m_csAddrTo
= GET_SAFE_STRING ( lpszAddrTo
);
811 // m_csFromName = GET_SAFE_STRING ( lpszFromName );
812 // m_csReceiverName = GET_SAFE_STRING ( lpszReceiverName );
813 m_csSubject
= GET_SAFE_STRING ( lpszSubject
);
814 m_csBody
= GET_SAFE_STRING ( lpszBody
);
816 this->m_csSender
= GET_SAFE_STRING(pSender
);
817 this->m_csToList
= GET_SAFE_STRING(pToList
);
819 m_nSmtpSrvPort
= nSmtpSrvPort
;
821 if ( lpszCharSet
&& lstrlen(lpszCharSet
) > 0 )
822 m_csCharSet
.Format ( _T("\r\n\tcharset=\"%s\"\r\n"), lpszCharSet
);
825 m_csAddrFrom
.GetLength() <= 0 || m_csAddrTo
.GetLength() <= 0
828 m_csLastError
.Format ( _T("Parameter Error!") );
834 m_StrAryAttach
.Append ( *pStrAryAttach
);
836 if ( m_StrAryAttach
.GetSize() < 1 )
837 m_csMIMEContentType
= FormatString ( _T( "text/plain; %s" ), m_csCharSet
);
841 if ( !m_SendSock
.Create () )
843 //int nResult = GetLastError();
844 m_csLastError
.Format ( _T("Create socket failed!") );
851 m_iSecurityLevel
= want_tls
;
854 m_iSecurityLevel
= ssl
;
857 m_iSecurityLevel
= none
;
861 if ( !m_SendSock
.Connect ( m_csSmtpSrvHost
, m_nSmtpSrvPort
) )
863 m_csLastError
.Format ( _T("Connect to [ %s ] failed"), m_csSmtpSrvHost
);
864 TRACE ( _T("%d\n"), GetLastError() );
868 if (m_iSecurityLevel
== want_tls
) {
869 if (!GetResponse(_T("220")))
873 if (!GetResponse(_T("220")))
875 m_iSecurityLevel
= tls_established
;
881 SECURITY_STATUS Status
;
883 CtxtHandle contextStruct
;
884 CredHandle credentialsStruct
;
886 if (m_iSecurityLevel
>= ssl
)
888 g_pSSPI
= InitSecurityInterface();
890 contextStruct
.dwLower
= 0;
891 contextStruct
.dwUpper
= 0;
893 hCreds
= &credentialsStruct
;
894 credentialsStruct
.dwLower
= 0;
895 credentialsStruct
.dwUpper
= 0;
896 Status
= CreateCredentials(hCreds
);
897 if (Status
!= SEC_E_OK
)
899 m_csLastError
= CFormatMessageWrapper(Status
);
903 hContext
= &contextStruct
;
904 Status
= PerformClientHandshake(&m_SendSock
, hCreds
, m_csSmtpSrvHost
.GetBuffer(), hContext
, &ExtraData
);
905 if (Status
!= SEC_E_OK
)
907 m_csLastError
= CFormatMessageWrapper(Status
);
911 PCCERT_CONTEXT pRemoteCertContext
= nullptr;
912 // Authenticate server's credentials. Get server's certificate.
913 Status
= g_pSSPI
->QueryContextAttributes(hContext
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (PVOID
)&pRemoteCertContext
);
916 m_csLastError
= CFormatMessageWrapper(Status
);
920 Status
= VerifyServerCertificate( pRemoteCertContext
, m_csSmtpSrvHost
.GetBuffer(), 0 );
923 m_csLastError
= CFormatMessageWrapper(Status
);
927 CertFreeCertificateContext(pRemoteCertContext
);
929 Status
= g_pSSPI
->QueryContextAttributes(hContext
, SECPKG_ATTR_STREAM_SIZES
, &Sizes
);
932 m_csLastError
= CFormatMessageWrapper(Status
);
937 cbIoBufferLength
= Sizes
.cbHeader
+ Sizes
.cbMaximumMessage
+ Sizes
.cbTrailer
;
938 pbIoBuffer
= (PBYTE
)LocalAlloc(LMEM_FIXED
, cbIoBufferLength
);
939 SecureZeroMemory(pbIoBuffer
, cbIoBufferLength
);
940 if (pbIoBuffer
== nullptr)
942 m_csLastError
= _T("Could not allocate memory");
947 if (m_iSecurityLevel
<= ssl
)
949 if (!GetResponse(_T("220")))
957 if (m_iSecurityLevel
>= ssl
)
959 if (hContext
&& hCreds
)
960 DisconnectFromServer(&m_SendSock
, hCreds
, hContext
);
963 LocalFree(pbIoBuffer
);
964 pbIoBuffer
= nullptr;
965 cbIoBufferLength
= 0;
969 g_pSSPI
->DeleteSecurityContext(hContext
);
974 g_pSSPI
->FreeCredentialsHandle(hCreds
);
985 BOOL
CHwSMTP::GetResponse ( LPCTSTR lpszVerifyCode
, int *pnCode
/*=NULL*/)
987 if ( !lpszVerifyCode
|| lstrlen(lpszVerifyCode
) < 1 )
990 SECURITY_STATUS scRet
= SEC_E_OK
;
992 char szRecvBuf
[1024] = {0};
994 char szStatusCode
[4] = {0};
996 if (m_iSecurityLevel
>= ssl
)
998 scRet
= ReadDecrypt(&m_SendSock
, hCreds
, hContext
, pbIoBuffer
, cbIoBufferLength
);
999 SecureZeroMemory(szRecvBuf
, 1024);
1000 memcpy(szRecvBuf
, pbIoBuffer
+Sizes
.cbHeader
, 1024);
1003 nRet
= m_SendSock
.Receive(szRecvBuf
, sizeof(szRecvBuf
));
1004 TRACE ( _T("Received : %s\r\n"), szRecvBuf
);
1005 if (nRet
== 0 && m_iSecurityLevel
== none
|| m_iSecurityLevel
>= ssl
&& scRet
!= SEC_E_OK
)
1007 m_csLastError
.Format ( _T("Receive TCP data failed") );
1010 // TRACE ( _T("ÊÕµ½·þÎñÆ÷»ØÓ¦£º%s\n"), szRecvBuf );
1012 memcpy ( szStatusCode
, szRecvBuf
, 3 );
1013 if ( pnCode
) (*pnCode
) = atoi ( szStatusCode
);
1015 if ( strcmp ( szStatusCode
, CMultiByteString(lpszVerifyCode
).GetBuffer() ) != 0 )
1017 m_csLastError
.Format ( _T("Received invalid response : %s"), GetCompatibleString(szRecvBuf
,FALSE
) );
1023 BOOL
CHwSMTP::SendBuffer(char *buff
,int size
)
1026 size
=(int)strlen(buff
);
1027 if ( !m_bConnected
)
1029 m_csLastError
.Format ( _T("Didn't connect") );
1033 if (m_iSecurityLevel
>= ssl
)
1036 while (size
- sent
> 0)
1038 int toSend
= min(size
- sent
, (int)Sizes
.cbMaximumMessage
);
1039 SecureZeroMemory(pbIoBuffer
+ Sizes
.cbHeader
, Sizes
.cbMaximumMessage
);
1040 memcpy(pbIoBuffer
+ Sizes
.cbHeader
, buff
+ sent
, toSend
);
1041 DWORD cbData
= EncryptSend(&m_SendSock
, hContext
, pbIoBuffer
, Sizes
);
1042 if (cbData
== SOCKET_ERROR
|| cbData
== 0)
1047 else if (m_SendSock
.Send ( buff
, size
) != size
)
1049 m_csLastError
.Format ( _T("Socket send data failed") );
1055 // ÀûÓÃsocket·¢ËÍÊý¾Ý£¬Êý¾Ý³¤¶È²»Äܳ¬¹ý10M
1056 BOOL
CHwSMTP::Send(const CString
&str
)
1058 CMultiByteString
cbsData ( str
);
1060 TRACE ( _T("Send : %s\r\n"), cbsData
.GetBuffer() );
1061 return SendBuffer(cbsData
.GetBuffer(), cbsData
.GetLength());
1064 BOOL
CHwSMTP::SendEmail()
1067 char szLocalHostName
[64] = {0};
1068 gethostname ( (char*)szLocalHostName
, sizeof(szLocalHostName
) );
1070 // make sure helo hostname can be interpreted as a FQDN
1071 CString
hostname(GetCompatibleString(szLocalHostName
, FALSE
));
1072 if (hostname
.Find(_T(".")) == -1)
1073 hostname
+= _T(".local");
1077 str
.Format(_T("HELO %s\r\n"), hostname
);
1082 if ( !GetResponse ( _T("250") ) )
1087 if ( m_bMustAuth
&& !auth() )
1097 if (!SendSubject(hostname
))
1107 if ( !SendAttach() )
1112 if ( !Send ( CString(_T(".\r\n") ) ) ) return FALSE
;
1113 if ( !GetResponse ( _T("250") ) )
1117 if ( HANDLE_IS_VALID(m_SendSock
.m_hSocket
) )
1118 Send ( CString(_T("QUIT\r\n")) );
1119 m_bConnected
= FALSE
;
1124 static CStringA
EncodeBase64(const char * source
, int len
)
1126 int neededLength
= Base64EncodeGetRequiredLength(len
);
1128 if (!Base64Encode((BYTE
*)source
, len
, output
.GetBufferSetLength(neededLength
), &neededLength
))
1130 output
.ReleaseBuffer(0);
1133 output
.ReleaseBuffer(neededLength
);
1137 BOOL
CHwSMTP::auth()
1139 int nResponseCode
= 0;
1140 if ( !Send ( CString(_T("auth login\r\n")) ) ) return FALSE
;
1141 if ( !GetResponse ( _T("334"), &nResponseCode
) ) return FALSE
;
1142 if ( nResponseCode
!= 334 ) // ²»ÐèÒªÑéÖ¤Óû§ÃûºÍÃÜÂë
1145 CMultiByteString
cbsUserName ( m_csUserName
), cbsPasswd ( m_csPasswd
);
1146 CString csBase64_UserName
= GetCompatibleString(EncodeBase64(cbsUserName
.GetBuffer(), cbsUserName
.GetLength()).GetBuffer(0), FALSE
);
1147 CString csBase64_Passwd
= GetCompatibleString(EncodeBase64(cbsPasswd
.GetBuffer(), cbsPasswd
.GetLength()).GetBuffer(0), FALSE
);
1150 str
.Format( _T("%s\r\n"), csBase64_UserName
);
1151 if ( !Send ( str
) )
1154 if ( !GetResponse ( _T("334") ) )
1156 m_csLastError
.Format ( _T("Authentication UserName failed") );
1160 str
.Format(_T("%s\r\n"), csBase64_Passwd
);
1161 if ( !Send ( str
) )
1164 if ( !GetResponse ( _T("235") ) )
1166 m_csLastError
.Format ( _T("Authentication Password failed") );
1173 BOOL
CHwSMTP::SendHead()
1177 GetNameAddress(m_csAddrFrom
,name
,addr
);
1179 str
.Format( _T("MAIL From: <%s>\r\n"), addr
);
1180 if ( !Send ( str
) ) return FALSE
;
1182 if ( !GetResponse ( _T("250") ) ) return FALSE
;
1187 CString one
=m_csAddrTo
.Tokenize(_T(";"),start
);
1193 GetNameAddress(one
,name
,addr
);
1195 str
.Format(_T("RCPT TO: <%s>\r\n"), addr
);
1196 if ( !Send ( str
) ) return FALSE
;
1197 if ( !GetResponse ( _T("250") ) ) return FALSE
;
1200 if ( !Send ( CString(_T("DATA\r\n") ) ) ) return FALSE
;
1201 if ( !GetResponse ( CString(_T("354") )) ) return FALSE
;
1206 BOOL
CHwSMTP::SendSubject(const CString
&hostname
)
1209 csSubject
+= _T("Date: ");
1210 COleDateTime tNow
= COleDateTime::GetCurrentTime();
1213 csSubject
+= FormatDateTime (tNow
, _T("%a, %d %b %y %H:%M:%S %Z"));
1215 csSubject
+= _T("\r\n");
1216 csSubject
+= FormatString ( _T("From: %s\r\n"), this->m_csAddrFrom
);
1218 if (!m_StrCC
.IsEmpty())
1219 csSubject
+= FormatString ( _T("CC: %s\r\n"), this->m_StrCC
);
1221 if(m_csSender
.IsEmpty())
1222 m_csSender
= this->m_csAddrFrom
;
1224 csSubject
+= FormatString ( _T("Sender: %s\r\n"), this->m_csSender
);
1226 if(this->m_csToList
.IsEmpty())
1227 m_csToList
= m_csReceiverName
;
1229 csSubject
+= FormatString ( _T("To: %s\r\n"), this->m_csToList
);
1233 csSubject
+= FormatString ( _T("Subject: %s\r\n"), m_csSubject
);
1237 HRESULT hr
= CoCreateGuid(&guid
);
1241 if (UuidToString(&guid
, &guidStr
) == RPC_S_OK
)
1243 m_ListID
= (LPTSTR
)guidStr
;
1244 RpcStringFree(&guidStr
);
1247 if (m_ListID
.IsEmpty())
1249 m_csLastError
= _T("Could not generate Message-ID");
1252 csSubject
+= FormatString( _T("Message-ID: <%s@%s>\r\n"), m_ListID
, hostname
);
1254 csSubject
+= FormatString ( _T("X-Mailer: TortoiseGit\r\nMIME-Version: 1.0\r\nContent-Type: %s\r\n\r\n"),
1255 m_csMIMEContentType
);
1257 return Send ( csSubject
);
1260 BOOL
CHwSMTP::SendBody()
1262 CString csBody
, csTemp
;
1264 if ( m_StrAryAttach
.GetSize() > 0 )
1266 csTemp
.Format ( _T("%s\r\n\r\n"), m_csNoMIMEText
);
1269 csTemp
.Format ( _T("--%s\r\n"), m_csPartBoundary
);
1272 csTemp
.Format ( _T("Content-Type: text/plain\r\n%sContent-Transfer-Encoding: UTF-8\r\n\r\n"),
1277 //csTemp.Format ( _T("%s\r\n"), m_csBody );
1279 csBody
+= _T("\r\n");
1281 return Send ( csBody
);
1284 BOOL
CHwSMTP::SendAttach()
1286 int nCountAttach
= (int)m_StrAryAttach
.GetSize();
1287 if ( nCountAttach
< 1 ) return TRUE
;
1289 for ( int i
=0; i
<nCountAttach
; i
++ )
1291 if ( !SendOnAttach ( m_StrAryAttach
.GetAt(i
) ) )
1295 Send(L
"--" + m_csPartBoundary
+ L
"--\r\n");
1300 BOOL
CHwSMTP::SendOnAttach(LPCTSTR lpszFileName
)
1302 ASSERT ( lpszFileName
);
1303 CString csAttach
, csTemp
;
1305 csTemp
= lpszFileName
;
1306 CString csShortFileName
= csTemp
.GetBuffer(0) + csTemp
.ReverseFind ( '\\' );
1307 csShortFileName
.TrimLeft ( _T("\\") );
1309 csTemp
.Format ( _T("--%s\r\n"), m_csPartBoundary
);
1312 csTemp
.Format ( _T("Content-Type: application/octet-stream; file=%s\r\n"), csShortFileName
);
1315 csTemp
.Format ( _T("Content-Transfer-Encoding: base64\r\n") );
1318 csTemp
.Format ( _T("Content-Disposition: attachment; filename=%s\r\n\r\n"), csShortFileName
);
1321 DWORD dwFileSize
= hwGetFileAttr(lpszFileName
);
1322 if ( dwFileSize
> 5*1024*1024 )
1324 m_csLastError
.Format ( _T("File [%s] too big. File size is : %s"), lpszFileName
, FormatBytes(dwFileSize
) );
1327 char *pBuf
= new char[dwFileSize
+1];
1330 ::AfxThrowMemoryException ();
1334 if(!Send ( csAttach
))
1344 if ( !file
.Open ( lpszFileName
, CFile::modeRead
) )
1346 m_csLastError
.Format ( _T("Open file [%s] failed"), lpszFileName
);
1350 UINT nFileLen
= file
.Read ( pBuf
, dwFileSize
);
1351 filedata
= EncodeBase64(pBuf
, nFileLen
);
1352 filedata
+= _T("\r\n\r\n");
1354 catch (CFileException
*e
)
1357 m_csLastError
.Format ( _T("Read file [%s] failed"), lpszFileName
);
1362 if(!SendBuffer( filedata
.GetBuffer() ))
1371 //return Send ( csAttach );
1374 CString
CHwSMTP::GetLastErrorText()
1376 return m_csLastError
;
1380 // ½«×Ö·û´® lpszOrg ת»»Îª¶à×Ö½ÚµÄ×Ö·û´®£¬Èç¹û»¹ÒªÊ¹Óöà×Ö·û´®µÄ³¤¶È£¬¿ÉÒÔÓÃÒÔÏ·½Ê½À´Ê¹ÓÃÕâ¸öÀࣺ
1381 // CMultiByteString MultiByteString(_T("UNICODE×Ö·û´®"));
1382 // printf ( "ANSI ×Ö·û´®Îª£º %s£¬ ×Ö·û¸öÊýΪ£º %d £¬ ³¤¶ÈΪ£º %d×Ö½Ú\n", MultiByteString.GetBuffer(), MultiByteString.GetLength(), MultiByteString.GetSize() );
1384 CMultiByteString::CMultiByteString( LPCTSTR lpszOrg
, int nOrgStringEncodeType
/*=STRING_IS_SOFTCODE*/, OUT
char *pOutBuf
/*=NULL*/, int nOutBufSize
/*=0*/ )
1386 m_bNewBuffer
= FALSE
;
1389 m_nCharactersNumber
= 0;
1390 if ( !lpszOrg
) return;
1392 BOOL bOrgIsUnicode
= FALSE
;
1393 if ( nOrgStringEncodeType
== STRING_IS_MULTICHARS
) bOrgIsUnicode
= FALSE
;
1394 else if ( nOrgStringEncodeType
== STRING_IS_UNICODE
) bOrgIsUnicode
= TRUE
;
1398 bOrgIsUnicode
= TRUE
;
1400 bOrgIsUnicode
= FALSE
;
1404 if ( bOrgIsUnicode
)
1406 m_nCharactersNumber
= (int)wcslen((WCHAR
*)lpszOrg
);
1407 m_nDataSize
= (m_nCharactersNumber
+ 1) * sizeof(WCHAR
);
1411 m_nCharactersNumber
= (int)strlen((char*)lpszOrg
);
1412 m_nDataSize
= (m_nCharactersNumber
+ 1) * sizeof(char);
1415 if ( pOutBuf
&& nOutBufSize
> 0 )
1417 m_pszData
= pOutBuf
;
1418 m_nDataSize
= nOutBufSize
;
1422 m_pszData
= (char*)new BYTE
[m_nDataSize
];
1425 ::AfxThrowMemoryException ();
1428 m_bNewBuffer
= TRUE
;
1430 memset ( m_pszData
, 0, m_nDataSize
);
1432 if ( bOrgIsUnicode
)
1434 m_nCharactersNumber
= WideCharToMultiByte(CP_UTF8
, 0, (LPCWSTR
)lpszOrg
, m_nCharactersNumber
, (LPSTR
)m_pszData
, m_nDataSize
/ sizeof(char) - 1, NULL
, NULL
);
1435 if ( m_nCharactersNumber
< 1 ) m_nCharactersNumber
= (int)strlen ( m_pszData
);
1439 m_nCharactersNumber
= __min ( m_nCharactersNumber
, (int)(m_nDataSize
/sizeof(char)-1) );
1440 strncpy ( m_pszData
, (const char*)lpszOrg
, m_nCharactersNumber
);
1441 m_nCharactersNumber
= (int)strlen ( m_pszData
);
1443 m_nDataSize
= ( m_nCharactersNumber
+ 1 ) * sizeof(char);
1446 CMultiByteString::~CMultiByteString ()
1448 if ( m_bNewBuffer
&& m_pszData
)
1454 CString
GetCompatibleString ( LPVOID lpszOrg
, BOOL bOrgIsUnicode
, int nOrgLength
/*=-1*/ )
1456 if ( !lpszOrg
) return _T("");
1461 if ( bOrgIsUnicode
)
1463 if ( nOrgLength
> 0 )
1465 WCHAR
*szRet
= new WCHAR
[nOrgLength
+1];
1466 if ( !szRet
) return _T("");
1467 memset ( szRet
, 0, (nOrgLength
+1)*sizeof(WCHAR
) );
1468 memcpy ( szRet
, lpszOrg
, nOrgLength
*sizeof(WCHAR
) );
1469 CString csRet
= szRet
;
1473 else if ( nOrgLength
== 0 )
1476 return (LPCTSTR
)lpszOrg
;
1479 if ( nOrgLength
< 0 )
1480 nOrgLength
= (int)strlen((const char*)lpszOrg
);
1481 int nWideCount
= nOrgLength
+ 1;
1482 WCHAR
*wchar
= new WCHAR
[nWideCount
];
1483 if ( !wchar
) return _T("");
1484 memset ( wchar
, 0, nWideCount
*sizeof(WCHAR
) );
1485 ::MultiByteToWideChar(CP_UTF8
, 0, (LPCSTR
)lpszOrg
, nOrgLength
, wchar
, nWideCount
);
1486 CString csRet
= wchar
;
1490 if ( !bOrgIsUnicode
)
1492 if ( nOrgLength
> 0 )
1494 char *szRet
= new char[nOrgLength
+1];
1495 if ( !szRet
) return _T("");
1496 memset ( szRet
, 0, (nOrgLength
+1)*sizeof(char) );
1497 memcpy ( szRet
, lpszOrg
, nOrgLength
*sizeof(char) );
1498 CString csRet
= szRet
;
1502 else if ( nOrgLength
== 0 )
1505 return (LPCTSTR
)lpszOrg
;
1508 if ( nOrgLength
< 0 )
1509 nOrgLength
= (int)wcslen((WCHAR
*)lpszOrg
);
1510 int nMultiByteCount
= nOrgLength
+ 1;
1511 char *szMultiByte
= new char[nMultiByteCount
];
1512 if ( !szMultiByte
) return _T("");
1513 memset ( szMultiByte
, 0, nMultiByteCount
*sizeof(char) );
1514 ::WideCharToMultiByte(CP_UTF8
, 0, (LPCWSTR
)lpszOrg
, nOrgLength
, (LPSTR
)szMultiByte
, nMultiByteCount
, NULL
, NULL
);
1515 CString csRet
= szMultiByte
;
1516 delete[] szMultiByte
;
1529 CString
FormatDateTime (COleDateTime
&DateTime
, LPCTSTR
/*pFormat*/)
1531 // If null, return empty string
1532 if ( DateTime
.GetStatus() == COleDateTime::null
|| DateTime
.GetStatus() == COleDateTime::invalid
)
1536 if (S_OK
!= VarUdateFromDate(DateTime
.m_dt
, 0, &ud
))
1541 TCHAR
*weeks
[]={_T("Sun"),_T("Mon"),_T("Tue"),_T("Wen"),_T("Thu"),_T("Fri"),_T("Sat")};
1542 TCHAR
*month
[]={_T("Jan"),_T("Feb"),_T("Mar"),_T("Apr"),
1543 _T("May"),_T("Jun"),_T("Jul"),_T("Aug"),
1544 _T("Sep"),_T("Oct"),_T("Nov"),_T("Dec")};
1546 TIME_ZONE_INFORMATION stTimeZone
;
1547 GetTimeZoneInformation(&stTimeZone
);
1550 strDate
.Format(_T("%s, %d %s %d %02d:%02d:%02d %c%04d")
1551 ,weeks
[ud
.st
.wDayOfWeek
],
1552 ud
.st
.wDay
,month
[ud
.st
.wMonth
-1],ud
.st
.wYear
,ud
.st
.wHour
,
1553 ud
.st
.wMinute
,ud
.st
.wSecond
,
1554 stTimeZone
.Bias
>0?_T('-'):_T('+'),
1555 abs(stTimeZone
.Bias
*10/6)
1560 CString
FormatString ( LPCTSTR lpszStr
, ... )
1563 for ( int nBufCount
= 1024; nBufCount
<5*1024*1024; nBufCount
+= 1024 )
1565 buf
= new TCHAR
[nBufCount
];
1568 ::AfxThrowMemoryException ();
1571 memset ( buf
, 0, nBufCount
*sizeof(TCHAR
) );
1574 va_start (va
, lpszStr
);
1575 int nLen
= _vsnprintf_hw ((TCHAR
*)buf
, nBufCount
-sizeof(TCHAR
), lpszStr
, va
);
1577 if ( nLen
<= (int)(nBufCount
-sizeof(TCHAR
)) )
1579 delete[] buf
; buf
= NULL
;
1586 CString csMsg
= buf
;
1587 delete[] buf
; buf
= NULL
;
1591 int hwGetFileAttr ( LPCTSTR lpFileName
, OUT CFileStatus
*pFileStatus
/*=NULL*/ )
1593 if ( !lpFileName
|| lstrlen(lpFileName
) < 1 ) return -1;
1595 CFileStatus fileStatus
;
1596 fileStatus
.m_attribute
= 0;
1597 fileStatus
.m_size
= 0;
1598 memset ( fileStatus
.m_szFullName
, 0, sizeof(fileStatus
.m_szFullName
) );
1602 if ( CFile::GetStatus(lpFileName
,fileStatus
) )
1607 CATCH (CFileException
, e
)
1621 pFileStatus
->m_ctime
= fileStatus
.m_ctime
;
1622 pFileStatus
->m_mtime
= fileStatus
.m_mtime
;
1623 pFileStatus
->m_atime
= fileStatus
.m_atime
;
1624 pFileStatus
->m_size
= fileStatus
.m_size
;
1625 pFileStatus
->m_attribute
= fileStatus
.m_attribute
;
1626 lstrcpy ( pFileStatus
->m_szFullName
, fileStatus
.m_szFullName
);
1630 return (int)fileStatus
.m_size
;
1633 CString
FormatBytes ( double fBytesNum
, BOOL bShowUnit
/*=TRUE*/, int nFlag
/*=0*/ )
1638 if ( fBytesNum
>= 1024.0 && fBytesNum
< 1024.0*1024.0 )
1639 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ 1024.0, bShowUnit
?_T(" K"):_T("") );
1640 else if ( fBytesNum
>= 1024.0*1024.0 && fBytesNum
< 1024.0*1024.0*1024.0 )
1641 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ (1024.0*1024.0), bShowUnit
?_T(" M"):_T("") );
1642 else if ( fBytesNum
>= 1024.0*1024.0*1024.0 )
1643 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ (1024.0*1024.0*1024.0), bShowUnit
?_T(" G"):_T("") );
1645 csRes
.Format ( _T("%.2f%s"), fBytesNum
, bShowUnit
?_T(" B"):_T("") );
1647 else if ( nFlag
== 1 )
1649 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ 1024.0, bShowUnit
?_T(" K"):_T("") );
1651 else if ( nFlag
== 2 )
1653 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ (1024.0*1024.0), bShowUnit
?_T(" M"):_T("") );
1655 else if ( nFlag
== 3 )
1657 csRes
.Format ( _T("%.2f%s"), fBytesNum
/ (1024.0*1024.0*1024.0), bShowUnit
?_T(" G"):_T("") );