Show copy icon in log message context menu
[TortoiseGit.git] / src / Utils / HwSMTP.cpp
blob37a83010643d6b290992f319ae8e2ad924184ac2
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>
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 //////////////////////////////////////////////////////////////////////
30 CHwSMTP::CHwSMTP () :
31 m_bConnected ( FALSE ),
32 m_nSmtpSrvPort ( 25 ),
33 m_bMustAuth ( TRUE )
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");
40 hContext = nullptr;
41 hCreds = nullptr;
42 pbIoBuffer = nullptr;
43 cbIoBufferLength = 0;
45 m_iSecurityLevel = none;
47 SecureZeroMemory(&Sizes, sizeof(SecPkgContext_StreamSizes));
49 AfxSocketInit();
52 CHwSMTP::~CHwSMTP()
56 void CHwSMTP::GetNameAddress(CString &in, CString &name,CString &address)
58 int start,end;
59 start=in.Find(_T('<'));
60 end=in.Find(_T('>'));
62 if(start >=0 && end >=0)
64 name=in.Left(start);
65 address=in.Mid(start+1,end-start-1);
67 else
68 address=in;
71 CString CHwSMTP::GetServerAddress(CString &email)
73 CString str;
74 int start,end;
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);
83 else
85 str=email;
88 start = str.Find(_T('@'));
89 return str.Mid(start+1);
93 BOOL CHwSMTP::SendSpeedEmail
95 LPCTSTR lpszAddrFrom,
96 LPCTSTR lpszAddrTo,
97 LPCTSTR lpszSubject,
98 LPCTSTR lpszBody,
99 LPCTSTR lpszCharSet,
100 CStringArray *pStrAryAttach,
101 LPCTSTR pStrAryCC,
102 LPCTSTR pSend
106 BOOL ret=true;
107 CString To;
108 To += GET_SAFE_STRING(lpszAddrTo);
109 To += _T(";");
110 To += GET_SAFE_STRING(pStrAryCC);
112 std::map<CString,std::vector<CString>> Address;
114 int start = 0;
115 while( start >= 0 )
117 CString one= To.Tokenize(_T(";"),start);
118 one=one.Trim();
119 if(one.IsEmpty())
120 continue;
122 CString addr;
123 addr = GetServerAddress(one);
124 if(addr.IsEmpty())
125 continue;
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;
135 PDNS_RECORD pNext;
137 DNS_STATUS status =
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.
142 NULL
144 if (status)
146 m_csLastError.Format(_T("DNS query failed %d"), status);
147 ret = false;
148 continue;
151 CString to;
152 to.Empty();
153 for (size_t i = 0; i < itr1->second.size(); ++i)
155 to+=itr1->second[i];
156 to+=_T(";");
158 if(to.IsEmpty())
159 continue;
161 pNext=pDnsRecord;
162 while(pNext)
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))
168 break;
169 pNext=pNext->pNext;
171 if(pNext == NULL)
172 ret = false;
174 if (pDnsRecord)
175 DnsRecordListFree(pDnsRecord,DnsFreeRecordList);
178 return ret;
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;
186 TimeStamp tsExpiry;
187 SECURITY_STATUS scRet;
188 PUCHAR IoBuffer;
189 BOOL fDoRead;
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;
201 cbIoBuffer = 0;
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.
211 if (fDoRead)
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;
218 break;
220 else if (cbData == 0)
222 // printf("**** Server unexpectedly disconnected\n");
223 scRet = SEC_E_INTERNAL_ERROR;
224 break;
226 // printf("%d bytes of handshake data received\n", cbData);
227 cbIoBuffer += cbData;
229 else
230 fDoRead = TRUE;
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
251 // garbage later.
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 );
315 else
317 pExtraData->pvBuffer = nullptr;
318 pExtraData->cbBuffer = 0;
319 pExtraData->BufferType = SECBUFFER_EMPTY;
321 break; // Bail out to quit
324 // Check for fatal error.
325 if (FAILED(scRet))
327 // printf("**** Error 0x%x returned by InitializeSecurityContext (2)\n", scRet);
328 break;
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);
345 // Go around again.
346 fDoRead = FALSE;
347 scRet = SEC_I_CONTINUE_NEEDED;
348 continue;
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;
357 else
358 cbIoBuffer = 0;
361 // Delete the security context in the case of a fatal error.
362 if (FAILED(scRet))
363 g_pSSPI->DeleteSecurityContext(phContext);
364 delete[] IoBuffer;
366 return scRet;
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;
374 TimeStamp tsExpiry;
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);
394 return 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)
419 TimeStamp tsExpiry;
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;
432 if (aiKeyExch)
433 rgbSupportedAlgs[cSupportedAlgs++] = aiKeyExch;
435 if (cSupportedAlgs)
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)
463 return Status;
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;
473 DWORD Status;
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;
481 goto cleanup;
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);
495 goto cleanup;
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();
515 goto cleanup;
518 if (PolicyStatus.dwError)
520 Status = PolicyStatus.dwError;
521 goto cleanup;
524 Status = SEC_E_OK;
526 cleanup:
527 if (pChainContext)
528 CertFreeCertificateChain(pChainContext);
530 return Status;
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];
540 DWORD cbMessage;
541 PBYTE pbMessage;
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.
568 if (FAILED(scRet))
570 // printf("**** Error 0x%x returned by EncryptMessage\n", scRet);
571 return 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)
581 PBYTE pbMessage;
582 DWORD dwType, dwSSPIFlags, dwSSPIOutFlags, cbMessage, cbData, Status;
583 SecBufferDesc OutBuffer;
584 SecBuffer OutBuffers[1];
585 TimeStamp tsExpiry;
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);
598 if (FAILED(Status))
600 // printf("**** Error 0x%x returned by ApplyControlToken\n", Status);
601 goto cleanup;
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);
617 if (FAILED(Status))
619 // printf("**** Error 0x%x returned by InitializeSecurityContext\n", Status);
620 goto cleanup;
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();
633 goto cleanup;
635 // printf("Sending Close Notify\n");
636 // printf("%d bytes of handshake data sent\n", cbData);
637 g_pSSPI->FreeContextBuffer(pbMessage); // Free output buffer.
640 cleanup:
641 g_pSSPI->DeleteSecurityContext(phContext); // Free the security context.
642 Socket->Close();
644 return Status;
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;
664 PBYTE buff;
666 // Read data from server until done.
667 cbIoBuffer = 0;
668 scRet = 0;
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;
678 break;
680 else if (cbData == 0) // Server disconnected.
682 if (cbIoBuffer)
684 // printf("**** Server unexpectedly disconnected\n");
685 scRet = SEC_E_INTERNAL_ERROR;
686 return scRet;
688 else
689 break; // All Done
691 else // success
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)
715 return scRet;
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.
729 if (pDataBuffer)
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
737 buff[length] = 0;
738 break;
743 // Move any "extra" data to the input buffer.
744 if (pExtraBuffer)
746 MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
747 cbIoBuffer = pExtraBuffer->cbBuffer;
749 else
750 cbIoBuffer = 0;
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)
758 return scRet;
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
768 return SEC_E_OK;
771 BOOL CHwSMTP::SendEmail (
772 LPCTSTR lpszSmtpSrvHost,
773 LPCTSTR lpszUserName,
774 LPCTSTR lpszPasswd,
775 BOOL bMustAuth,
776 LPCTSTR lpszAddrFrom,
777 LPCTSTR lpszAddrTo,
778 LPCTSTR lpszSubject,
779 LPCTSTR lpszBody,
780 LPCTSTR lpszCharSet, // ×Ö·û¼¯ÀàÐÍ£¬ÀýÈ磺·±ÌåÖÐÎÄÕâÀïÓ¦ÊäÈë"big5"£¬¼òÌåÖÐÎÄʱÊäÈë"gb2312"
781 CStringArray *pStrAryAttach/*=NULL*/,
782 LPCTSTR pStrAryCC/*=NULL*/,
783 UINT nSmtpSrvPort,/*=25*/
784 LPCTSTR pSender,
785 LPCTSTR pToList,
786 DWORD secLevel
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!") );
798 return FALSE;
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!") );
806 return FALSE;
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 );
824 if (
825 m_csAddrFrom.GetLength() <= 0 || m_csAddrTo.GetLength() <= 0
828 m_csLastError.Format ( _T("Parameter Error!") );
829 return FALSE;
832 if ( pStrAryAttach )
834 m_StrAryAttach.Append ( *pStrAryAttach );
836 if ( m_StrAryAttach.GetSize() < 1 )
837 m_csMIMEContentType = FormatString ( _T( "text/plain; %s" ), m_csCharSet);
839 // ´´½¨Socket
840 m_SendSock.Close();
841 if ( !m_SendSock.Create () )
843 //int nResult = GetLastError();
844 m_csLastError.Format ( _T("Create socket failed!") );
845 return FALSE;
848 switch (secLevel)
850 case 1:
851 m_iSecurityLevel = want_tls;
852 break;
853 case 2:
854 m_iSecurityLevel = ssl;
855 break;
856 default:
857 m_iSecurityLevel = none;
860 // Á¬½Óµ½·þÎñÆ÷
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() );
865 return FALSE;
868 if (m_iSecurityLevel == want_tls) {
869 if (!GetResponse(_T("220")))
870 return FALSE;
871 m_bConnected = TRUE;
872 Send(L"STARTTLS\n");
873 if (!GetResponse(_T("220")))
874 return FALSE;
875 m_iSecurityLevel = tls_established;
878 BOOL ret = FALSE;
880 SecBuffer ExtraData;
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);
900 return FALSE;
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);
908 return FALSE;
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);
914 if (Status)
916 m_csLastError = CFormatMessageWrapper(Status);
917 goto cleanup;
920 Status = VerifyServerCertificate( pRemoteCertContext, m_csSmtpSrvHost.GetBuffer(), 0 );
921 if (Status)
923 m_csLastError = CFormatMessageWrapper(Status);
924 goto cleanup;
927 CertFreeCertificateContext(pRemoteCertContext);
929 Status = g_pSSPI->QueryContextAttributes(hContext, SECPKG_ATTR_STREAM_SIZES, &Sizes);
930 if (Status)
932 m_csLastError = CFormatMessageWrapper(Status);
933 goto cleanup;
936 // Create a buffer.
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");
943 goto cleanup;
947 if (m_iSecurityLevel <= ssl)
949 if (!GetResponse(_T("220")))
950 goto cleanup;
951 m_bConnected = TRUE;
954 ret = SendEmail();
956 cleanup:
957 if (m_iSecurityLevel >= ssl)
959 if (hContext && hCreds)
960 DisconnectFromServer(&m_SendSock, hCreds, hContext);
961 if (pbIoBuffer)
963 LocalFree(pbIoBuffer);
964 pbIoBuffer = nullptr;
965 cbIoBufferLength = 0;
967 if (hContext)
969 g_pSSPI->DeleteSecurityContext(hContext);
970 hContext = nullptr;
972 if (hCreds)
974 g_pSSPI->FreeCredentialsHandle(hCreds);
975 hCreds = nullptr;
977 g_pSSPI = nullptr;
979 else
980 m_SendSock.Close();
982 return ret;
985 BOOL CHwSMTP::GetResponse ( LPCTSTR lpszVerifyCode, int *pnCode/*=NULL*/)
987 if ( !lpszVerifyCode || lstrlen(lpszVerifyCode) < 1 )
988 return FALSE;
990 SECURITY_STATUS scRet = SEC_E_OK;
992 char szRecvBuf[1024] = {0};
993 int nRet = 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);
1002 else
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") );
1008 return FALSE;
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) );
1018 return FALSE;
1021 return TRUE;
1023 BOOL CHwSMTP::SendBuffer(char *buff,int size)
1025 if(size<0)
1026 size=(int)strlen(buff);
1027 if ( !m_bConnected )
1029 m_csLastError.Format ( _T("Didn't connect") );
1030 return FALSE;
1033 if (m_iSecurityLevel >= ssl)
1035 int sent = 0;
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)
1043 return FALSE;
1044 sent += toSend;
1047 else if (m_SendSock.Send ( buff, size ) != size)
1049 m_csLastError.Format ( _T("Socket send data failed") );
1050 return FALSE;
1053 return TRUE;
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()
1066 BOOL bRet = TRUE;
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");
1075 // hello£¬ÎÕÊÖ
1076 CString str;
1077 str.Format(_T("HELO %s\r\n"), hostname);
1078 if ( !Send ( str ))
1080 return FALSE;
1082 if ( !GetResponse ( _T("250") ) )
1084 return FALSE;
1086 // Éí·ÝÑéÖ¤
1087 if ( m_bMustAuth && !auth() )
1089 return FALSE;
1091 // ·¢ËÍÓʼþÍ·
1092 if ( !SendHead() )
1094 return FALSE;
1096 // ·¢ËÍÓʼþÖ÷Ìâ
1097 if (!SendSubject(hostname))
1099 return FALSE;
1101 // ·¢ËÍÓʼþÕýÎÄ
1102 if ( !SendBody() )
1104 return FALSE;
1106 // ·¢Ë͸½¼þ
1107 if ( !SendAttach() )
1109 return FALSE;
1111 // ½áÊøÓʼþÕýÎÄ
1112 if ( !Send ( CString(_T(".\r\n") ) ) ) return FALSE;
1113 if ( !GetResponse ( _T("250") ) )
1114 return FALSE;
1116 // Í˳ö·¢ËÍ
1117 if ( HANDLE_IS_VALID(m_SendSock.m_hSocket) )
1118 Send ( CString(_T("QUIT\r\n")) );
1119 m_bConnected = FALSE;
1121 return bRet;
1124 static CStringA EncodeBase64(const char * source, int len)
1126 int neededLength = Base64EncodeGetRequiredLength(len);
1127 CStringA output;
1128 if (!Base64Encode((BYTE *)source, len, output.GetBufferSetLength(neededLength), &neededLength))
1130 output.ReleaseBuffer(0);
1131 return output;
1133 output.ReleaseBuffer(neededLength);
1134 return output;
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 ) // ²»ÐèÒªÑéÖ¤Óû§ÃûºÍÃÜÂë
1143 return TRUE;
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);
1149 CString str;
1150 str.Format( _T("%s\r\n"), csBase64_UserName );
1151 if ( !Send ( str ) )
1152 return FALSE;
1154 if ( !GetResponse ( _T("334") ) )
1156 m_csLastError.Format ( _T("Authentication UserName failed") );
1157 return FALSE;
1160 str.Format(_T("%s\r\n"), csBase64_Passwd );
1161 if ( !Send ( str ) )
1162 return FALSE;
1164 if ( !GetResponse ( _T("235") ) )
1166 m_csLastError.Format ( _T("Authentication Password failed") );
1167 return FALSE;
1170 return TRUE;
1173 BOOL CHwSMTP::SendHead()
1175 CString str;
1176 CString name,addr;
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;
1184 int start=0;
1185 while(start>=0)
1187 CString one=m_csAddrTo.Tokenize(_T(";"),start);
1188 one=one.Trim();
1189 if(one.IsEmpty())
1190 continue;
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;
1203 return TRUE;
1206 BOOL CHwSMTP::SendSubject(const CString &hostname)
1208 CString csSubject;
1209 csSubject += _T("Date: ");
1210 COleDateTime tNow = COleDateTime::GetCurrentTime();
1211 if ( tNow > 1 )
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);
1231 CString m_csToList;
1233 csSubject += FormatString ( _T("Subject: %s\r\n"), m_csSubject );
1235 CString m_ListID;
1236 GUID guid;
1237 HRESULT hr = CoCreateGuid(&guid);
1238 if (hr == S_OK)
1240 RPC_WSTR guidStr;
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");
1250 return FALSE;
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 );
1267 csBody += csTemp;
1269 csTemp.Format ( _T("--%s\r\n"), m_csPartBoundary );
1270 csBody += csTemp;
1272 csTemp.Format ( _T("Content-Type: text/plain\r\n%sContent-Transfer-Encoding: UTF-8\r\n\r\n"),
1273 m_csCharSet );
1274 csBody += csTemp;
1277 //csTemp.Format ( _T("%s\r\n"), m_csBody );
1278 csBody += 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) ) )
1292 return FALSE;
1295 Send(L"--" + m_csPartBoundary + L"--\r\n");
1297 return TRUE;
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 );
1310 csAttach += csTemp;
1312 csTemp.Format ( _T("Content-Type: application/octet-stream; file=%s\r\n"), csShortFileName );
1313 csAttach += csTemp;
1315 csTemp.Format ( _T("Content-Transfer-Encoding: base64\r\n") );
1316 csAttach += csTemp;
1318 csTemp.Format ( _T("Content-Disposition: attachment; filename=%s\r\n\r\n"), csShortFileName );
1319 csAttach += csTemp;
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) );
1325 return FALSE;
1327 char *pBuf = new char[dwFileSize+1];
1328 if ( !pBuf )
1330 ::AfxThrowMemoryException ();
1331 return FALSE;
1334 if(!Send ( csAttach ))
1336 delete[] pBuf;
1337 return FALSE;
1340 CFile file;
1341 CStringA filedata;
1344 if ( !file.Open ( lpszFileName, CFile::modeRead ) )
1346 m_csLastError.Format ( _T("Open file [%s] failed"), lpszFileName );
1347 delete[] pBuf;
1348 return FALSE;
1350 UINT nFileLen = file.Read ( pBuf, dwFileSize );
1351 filedata = EncodeBase64(pBuf, nFileLen);
1352 filedata += _T("\r\n\r\n");
1354 catch (CFileException *e)
1356 e->Delete();
1357 m_csLastError.Format ( _T("Read file [%s] failed"), lpszFileName );
1358 delete[] pBuf;
1359 return FALSE;
1362 if(!SendBuffer( filedata.GetBuffer() ))
1364 delete[] pBuf;
1365 return FALSE;
1368 delete[] pBuf;
1370 return TRUE;
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;
1387 m_pszData = NULL;
1388 m_nDataSize = 0;
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;
1395 else
1397 #ifdef UNICODE
1398 bOrgIsUnicode = TRUE;
1399 #else
1400 bOrgIsUnicode = FALSE;
1401 #endif
1404 if ( bOrgIsUnicode )
1406 m_nCharactersNumber = (int)wcslen((WCHAR*)lpszOrg);
1407 m_nDataSize = (m_nCharactersNumber + 1) * sizeof(WCHAR);
1409 else
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;
1420 else
1422 m_pszData = (char*)new BYTE[m_nDataSize];
1423 if ( !m_pszData )
1425 ::AfxThrowMemoryException ();
1426 return;
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 );
1437 else
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 )
1450 delete[] m_pszData;
1454 CString GetCompatibleString ( LPVOID lpszOrg, BOOL bOrgIsUnicode, int nOrgLength/*=-1*/ )
1456 if ( !lpszOrg ) return _T("");
1460 #ifdef UNICODE
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;
1470 delete[] szRet;
1471 return csRet;
1473 else if ( nOrgLength == 0 )
1474 return _T("");
1475 else
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;
1487 delete[] wchar;
1488 return csRet;
1489 #else
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;
1499 delete[] szRet;
1500 return csRet;
1502 else if ( nOrgLength == 0 )
1503 return _T("");
1504 else
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;
1517 return csRet;
1518 #endif
1520 CATCH_ALL(e)
1522 THROW_LAST ();
1524 END_CATCH_ALL
1526 return _T("");
1529 CString FormatDateTime (COleDateTime &DateTime, LPCTSTR /*pFormat*/)
1531 // If null, return empty string
1532 if ( DateTime.GetStatus() == COleDateTime::null || DateTime.GetStatus() == COleDateTime::invalid )
1533 return _T("");
1535 UDATE ud;
1536 if (S_OK != VarUdateFromDate(DateTime.m_dt, 0, &ud))
1538 return _T("");
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);
1549 CString strDate;
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)
1557 return strDate;
1560 CString FormatString ( LPCTSTR lpszStr, ... )
1562 TCHAR *buf = NULL;
1563 for ( int nBufCount = 1024; nBufCount<5*1024*1024; nBufCount += 1024 )
1565 buf = new TCHAR[nBufCount];
1566 if ( !buf )
1568 ::AfxThrowMemoryException ();
1569 return _T("");
1571 memset ( buf, 0, nBufCount*sizeof(TCHAR) );
1573 va_list va;
1574 va_start (va, lpszStr);
1575 int nLen = _vsnprintf_hw ((TCHAR*)buf, nBufCount-sizeof(TCHAR), lpszStr, va);
1576 va_end(va);
1577 if ( nLen <= (int)(nBufCount-sizeof(TCHAR)) )
1578 break;
1579 delete[] buf; buf = NULL;
1581 if ( !buf )
1583 return _T("");
1586 CString csMsg = buf;
1587 delete[] buf; buf = NULL;
1588 return csMsg;
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) );
1599 BOOL bRet = FALSE;
1602 if ( CFile::GetStatus(lpFileName,fileStatus) )
1604 bRet = TRUE;
1607 CATCH (CFileException, e)
1609 ASSERT ( FALSE );
1610 bRet = FALSE;
1612 CATCH_ALL(e)
1614 ASSERT ( FALSE );
1615 bRet = FALSE;
1617 END_CATCH_ALL;
1619 if ( pFileStatus )
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*/ )
1635 CString csRes;
1636 if ( 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("") );
1644 else
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("") );
1660 return csRes;