CommitDlg: Update empty file list message
[TortoiseGit.git] / src / Utils / HwSMTP.cpp
blob1064837681c61364cfcee700a6d0a23a6f52fea9
1 // HwSMTP.cpp: implementation of the CHwSMTP class.
2 //
3 // Schannel/SSPI implementation based on http://www.coastrd.com/c-schannel-smtp
4 //
5 //////////////////////////////////////////////////////////////////////
7 #include "stdafx.h"
8 #include "afxstr.h"
9 #include "HwSMTP.h"
10 #include "SpeedPostEmail.h"
11 #include "Windns.h"
12 #include <Afxmt.h>
13 #include "FormatMessageWrapper.h"
14 #include <atlenc.h>
15 #include "AppUtils.h"
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 //////////////////////////////////////////////////////////////////////
31 CHwSMTP::CHwSMTP () :
32 m_bConnected ( FALSE ),
33 m_nSmtpSrvPort ( 25 ),
34 m_bMustAuth ( TRUE )
36 m_csPartBoundary = _T( "WC_MAIL_PaRt_BoUnDaRy_05151998" );
37 m_csMIMEContentType = FormatString ( _T( "multipart/mixed; boundary=%s" ), (LPCTSTR)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");
41 hContext = nullptr;
42 hCreds = nullptr;
43 pbIoBuffer = nullptr;
44 cbIoBufferLength = 0;
46 m_iSecurityLevel = none;
48 SecureZeroMemory(&Sizes, sizeof(SecPkgContext_StreamSizes));
50 AfxSocketInit();
53 CHwSMTP::~CHwSMTP()
57 void CHwSMTP::GetNameAddress(CString &in, CString &name,CString &address)
59 int start,end;
60 start=in.Find(_T('<'));
61 end=in.Find(_T('>'));
63 if(start >=0 && end >=0)
65 name=in.Left(start);
66 address=in.Mid(start+1,end-start-1);
68 else
69 address=in;
72 CString CHwSMTP::GetServerAddress(CString &email)
74 CString str;
75 int start,end;
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);
84 else
86 str=email;
89 start = str.Find(_T('@'));
90 return str.Mid(start+1);
94 BOOL CHwSMTP::SendSpeedEmail
96 LPCTSTR lpszAddrFrom,
97 LPCTSTR lpszAddrTo,
98 LPCTSTR lpszSubject,
99 LPCTSTR lpszBody,
100 LPCTSTR lpszCharSet,
101 CStringArray *pStrAryAttach,
102 LPCTSTR pStrAryCC,
103 LPCTSTR pSend
107 BOOL ret=true;
108 CString To;
109 To += GET_SAFE_STRING(lpszAddrTo);
110 To += _T(";");
111 To += GET_SAFE_STRING(pStrAryCC);
113 std::map<CString,std::vector<CString>> Address;
115 int start = 0;
116 while( start >= 0 )
118 CString one= To.Tokenize(_T(";"),start);
119 one=one.Trim();
120 if(one.IsEmpty())
121 continue;
123 CString addr;
124 addr = GetServerAddress(one);
125 if(addr.IsEmpty())
126 continue;
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;
136 PDNS_RECORD pNext;
138 DNS_STATUS status =
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.
143 NULL
145 if (status)
147 m_csLastError.Format(_T("DNS query failed %d"), status);
148 ret = false;
149 continue;
152 CString to;
153 to.Empty();
154 for (size_t i = 0; i < itr1->second.size(); ++i)
156 to+=itr1->second[i];
157 to+=_T(";");
159 if(to.IsEmpty())
160 continue;
162 pNext=pDnsRecord;
163 while(pNext)
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))
169 break;
170 pNext=pNext->pNext;
172 if(pNext == NULL)
173 ret = false;
175 if (pDnsRecord)
176 DnsRecordListFree(pDnsRecord,DnsFreeRecordList);
179 return ret;
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;
187 TimeStamp tsExpiry;
188 SECURITY_STATUS scRet;
189 PUCHAR IoBuffer;
190 BOOL fDoRead;
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;
202 cbIoBuffer = 0;
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.
212 if (fDoRead)
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;
219 break;
221 else if (cbData == 0)
223 // printf("**** Server unexpectedly disconnected\n");
224 scRet = SEC_E_INTERNAL_ERROR;
225 break;
227 // printf("%d bytes of handshake data received\n", cbData);
228 cbIoBuffer += cbData;
230 else
231 fDoRead = TRUE;
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
252 // garbage later.
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 );
316 else
318 pExtraData->pvBuffer = nullptr;
319 pExtraData->cbBuffer = 0;
320 pExtraData->BufferType = SECBUFFER_EMPTY;
322 break; // Bail out to quit
325 // Check for fatal error.
326 if (FAILED(scRet))
328 // printf("**** Error 0x%x returned by InitializeSecurityContext (2)\n", scRet);
329 break;
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);
346 // Go around again.
347 fDoRead = FALSE;
348 scRet = SEC_I_CONTINUE_NEEDED;
349 continue;
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;
358 else
359 cbIoBuffer = 0;
362 // Delete the security context in the case of a fatal error.
363 if (FAILED(scRet))
364 g_pSSPI->DeleteSecurityContext(phContext);
365 delete[] IoBuffer;
367 return scRet;
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;
375 TimeStamp tsExpiry;
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);
395 return 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)
420 TimeStamp tsExpiry;
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;
433 if (aiKeyExch)
434 rgbSupportedAlgs[cSupportedAlgs++] = aiKeyExch;
436 if (cSupportedAlgs)
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)
464 return Status;
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];
474 DWORD cbMessage;
475 PBYTE pbMessage;
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.
502 if (FAILED(scRet))
504 // printf("**** Error 0x%x returned by EncryptMessage\n", scRet);
505 return 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)
515 PBYTE pbMessage;
516 DWORD dwType, dwSSPIFlags, dwSSPIOutFlags, cbMessage, cbData, Status;
517 SecBufferDesc OutBuffer;
518 SecBuffer OutBuffers[1];
519 TimeStamp tsExpiry;
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);
532 if (FAILED(Status))
534 // printf("**** Error 0x%x returned by ApplyControlToken\n", Status);
535 goto cleanup;
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);
551 if (FAILED(Status))
553 // printf("**** Error 0x%x returned by InitializeSecurityContext\n", Status);
554 goto cleanup;
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();
567 goto cleanup;
569 // printf("Sending Close Notify\n");
570 // printf("%d bytes of handshake data sent\n", cbData);
571 g_pSSPI->FreeContextBuffer(pbMessage); // Free output buffer.
574 cleanup:
575 g_pSSPI->DeleteSecurityContext(phContext); // Free the security context.
576 Socket->Close();
578 return Status;
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;
598 PBYTE buff;
600 // Read data from server until done.
601 cbIoBuffer = 0;
602 scRet = 0;
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;
612 break;
614 else if (cbData == 0) // Server disconnected.
616 if (cbIoBuffer)
618 // printf("**** Server unexpectedly disconnected\n");
619 scRet = SEC_E_INTERNAL_ERROR;
620 return scRet;
622 else
623 break; // All Done
625 else // success
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)
649 return scRet;
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.
663 if (pDataBuffer)
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
671 buff[length] = 0;
672 break;
677 // Move any "extra" data to the input buffer.
678 if (pExtraBuffer)
680 MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
681 cbIoBuffer = pExtraBuffer->cbBuffer;
683 else
684 cbIoBuffer = 0;
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)
692 return scRet;
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
702 return SEC_E_OK;
705 BOOL CHwSMTP::SendEmail (
706 LPCTSTR lpszSmtpSrvHost,
707 LPCTSTR lpszUserName,
708 LPCTSTR lpszPasswd,
709 BOOL bMustAuth,
710 LPCTSTR lpszAddrFrom,
711 LPCTSTR lpszAddrTo,
712 LPCTSTR lpszSubject,
713 LPCTSTR lpszBody,
714 LPCTSTR lpszCharSet, // ×Ö·û¼¯ÀàÐÍ£¬ÀýÈ磺·±ÌåÖÐÎÄÕâÀïÓ¦ÊäÈë"big5"£¬¼òÌåÖÐÎÄʱÊäÈë"gb2312"
715 CStringArray *pStrAryAttach/*=NULL*/,
716 LPCTSTR pStrAryCC/*=NULL*/,
717 UINT nSmtpSrvPort,/*=25*/
718 LPCTSTR pSender,
719 LPCTSTR pToList,
720 DWORD secLevel
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!") );
732 return FALSE;
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!") );
740 return FALSE;
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 );
758 if (
759 m_csAddrFrom.GetLength() <= 0 || m_csAddrTo.GetLength() <= 0
762 m_csLastError.Format ( _T("Parameter Error!") );
763 return FALSE;
766 if ( pStrAryAttach )
768 m_StrAryAttach.Append ( *pStrAryAttach );
770 if ( m_StrAryAttach.GetSize() < 1 )
771 m_csMIMEContentType = FormatString(_T("text/plain; %s"), (LPCTSTR)m_csCharSet);
773 // ´´½¨Socket
774 m_SendSock.Close();
775 if ( !m_SendSock.Create () )
777 //int nResult = GetLastError();
778 m_csLastError.Format ( _T("Create socket failed!") );
779 return FALSE;
782 switch (secLevel)
784 case 1:
785 m_iSecurityLevel = want_tls;
786 break;
787 case 2:
788 m_iSecurityLevel = ssl;
789 break;
790 default:
791 m_iSecurityLevel = none;
794 // Á¬½Óµ½·þÎñÆ÷
795 if ( !m_SendSock.Connect ( m_csSmtpSrvHost, m_nSmtpSrvPort ) )
797 m_csLastError.Format(_T("Connect to [%s] failed"), (LPCTSTR)m_csSmtpSrvHost);
798 TRACE ( _T("%d\n"), GetLastError() );
799 return FALSE;
802 if (m_iSecurityLevel == want_tls) {
803 if (!GetResponse(_T("220")))
804 return FALSE;
805 m_bConnected = TRUE;
806 Send(L"STARTTLS\n");
807 if (!GetResponse(_T("220")))
808 return FALSE;
809 m_iSecurityLevel = tls_established;
812 BOOL ret = FALSE;
814 SecBuffer ExtraData;
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);
834 return FALSE;
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);
842 return FALSE;
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);
848 if (Status)
850 m_csLastError = CFormatMessageWrapper(Status);
851 goto cleanup;
854 git_cert_x509 cert;
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.");
862 goto cleanup;
865 CertFreeCertificateContext(pRemoteCertContext);
867 Status = g_pSSPI->QueryContextAttributes(hContext, SECPKG_ATTR_STREAM_SIZES, &Sizes);
868 if (Status)
870 m_csLastError = CFormatMessageWrapper(Status);
871 goto cleanup;
874 // Create a buffer.
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");
881 goto cleanup;
885 if (m_iSecurityLevel <= ssl)
887 if (!GetResponse(_T("220")))
888 goto cleanup;
889 m_bConnected = TRUE;
892 ret = SendEmail();
894 cleanup:
895 if (m_iSecurityLevel >= ssl)
897 if (hContext && hCreds)
898 DisconnectFromServer(&m_SendSock, hCreds, hContext);
899 if (pbIoBuffer)
901 LocalFree(pbIoBuffer);
902 pbIoBuffer = nullptr;
903 cbIoBufferLength = 0;
905 if (hContext)
907 g_pSSPI->DeleteSecurityContext(hContext);
908 hContext = nullptr;
910 if (hCreds)
912 g_pSSPI->FreeCredentialsHandle(hCreds);
913 hCreds = nullptr;
915 g_pSSPI = nullptr;
917 else
918 m_SendSock.Close();
920 return ret;
923 BOOL CHwSMTP::GetResponse ( LPCTSTR lpszVerifyCode, int *pnCode/*=NULL*/)
925 if ( !lpszVerifyCode || lstrlen(lpszVerifyCode) < 1 )
926 return FALSE;
928 SECURITY_STATUS scRet = SEC_E_OK;
930 char szRecvBuf[1024] = {0};
931 int nRet = 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);
940 else
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") );
946 return FALSE;
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"), (LPCTSTR)GetCompatibleString(szRecvBuf, FALSE));
956 return FALSE;
959 return TRUE;
961 BOOL CHwSMTP::SendBuffer(char *buff,int size)
963 if(size<0)
964 size=(int)strlen(buff);
965 if ( !m_bConnected )
967 m_csLastError.Format ( _T("Didn't connect") );
968 return FALSE;
971 if (m_iSecurityLevel >= ssl)
973 int sent = 0;
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)
981 return FALSE;
982 sent += toSend;
985 else if (m_SendSock.Send ( buff, size ) != size)
987 m_csLastError.Format ( _T("Socket send data failed") );
988 return FALSE;
991 return TRUE;
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()
1004 BOOL bRet = TRUE;
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");
1013 // hello£¬ÎÕÊÖ
1014 CString str;
1015 str.Format(_T("HELO %s\r\n"), (LPCTSTR)hostname);
1016 if ( !Send ( str ))
1018 return FALSE;
1020 if ( !GetResponse ( _T("250") ) )
1022 return FALSE;
1024 // Éí·ÝÑéÖ¤
1025 if ( m_bMustAuth && !auth() )
1027 return FALSE;
1029 // ·¢ËÍÓʼþÍ·
1030 if ( !SendHead() )
1032 return FALSE;
1034 // ·¢ËÍÓʼþÖ÷Ìâ
1035 if (!SendSubject(hostname))
1037 return FALSE;
1039 // ·¢ËÍÓʼþÕýÎÄ
1040 if ( !SendBody() )
1042 return FALSE;
1044 // ·¢Ë͸½¼þ
1045 if ( !SendAttach() )
1047 return FALSE;
1049 // ½áÊøÓʼþÕýÎÄ
1050 if ( !Send ( CString(_T(".\r\n") ) ) ) return FALSE;
1051 if ( !GetResponse ( _T("250") ) )
1052 return FALSE;
1054 // Í˳ö·¢ËÍ
1055 if ( HANDLE_IS_VALID(m_SendSock.m_hSocket) )
1056 Send ( CString(_T("QUIT\r\n")) );
1057 m_bConnected = FALSE;
1059 return bRet;
1062 static CStringA EncodeBase64(const char * source, int len)
1064 int neededLength = Base64EncodeGetRequiredLength(len);
1065 CStringA output;
1066 if (!Base64Encode((BYTE *)source, len, output.GetBufferSetLength(neededLength), &neededLength))
1068 output.ReleaseBuffer(0);
1069 return output;
1071 output.ReleaseBuffer(neededLength);
1072 return output;
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 ) // ²»ÐèÒªÑéÖ¤Óû§ÃûºÍÃÜÂë
1081 return TRUE;
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);
1087 CString str;
1088 str.Format(_T("%s\r\n"), (LPCTSTR)csBase64_UserName);
1089 if ( !Send ( str ) )
1090 return FALSE;
1092 if ( !GetResponse ( _T("334") ) )
1094 m_csLastError.Format ( _T("Authentication UserName failed") );
1095 return FALSE;
1098 str.Format(_T("%s\r\n"), (LPCTSTR)csBase64_Passwd);
1099 if ( !Send ( str ) )
1100 return FALSE;
1102 if ( !GetResponse ( _T("235") ) )
1104 m_csLastError.Format ( _T("Authentication Password failed") );
1105 return FALSE;
1108 return TRUE;
1111 BOOL CHwSMTP::SendHead()
1113 CString str;
1114 CString name,addr;
1115 GetNameAddress(m_csAddrFrom,name,addr);
1117 str.Format(_T("MAIL From: <%s>\r\n"), (LPCTSTR)addr);
1118 if ( !Send ( str ) ) return FALSE;
1120 if ( !GetResponse ( _T("250") ) ) return FALSE;
1122 int start=0;
1123 while(start>=0)
1125 CString one=m_csAddrTo.Tokenize(_T(";"),start);
1126 one=one.Trim();
1127 if(one.IsEmpty())
1128 continue;
1131 GetNameAddress(one,name,addr);
1133 str.Format(_T("RCPT TO: <%s>\r\n"), (LPCTSTR)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;
1141 return TRUE;
1144 BOOL CHwSMTP::SendSubject(const CString &hostname)
1146 CString csSubject;
1147 csSubject += _T("Date: ");
1148 COleDateTime tNow = COleDateTime::GetCurrentTime();
1149 if ( tNow > 1 )
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"), (LPCTSTR)m_csAddrFrom);
1156 if (!m_StrCC.IsEmpty())
1157 csSubject += FormatString ( _T("CC: %s\r\n"), (LPCTSTR)m_StrCC);
1159 if(m_csSender.IsEmpty())
1160 m_csSender = this->m_csAddrFrom;
1162 csSubject += FormatString ( _T("Sender: %s\r\n"), (LPCTSTR)m_csSender);
1164 if(this->m_csToList.IsEmpty())
1165 m_csToList = m_csReceiverName;
1167 csSubject += FormatString ( _T("To: %s\r\n"), (LPCTSTR)m_csToList);
1169 csSubject += FormatString ( _T("Subject: %s\r\n"), (LPCTSTR)m_csSubject );
1171 CString m_ListID;
1172 GUID guid;
1173 HRESULT hr = CoCreateGuid(&guid);
1174 if (hr == S_OK)
1176 RPC_WSTR guidStr;
1177 if (UuidToString(&guid, &guidStr) == RPC_S_OK)
1179 m_ListID = (LPTSTR)guidStr;
1180 RpcStringFree(&guidStr);
1183 if (m_ListID.IsEmpty())
1185 m_csLastError = _T("Could not generate Message-ID");
1186 return FALSE;
1188 csSubject += FormatString( _T("Message-ID: <%s@%s>\r\n"), (LPCTSTR)m_ListID, (LPCTSTR)hostname);
1190 csSubject += FormatString ( _T("X-Mailer: TortoiseGit\r\nMIME-Version: 1.0\r\nContent-Type: %s\r\n\r\n"),
1191 (LPCTSTR)m_csMIMEContentType );
1193 return Send ( csSubject );
1196 BOOL CHwSMTP::SendBody()
1198 CString csBody, csTemp;
1200 if ( m_StrAryAttach.GetSize() > 0 )
1202 csTemp.Format ( _T("%s\r\n\r\n"), (LPCTSTR)m_csNoMIMEText );
1203 csBody += csTemp;
1205 csTemp.Format ( _T("--%s\r\n"), (LPCTSTR)m_csPartBoundary );
1206 csBody += csTemp;
1208 csTemp.Format ( _T("Content-Type: text/plain\r\n%sContent-Transfer-Encoding: UTF-8\r\n\r\n"),
1209 m_csCharSet );
1210 csBody += csTemp;
1213 //csTemp.Format ( _T("%s\r\n"), m_csBody );
1214 csBody += m_csBody;
1215 csBody += _T("\r\n");
1217 return Send ( csBody );
1220 BOOL CHwSMTP::SendAttach()
1222 int nCountAttach = (int)m_StrAryAttach.GetSize();
1223 if ( nCountAttach < 1 ) return TRUE;
1225 for ( int i=0; i<nCountAttach; i++ )
1227 if ( !SendOnAttach ( m_StrAryAttach.GetAt(i) ) )
1228 return FALSE;
1231 Send(L"--" + m_csPartBoundary + L"--\r\n");
1233 return TRUE;
1236 BOOL CHwSMTP::SendOnAttach(LPCTSTR lpszFileName)
1238 ASSERT ( lpszFileName );
1239 CString csAttach, csTemp;
1241 csTemp = lpszFileName;
1242 CString csShortFileName = csTemp.GetBuffer(0) + csTemp.ReverseFind ( '\\' );
1243 csShortFileName.TrimLeft ( _T("\\") );
1245 csTemp.Format ( _T("--%s\r\n"), (LPCTSTR)m_csPartBoundary );
1246 csAttach += csTemp;
1248 csTemp.Format ( _T("Content-Type: application/octet-stream; file=%s\r\n"), (LPCTSTR)csShortFileName );
1249 csAttach += csTemp;
1251 csTemp.Format ( _T("Content-Transfer-Encoding: base64\r\n") );
1252 csAttach += csTemp;
1254 csTemp.Format ( _T("Content-Disposition: attachment; filename=%s\r\n\r\n"), (LPCTSTR)csShortFileName );
1255 csAttach += csTemp;
1257 DWORD dwFileSize = hwGetFileAttr(lpszFileName);
1258 if ( dwFileSize > 5*1024*1024 )
1260 m_csLastError.Format ( _T("File [%s] too big. File size is : %s"), lpszFileName, FormatBytes(dwFileSize) );
1261 return FALSE;
1263 char *pBuf = new char[dwFileSize+1];
1264 if ( !pBuf )
1266 ::AfxThrowMemoryException ();
1269 if(!Send ( csAttach ))
1271 delete[] pBuf;
1272 return FALSE;
1275 CFile file;
1276 CStringA filedata;
1279 if ( !file.Open ( lpszFileName, CFile::modeRead ) )
1281 m_csLastError.Format ( _T("Open file [%s] failed"), lpszFileName );
1282 delete[] pBuf;
1283 return FALSE;
1285 UINT nFileLen = file.Read ( pBuf, dwFileSize );
1286 filedata = EncodeBase64(pBuf, nFileLen);
1287 filedata += _T("\r\n\r\n");
1289 catch (CFileException *e)
1291 e->Delete();
1292 m_csLastError.Format ( _T("Read file [%s] failed"), lpszFileName );
1293 delete[] pBuf;
1294 return FALSE;
1297 if(!SendBuffer( filedata.GetBuffer() ))
1299 delete[] pBuf;
1300 return FALSE;
1303 delete[] pBuf;
1305 return TRUE;
1306 //return Send ( csAttach );
1309 CString CHwSMTP::GetLastErrorText()
1311 return m_csLastError;
1315 // ½«×Ö·û´® lpszOrg ת»»Îª¶à×Ö½ÚµÄ×Ö·û´®£¬Èç¹û»¹ÒªÊ¹Óöà×Ö·û´®µÄ³¤¶È£¬¿ÉÒÔÓÃÒÔÏ·½Ê½À´Ê¹ÓÃÕâ¸öÀࣺ
1316 // CMultiByteString MultiByteString(_T("UNICODE×Ö·û´®"));
1317 // printf ( "ANSI ×Ö·û´®Îª£º %s£¬ ×Ö·û¸öÊýΪ£º %d £¬ ³¤¶ÈΪ£º %d×Ö½Ú\n", MultiByteString.GetBuffer(), MultiByteString.GetLength(), MultiByteString.GetSize() );
1319 CMultiByteString::CMultiByteString( LPCTSTR lpszOrg, int nOrgStringEncodeType/*=STRING_IS_SOFTCODE*/, OUT char *pOutBuf/*=NULL*/, int nOutBufSize/*=0*/ )
1321 m_bNewBuffer = FALSE;
1322 m_pszData = NULL;
1323 m_nDataSize = 0;
1324 m_nCharactersNumber = 0;
1325 if ( !lpszOrg ) return;
1327 BOOL bOrgIsUnicode = FALSE;
1328 if ( nOrgStringEncodeType == STRING_IS_MULTICHARS ) bOrgIsUnicode = FALSE;
1329 else if ( nOrgStringEncodeType == STRING_IS_UNICODE ) bOrgIsUnicode = TRUE;
1330 else
1332 #ifdef UNICODE
1333 bOrgIsUnicode = TRUE;
1334 #else
1335 bOrgIsUnicode = FALSE;
1336 #endif
1339 if ( bOrgIsUnicode )
1341 m_nCharactersNumber = (int)wcslen((WCHAR*)lpszOrg);
1342 m_nDataSize = (m_nCharactersNumber + 1) * sizeof(WCHAR);
1344 else
1346 m_nCharactersNumber = (int)strlen((char*)lpszOrg);
1347 m_nDataSize = (m_nCharactersNumber + 1) * sizeof(char);
1350 if ( pOutBuf && nOutBufSize > 0 )
1352 m_pszData = pOutBuf;
1353 m_nDataSize = nOutBufSize;
1355 else
1357 m_pszData = (char*)new BYTE[m_nDataSize];
1358 if ( !m_pszData )
1360 ::AfxThrowMemoryException ();
1361 return;
1363 m_bNewBuffer = TRUE;
1365 memset ( m_pszData, 0, m_nDataSize );
1367 if ( bOrgIsUnicode )
1369 m_nCharactersNumber = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)lpszOrg, m_nCharactersNumber, (LPSTR)m_pszData, m_nDataSize / sizeof(char) - 1, NULL, NULL);
1370 if ( m_nCharactersNumber < 1 ) m_nCharactersNumber = (int)strlen ( m_pszData );
1372 else
1374 m_nCharactersNumber = __min ( m_nCharactersNumber, (int)(m_nDataSize/sizeof(char)-1) );
1375 strncpy_s(m_pszData, m_nCharactersNumber, (const char*)lpszOrg, m_nCharactersNumber);
1376 m_nCharactersNumber = (int)strlen ( m_pszData );
1378 m_nDataSize = ( m_nCharactersNumber + 1 ) * sizeof(char);
1381 CMultiByteString::~CMultiByteString ()
1383 if ( m_bNewBuffer && m_pszData )
1385 delete[] m_pszData;
1389 CString GetCompatibleString ( LPVOID lpszOrg, BOOL bOrgIsUnicode, int nOrgLength/*=-1*/ )
1391 if ( !lpszOrg ) return _T("");
1395 #ifdef UNICODE
1396 if ( bOrgIsUnicode )
1398 if ( nOrgLength > 0 )
1400 WCHAR *szRet = new WCHAR[nOrgLength+1];
1401 if ( !szRet ) return _T("");
1402 memset ( szRet, 0, (nOrgLength+1)*sizeof(WCHAR) );
1403 memcpy ( szRet, lpszOrg, nOrgLength*sizeof(WCHAR) );
1404 CString csRet = szRet;
1405 delete[] szRet;
1406 return csRet;
1408 else if ( nOrgLength == 0 )
1409 return _T("");
1410 else
1411 return (LPCTSTR)lpszOrg;
1414 if ( nOrgLength < 0 )
1415 nOrgLength = (int)strlen((const char*)lpszOrg);
1416 int nWideCount = nOrgLength + 1;
1417 WCHAR *wchar = new WCHAR[nWideCount];
1418 if ( !wchar ) return _T("");
1419 memset ( wchar, 0, nWideCount*sizeof(WCHAR) );
1420 ::MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)lpszOrg, nOrgLength, wchar, nWideCount);
1421 CString csRet = wchar;
1422 delete[] wchar;
1423 return csRet;
1424 #else
1425 if ( !bOrgIsUnicode )
1427 if ( nOrgLength > 0 )
1429 char *szRet = new char[nOrgLength+1];
1430 if ( !szRet ) return _T("");
1431 memset ( szRet, 0, (nOrgLength+1)*sizeof(char) );
1432 memcpy ( szRet, lpszOrg, nOrgLength*sizeof(char) );
1433 CString csRet = szRet;
1434 delete[] szRet;
1435 return csRet;
1437 else if ( nOrgLength == 0 )
1438 return _T("");
1439 else
1440 return (LPCTSTR)lpszOrg;
1443 if ( nOrgLength < 0 )
1444 nOrgLength = (int)wcslen((WCHAR*)lpszOrg);
1445 int nMultiByteCount = nOrgLength + 1;
1446 char *szMultiByte = new char[nMultiByteCount];
1447 if ( !szMultiByte ) return _T("");
1448 memset ( szMultiByte, 0, nMultiByteCount*sizeof(char) );
1449 ::WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)lpszOrg, nOrgLength, (LPSTR)szMultiByte, nMultiByteCount, NULL, NULL);
1450 CString csRet = szMultiByte;
1451 delete[] szMultiByte;
1452 return csRet;
1453 #endif
1455 CATCH_ALL(e)
1457 THROW_LAST ();
1459 END_CATCH_ALL
1461 return _T("");
1464 CString FormatDateTime (COleDateTime &DateTime, LPCTSTR /*pFormat*/)
1466 // If null, return empty string
1467 if ( DateTime.GetStatus() == COleDateTime::null || DateTime.GetStatus() == COleDateTime::invalid )
1468 return _T("");
1470 UDATE ud;
1471 if (S_OK != VarUdateFromDate(DateTime.m_dt, 0, &ud))
1473 return _T("");
1476 TCHAR *weeks[]={_T("Sun"),_T("Mon"),_T("Tue"),_T("Wen"),_T("Thu"),_T("Fri"),_T("Sat")};
1477 TCHAR *month[]={_T("Jan"),_T("Feb"),_T("Mar"),_T("Apr"),
1478 _T("May"),_T("Jun"),_T("Jul"),_T("Aug"),
1479 _T("Sep"),_T("Oct"),_T("Nov"),_T("Dec")};
1481 TIME_ZONE_INFORMATION stTimeZone;
1482 GetTimeZoneInformation(&stTimeZone);
1484 CString strDate;
1485 strDate.Format(_T("%s, %d %s %d %02d:%02d:%02d %c%04d")
1486 ,weeks[ud.st.wDayOfWeek],
1487 ud.st.wDay,month[ud.st.wMonth-1],ud.st.wYear,ud.st.wHour,
1488 ud.st.wMinute,ud.st.wSecond,
1489 stTimeZone.Bias>0?_T('-'):_T('+'),
1490 abs(stTimeZone.Bias*10/6)
1492 return strDate;
1495 CString FormatString ( LPCTSTR lpszStr, ... )
1497 TCHAR *buf = NULL;
1498 for ( int nBufCount = 1024; nBufCount<5*1024*1024; nBufCount += 1024 )
1500 buf = new TCHAR[nBufCount];
1501 if ( !buf )
1503 ::AfxThrowMemoryException ();
1505 memset ( buf, 0, nBufCount*sizeof(TCHAR) );
1507 va_list va;
1508 va_start (va, lpszStr);
1509 int nLen = _vsnprintf_hw((TCHAR*)buf, nBufCount, _TRUNCATE, lpszStr, va);
1510 va_end(va);
1511 if ( nLen <= (int)(nBufCount-sizeof(TCHAR)) )
1512 break;
1513 delete[] buf; buf = NULL;
1515 if ( !buf )
1517 return _T("");
1520 CString csMsg = buf;
1521 delete[] buf; buf = NULL;
1522 return csMsg;
1525 int hwGetFileAttr ( LPCTSTR lpFileName, OUT CFileStatus *pFileStatus/*=NULL*/ )
1527 if ( !lpFileName || lstrlen(lpFileName) < 1 ) return -1;
1529 CFileStatus fileStatus;
1530 fileStatus.m_attribute = 0;
1531 fileStatus.m_size = 0;
1532 memset ( fileStatus.m_szFullName, 0, sizeof(fileStatus.m_szFullName) );
1533 BOOL bRet = FALSE;
1536 if ( CFile::GetStatus(lpFileName,fileStatus) )
1538 bRet = TRUE;
1541 CATCH (CFileException, e)
1543 ASSERT ( FALSE );
1544 bRet = FALSE;
1546 CATCH_ALL(e)
1548 ASSERT ( FALSE );
1549 bRet = FALSE;
1551 END_CATCH_ALL;
1553 if ( pFileStatus )
1555 pFileStatus->m_ctime = fileStatus.m_ctime;
1556 pFileStatus->m_mtime = fileStatus.m_mtime;
1557 pFileStatus->m_atime = fileStatus.m_atime;
1558 pFileStatus->m_size = fileStatus.m_size;
1559 pFileStatus->m_attribute = fileStatus.m_attribute;
1560 lstrcpy ( pFileStatus->m_szFullName, fileStatus.m_szFullName );
1564 return (int)fileStatus.m_size;
1567 CString FormatBytes ( double fBytesNum, BOOL bShowUnit/*=TRUE*/, int nFlag/*=0*/ )
1569 CString csRes;
1570 if ( nFlag == 0 )
1572 if ( fBytesNum >= 1024.0 && fBytesNum < 1024.0*1024.0 )
1573 csRes.Format ( _T("%.2f%s"), fBytesNum / 1024.0, bShowUnit?_T(" K"):_T("") );
1574 else if ( fBytesNum >= 1024.0*1024.0 && fBytesNum < 1024.0*1024.0*1024.0 )
1575 csRes.Format ( _T("%.2f%s"), fBytesNum / (1024.0*1024.0), bShowUnit?_T(" M"):_T("") );
1576 else if ( fBytesNum >= 1024.0*1024.0*1024.0 )
1577 csRes.Format ( _T("%.2f%s"), fBytesNum / (1024.0*1024.0*1024.0), bShowUnit?_T(" G"):_T("") );
1578 else
1579 csRes.Format ( _T("%.2f%s"), fBytesNum, bShowUnit?_T(" B"):_T("") );
1581 else if ( nFlag == 1 )
1583 csRes.Format ( _T("%.2f%s"), fBytesNum / 1024.0, bShowUnit?_T(" K"):_T("") );
1585 else if ( nFlag == 2 )
1587 csRes.Format ( _T("%.2f%s"), fBytesNum / (1024.0*1024.0), bShowUnit?_T(" M"):_T("") );
1589 else if ( nFlag == 3 )
1591 csRes.Format ( _T("%.2f%s"), fBytesNum / (1024.0*1024.0*1024.0), bShowUnit?_T(" G"):_T("") );
1594 return csRes;