64 bit SSL SMTP
Posted: Sat Dec 22, 2012 2:27 am
				
				This is a 64 bit SSL based SMTP process that I created to be able to send email via GMail. Credit goes to the fine people who wrote cryptlib.dll, Mike Trader from coastrd.com and the many good people who have helped me on the forum. Note that I renamed the dll's to a name a bit more descriptive than they were, and I reduced the header include file to it's bare minimum for SSL Email. You can find the entire project here:
http://www.rca2.com/dlls/Cryptlibdlls.zip
ssl_mail64.pbi
email_ssl64.pb
			http://www.rca2.com/dlls/Cryptlibdlls.zip
ssl_mail64.pbi
Code: Select all
;*****************************************************************************
;*                                                                           *
;*                        cryptlib External API Interface                    *
;*                       Copyright Peter Gutmann 1997-2008                   *
;*                                                                           *
;*                 Adapted For BASIC Nov 2009 - Coast Research               *
;*                 Adapted for PureBasic by Bart                             *
;*                                                                           *
;*****************************************************************************
;
;* Translated for PureBasic Nov 2009 from: http://www.coastrd.com/smtps/cryptlib/cryptlib_header-inc
#CRYPTLIB_VERSION    = 3330   ;  Not the same As 3.32
; cutdown version for SSL SMTP, 64 bit
;  /****************************************************************************
;  *                                            General Constants               *
;  ****************************************************************************/
#CRYPT_MAX_KEYSIZE              =  256 ; /* The maximum user key size - 2048 bits */
#CRYPT_MAX_IVSIZE               =  32  ; /* The maximum IV size - 256 bits */
#CRYPT_MAX_PKCSIZE              =  512 ; The maximum public-key component size - 4096 bits, and maximum component
#CRYPT_MAX_PKCSIZE_ECC          =  72  ; size FOR ECCs - 576 bits (TO HANDLE the P521 curve) */
#CRYPT_MAX_HASHSIZE             =  32  ; /* The maximum hash size - 256 bits */
#CRYPT_MAX_TEXTSIZE             =  64  ; /* The maximum size of a text string (e.g.key owner name) */
#CRYPT_USE_DEFAULT              = -100 ; A magic value indicating that the default setting
#CRYPT_UNUSED                   = -101 ; /* A magic value for unused parameters */
#CRYPT_SESSION_SSL              =  03  ; /* SSL/TLS */
#CRYPT_SESSINFO_ACTIVE          = 6001 ; /* Whether session is active */
#CRYPT_SESSINFO_SERVER_NAME     = 6008 ; /* SERVER NAME */
#CRYPT_SESSINFO_SERVER_PORT     = 6009 ; /* SERVER PORT number */
;  /****************************************************************************
;  *                                           STATUS Codes                     *
;  ****************************************************************************/
; /* Errors in function calls */
#CRYPT_OK                       =   0  ; /* No error */
#CRYPT_ERROR_PARAM1             =  -1  ; /* Bad argument, parameter 1 */
#CRYPT_ERROR_PARAM2             =  -2  ; /* Bad argument, parameter 2 */
#CRYPT_ERROR_PARAM3             =  -3  ; /* Bad argument, parameter 3 */
#CRYPT_ERROR_PARAM4             =  -4  ; /* Bad argument, parameter 4 */
#CRYPT_ERROR_PARAM5             =  -5  ; /* Bad argument, parameter 5 */
#CRYPT_ERROR_PARAM6             =  -6  ; /* Bad argument, parameter 6 */
#CRYPT_ERROR_PARAM7             =  -7  ; /* Bad argument, parameter 7 */
;/* Errors due to insufficient resources */
#CRYPT_ERROR_MEMORY             = -10  ; /* Out of memory */
#CRYPT_ERROR_NOTINITED          = -11  ; /* Data has not been initialised */
#CRYPT_ERROR_INITED             = -12  ; /* Data has already been init;d */
#CRYPT_ERROR_NOSECURE           = -13  ; /* Opn.not avail.at requested sec.level */
#CRYPT_ERROR_RANDOM             = -14  ; /* No reliable random data available */
#CRYPT_ERROR_FAILED             = -15  ; /* Operation failed */
#CRYPT_ERROR_INTERNAL           = -16  ; /* Internal consistency check failed */
;/* Security violations */
#CRYPT_ERROR_NOTAVAIL           = -20  ; /* This type of opn.not available */
#CRYPT_ERROR_PERMISSION         = -21  ; /* No permiss.TO perform this operation */
#CRYPT_ERROR_WRONGKEY           = -22  ; /* Incorrect key used to decrypt data */
#CRYPT_ERROR_INCOMPLETE         = -23  ; /* Operation incomplete/still IN progress */
#CRYPT_ERROR_COMPLETE           = -24  ; /* Operation complete/can;t Continue */
#CRYPT_ERROR_TIMEOUT            = -25  ; /* Operation timed out before completion */
#CRYPT_ERROR_INVALID            = -26  ; /* Invalid/inconsistent information */
#CRYPT_ERROR_SIGNALLED          = -27  ; /* Resource destroyed by extnl.event */
;/* High-level function errors */
#CRYPT_ERROR_OVERFLOW           = -30  ; /* Resources/space exhausted */
#CRYPT_ERROR_UNDERFLOW          = -31  ; /* Not enough data available */
#CRYPT_ERROR_BADDATA            = -32  ; /* Bad/unrecognised data format */
#CRYPT_ERROR_SIGNATURE          = -33  ; /* Signature/integrity check failed */
;/* Data access function errors */
#CRYPT_ERROR_OPEN               = -40  ; /* Cannot OPEN object */
#CRYPT_ERROR_READ               = -41  ; /* Cannot READ item from object */
#CRYPT_ERROR_WRITE              = -42  ; /* Cannot WRITE item to object */
#CRYPT_ERROR_NOTFOUND           = -43  ; /* Requested item not found in object */
#CRYPT_ERROR_DUPLICATE          = -44  ; /* Item already present in object */
;/* Data enveloping errors */
#CRYPT_ENVELOPE_RESOURCE        = -50  ; /* Need resource to proceed */
;  /****************************************************************************
;  *                                        General Functions                   *
;  ****************************************************************************/
Global cryptLib.i               = OpenLibrary(#PB_Any, "cryptlib64.dll")
Global *cryptInit               = GetFunction(cryptLib, "cryptInit")
Global *cryptEnd                = GetFunction(cryptLib, "cryptEnd")
Global *cryptSetAttribute       = GetFunction(cryptLib, "cryptSetAttribute")
Global *cryptSetAttributeString = GetFunction(cryptLib, "cryptSetAttributeString")
Global *cryptCreateSession      = GetFunction(cryptLib, "cryptCreateSession")
Global *cryptDestroySession     = GetFunction(cryptLib, "cryptDestroySession")
Global *cryptCreateEnvelope     = GetFunction(cryptLib, "cryptCreateEnvelope")
Global *cryptDestroyEnvelope    = GetFunction(cryptLib, "cryptDestroyEnvelope")
Global *cryptPushData           = GetFunction(cryptLib, "cryptPushData")
Global *cryptFlushData          = GetFunction(cryptLib, "cryptFlushData")
Global *cryptPopData            = GetFunction(cryptLib, "cryptPopData")
Procedure.i cryptInit()
    ProcedureReturn CallFunctionFast(*cryptInit)
EndProcedure
Procedure.i cryptEnd()
    ProcedureReturn CallFunctionFast(*cryptEnd)
EndProcedure
Procedure.i cryptSetAttribute(hCrypt.i,CryptAttType.i, value.i)
   ProcedureReturn CallFunctionFast(*cryptSetAttribute, hCrypt.i,CryptAttType.i, value.i)
EndProcedure
Procedure.i cryptSetAttributeString(hCrypt.i, CryptAttType.i, pBuff.i, StrLen.i)
    ProcedureReturn CallFunctionFast(*cryptSetAttributeString, hCrypt.i, CryptAttType.i, pBuff.i, StrLen.i)
EndProcedure
;  /****************************************************************************
;  *                                        Envelope & Session Functions        *
;  ****************************************************************************/
Procedure.i cryptCreateSession(pSession.i, cryptUser.i, SessionType.i)
    ProcedureReturn CallFunctionFast(*cryptCreateSession, pSession.i, cryptUser.i, SessionType.i)
EndProcedure
Procedure.i cryptDestroySession(session.i)
    ProcedureReturn CallFunctionFast(*cryptDestroySession, session.i)
EndProcedure
Procedure.i cryptCreateEnvelope(penvelope.i, cryptUser.i, FormatType.i)
    ProcedureReturn CallFunctionFast(*cryptCreateEnvelope, penvelope.i, cryptUser.i, FormatType.i)
EndProcedure
Procedure.i cryptDestroyEnvelope(envelope.i)
    ProcedureReturn CallFunctionFast(*cryptDestroyEnvelope, envelope.i)
EndProcedure
Procedure.i cryptPushData(envelope.i, pBuff.i, StrLen.i,  pBytesCopied.i)
    ProcedureReturn CallFunctionFast(*cryptPushData, envelope.i, pBuff.i, StrLen.i,  pBytesCopied.i)
EndProcedure
Procedure.i cryptFlushData(envelope.i)
    ProcedureReturn CallFunctionFast(*cryptFlushData, envelope.i)
EndProcedure
Procedure.i cryptPopData(envelope.i, pBuff.i, StrLen.i, pBytesCopied.i)
    ProcedureReturn CallFunctionFast(*cryptPopData, envelope.i, pBuff.i, StrLen.i, pBytesCopied.i)
EndProcedure
;  /*****************************************************************************                                                                                                                 *
;  *                                          User Interface Functions          *
;  ****************************************************************************/
Procedure.s lookupError(errorCode.i)
    Select errorCode
    Case #CRYPT_OK
        ProcedureReturn  ""
    Case #CRYPT_ERROR_PARAM1
        ProcedureReturn  "Bad argument - parameter 1"
    Case #CRYPT_ERROR_PARAM2
        ProcedureReturn  "Bad argument - parameter 2"
    Case #CRYPT_ERROR_PARAM3
        ProcedureReturn  "Bad argument - parameter 3"
    Case #CRYPT_ERROR_PARAM4
        ProcedureReturn  "Bad argument - parameter 4"
    Case #CRYPT_ERROR_PARAM5
        ProcedureReturn  "Bad argument - parameter 5"
    Case #CRYPT_ERROR_PARAM6
        ProcedureReturn  "Bad argument - parameter 6"
    Case #CRYPT_ERROR_PARAM7
        ProcedureReturn  "Bad argument - parameter 7"
    Case #CRYPT_ERROR_MEMORY
        ProcedureReturn  "Out of memory"
    Case #CRYPT_ERROR_NOTINITED
        ProcedureReturn  "Data has not been initialized"
    Case #CRYPT_ERROR_INITED
        ProcedureReturn  "Data has already been initialized"
    Case #CRYPT_ERROR_NOSECURE
        ProcedureReturn  "Operation not available at requested security level"
    Case #CRYPT_ERROR_RANDOM
        ProcedureReturn  "No reliable random data available"
    Case #CRYPT_ERROR_FAILED
        ProcedureReturn  "Operation failed"
    Case #CRYPT_ERROR_INTERNAL
        ProcedureReturn  "Internal consistency check failed"
    Case #CRYPT_ERROR_NOTAVAIL
        ProcedureReturn  "This type of operation not available"
    Case #CRYPT_ERROR_PERMISSION
        ProcedureReturn  "No permission to perform this operation"
    Case #CRYPT_ERROR_WRONGKEY
        ProcedureReturn  "Incorrect key used to decrypt data"
    Case #CRYPT_ERROR_INCOMPLETE
        ProcedureReturn  "Operation incomplete/still in progress"
    Case #CRYPT_ERROR_COMPLETE
        ProcedureReturn  "Operation complete/can't continue"
    Case #CRYPT_ERROR_TIMEOUT
        ProcedureReturn  "Operation timed out before completion"
    Case #CRYPT_ERROR_INVALID
        ProcedureReturn  "Invalid/inconsistent information"
    Case #CRYPT_ERROR_SIGNALLED
        ProcedureReturn  "Resource destroyed by external event"
    Case #CRYPT_ERROR_OVERFLOW
        ProcedureReturn  "Resources/space exhausted"
    Case #CRYPT_ERROR_UNDERFLOW
        ProcedureReturn  "Not enough data available"
    Case #CRYPT_ERROR_BADDATA
        ProcedureReturn  "Bad/unrecognised data format"
    Case #CRYPT_ERROR_SIGNATURE
        ProcedureReturn  "Signature/integrity check failed"
    Case #CRYPT_ERROR_OPEN
        ProcedureReturn  "Cannot open object"
    Case #CRYPT_ERROR_READ
        ProcedureReturn  "Cannot read item from object"
    Case #CRYPT_ERROR_WRITE
        ProcedureReturn  "Cannot write item to object"
    Case #CRYPT_ERROR_NOTFOUND
        ProcedureReturn  "Requested item not found in object"
    Case #CRYPT_ERROR_DUPLICATE
        ProcedureReturn  "Item already present in object"
    Case #CRYPT_ENVELOPE_RESOURCE
        ProcedureReturn  "Need resource to proceed"
    Default
        ProcedureReturn  "Unknown error code: " + Str(errorCode)
    EndSelect
EndProcedure
Code: Select all
EnableExplicit
XIncludeFile "ssl_mail64.pbi"
#SMTP_RESPONSE_TIMEOUT = 5000 ; m/s
;****************************************************************************************
; receive data from the email server encrypted by ssl
;****************************************************************************************
Procedure.i sslRecvData(secureSession.i, *sslError, *returnData, *lenRtnData, buffSize.i)
    Protected retValue.i
    Protected totReturned = 0
    Protected totalMS.i   = 0
; get server response, wait as needed
    Repeat
        Delay(100)
        totalMS = totalMS + 100
        If totalMS > #SMTP_RESPONSE_TIMEOUT
            Break
        EndIf
        retValue = CryptPopData(secureSession, *returnData, buffSize, @totReturned)
        If retValue <> #CRYPT_OK
            PokeS(*sslError, "CryptPopData ERROR: " + lookupError(retValue))
            ProcedureReturn -999
        Else
            If totReturned > 0
                PokeI(*lenRtnData, totReturned)
                Break
            EndIf
        EndIf
    ForEver
    PrintN("Recd:")
    If totReturned > 0
        ConsoleColor(10,0)
        PrintN(PeekS(*returnData, totReturned))
        ConsoleColor(15,0)
    EndIf
    ProcedureReturn retValue
EndProcedure
;****************************************************************************************
; send data to the email server encrypted by ssl
;****************************************************************************************
Procedure.i sslSendData(secureSession.i, *sslError, *sendText, lenSendText)
    Protected retValue.i
    Protected bytesSent.i
; send data to email server
    retValue = CryptPushData(secureSession, *sendText, lenSendText, @bytesSent); used to copy data into an encrypted envelope
    If retValue <> #CRYPT_OK Or lenSendText <> bytesSent
        PokeS(*sslError, "CryptPushData ERROR " + lookupError(retValue) + ", lenSend = " + Str(lenSendText) + ", bytesSent = " + Str(bytesSent))
        ProcedureReturn -998
    EndIf
    retValue = CryptFlushData(secureSession); used to send data to remote machine
    If retValue <> #CRYPT_OK
        PokeS(*sslError, "CryptFlushData ERROR " + lookupError(retValue))
        ProcedureReturn -997
    EndIf
    PrintN("Sent:")
    ConsoleColor(14,0)
    PrintN(PeekS(*sendText, lenSendText))
    ConsoleColor(15,0)
    ProcedureReturn retValue
EndProcedure
;****************************************************************************************
Procedure.i SendPage(*mailHost, lenMailHost.i, mailPort.i, *userName, lenUserName, *password, lenPassword, *mailfrom, *mailTo, *emailBody, *rtnError)
    Protected retValue.i
    Protected cmdString.s
    Protected cmdNumber.i
    Protected lenghtTTS.i
    Protected sslSession.i
    Protected base64Size.i
    Protected base64Data.s
    Protected bytesRecvd.i
    Protected textToSend.s
    Protected serverReply.s
    Protected lenToEncode.i
    Protected replySize   = 255
    Protected mailfrom.s  = PeekS(*mailfrom)
    Protected mailTo.s    = PeekS(*mailTo)
    Protected emailBody.s = PeekS(*emailBody)
; create the ssl session
    retValue = CryptCreateSession(@sslSession, #CRYPT_UNUSED, #CRYPT_SESSION_SSL)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; add the server name "smtp.gmail.com"
    retValue = CryptSetAttributeString(sslSession, #CRYPT_SESSINFO_SERVER_NAME, *mailHost, lenMailHost)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; specify the Port, for SSL GMail, it's 465
    retValue = CryptSetAttribute(sslSession, #CRYPT_SESSINFO_SERVER_PORT, mailPort)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; activate the ssl session
    retValue = CryptSetAttribute(sslSession, #CRYPT_SESSINFO_ACTIVE, 1)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; receive response created by connecting
    serverReply = Space(replySize)
    retValue    = sslRecvData(sslSession, *rtnError, @serverReply, @bytesRecvd, replySize)
    If retValue = #CRYPT_OK
        cmdString = PeekS(@serverReply, 3)
        cmdNumber = Val(cmdString)    
    Else
        PokeS(*rtnError, "sslRecvData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; need 220, connection okay, begin enhanced smtp dialog
    If cmdNumber <> 220
        PokeS(*rtnError, "Invalid handshake received: " + cmdString)
        ProcedureReturn -996
    EndIf
; send EHLO to server
    textToSend = "EHLO" + #CRLF$
    lenghtTTS  = Len(textToSend)
    retValue   = sslSendData(sslSession, *rtnError, @textToSend, lenghtTTS)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, "sslSendData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; receive response from EHLO command
    serverReply = Space(replySize)
    retValue    = sslRecvData(sslSession, *rtnError, @serverReply, @bytesRecvd, replySize)
    If retValue = #CRYPT_OK
        cmdString = PeekS(@serverReply, 3)
        cmdNumber = Val(cmdString)    
    Else
        PokeS(*rtnError, "sslRecvData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; need 250, requested mail action okay, begin enhanced smtp dialog
    If cmdNumber <> 250
        PokeS(*rtnError, "EHLO Failed: " + cmdString)
        ProcedureReturn -995
    EndIf
; send auth login to server
    textToSend = "AUTH LOGIN" + #CRLF$
    lenghtTTS  = Len(textToSend)
    retValue   = sslSendData(sslSession, *rtnError, @textToSend, lenghtTTS)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, "sslSendData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; receive response from AUTH LOGIN command
    serverReply = Space(replySize)
    retValue    = sslRecvData(sslSession, *rtnError, @serverReply, @bytesRecvd, replySize)
    If retValue = #CRYPT_OK
        cmdString = PeekS(@serverReply, 3)
        cmdNumber = Val(cmdString)    
    Else
        PokeS(*rtnError, "sslRecvData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; need 334, ok to login, send username but encode the user name to base64 (mime) first
    If cmdNumber <> 334
        PokeS(*rtnError, "AUTH LOGIN Failed: " + cmdString)
        ProcedureReturn -994
    EndIf
; send the username to server
    base64Size  = lenUserName * 2
    If  base64Size < 64
        base64Size = 64
    EndIf
    base64Data = Space(base64Size + 1)
    Base64Encoder(*userName, lenUserName, @base64Data, base64Size)
    textToSend = base64Data + #CRLF$
    lenghtTTS  = Len(textToSend)
    retValue   = sslSendData(sslSession, *rtnError, @textToSend, lenghtTTS)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, "sslSendData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; receive response from username command
    serverReply = Space(replySize)
    retValue    = sslRecvData(sslSession, *rtnError, @serverReply, @bytesRecvd, replySize)
    If retValue = #CRYPT_OK
        cmdString = PeekS(@serverReply, 3)
        cmdNumber = Val(cmdString)    
    Else
        PokeS(*rtnError, "sslRecvData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; need 334, username ok, send password but encode the password to base64 (mime) first
    If cmdNumber <> 334
        PokeS(*rtnError, "Username Failed: " + cmdString)
        ProcedureReturn -993
    EndIf
; send the password to server
    base64Size  = lenPassword * 2
    If  base64Size < 64
        base64Size = 64
    EndIf
    base64Data = Space(base64Size + 1)
    Base64Encoder(*password, lenPassword, @base64Data, base64Size)
    textToSend = base64Data + #CRLF$
    lenghtTTS  = Len(textToSend)
    retValue   = sslSendData(sslSession, *rtnError, @textToSend, lenghtTTS)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, "sslSendData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; receive response from password command
    serverReply = Space(replySize)
    retValue    = sslRecvData(sslSession, *rtnError, @serverReply, @bytesRecvd, replySize)
    If retValue = #CRYPT_OK
        cmdString = PeekS(@serverReply, 3)
        cmdNumber = Val(cmdString)    
    Else
        PokeS(*rtnError, "sslRecvData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; need 235 = authentication ok or 250 = all ok, enter the from email address
    If cmdNumber <> 235 And cmdNumber <> 250
        PokeS(*rtnError, "Password Failed: " + cmdString)
        ProcedureReturn -992
    EndIf
; send the mail from address to server
    textToSend = "MAIL FROM: <" + mailfrom + ">" + #CRLF$
    lenghtTTS  = Len(textToSend)
    retValue   = sslSendData(sslSession, *rtnError, @textToSend, lenghtTTS)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, "sslSendData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; receive response from 'mail from' command
    serverReply = Space(replySize)
    retValue    = sslRecvData(sslSession, *rtnError, @serverReply, @bytesRecvd, replySize)
    If retValue = #CRYPT_OK
        cmdString = PeekS(@serverReply, 3)
        cmdNumber = Val(cmdString)    
    Else
        PokeS(*rtnError, "sslRecvData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; need a 250 here, sender ok, enter the receiving email address
    If cmdNumber <> 250
        PokeS(*rtnError, "MAIL FROM Failed: " + cmdString)
        ProcedureReturn -991
    EndIf
; send the receiving email address to server
    textToSend = "RCPT TO: <" + mailTo + ">" + #CRLF$
    lenghtTTS  = Len(textToSend)
    retValue   = sslSendData(sslSession, *rtnError, @textToSend, lenghtTTS)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, "sslSendData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; receive response from 'send to' command
    serverReply = Space(replySize)
    retValue    = sslRecvData(sslSession, *rtnError, @serverReply, @bytesRecvd, replySize)
    If retValue = #CRYPT_OK
        cmdString = PeekS(@serverReply, 3)
        cmdNumber = Val(cmdString)    
    Else
        PokeS(*rtnError, "sslRecvData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; need a 250 or 251 here, ok to proceed, send the data command
    If cmdNumber <> 250 And cmdNumber <> 251
        PokeS(*rtnError, "RCPT TO Failed: " + cmdString)
        ProcedureReturn -990
    EndIf
; send the 'DATA' command
    textToSend = "DATA" + #CRLF$
    lenghtTTS  = Len(textToSend)
    retValue   = sslSendData(sslSession, *rtnError, @textToSend, lenghtTTS)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, "sslSendData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; receive response from 'DATA' command
    serverReply = Space(replySize)
    retValue    = sslRecvData(sslSession, *rtnError, @serverReply, @bytesRecvd, replySize)
    If retValue = #CRYPT_OK
        cmdString = PeekS(@serverReply, 3)
        cmdNumber = Val(cmdString)    
    Else
        PokeS(*rtnError, "sslRecvData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; we should receive 354
    If cmdNumber <> 354
        PokeS(*rtnError, "DATA Failed: " + cmdString)
        ProcedureReturn -989
    EndIf
; send the mail itself, end with "." on a line by itself
    textToSend = emailBody + #CRLF$ + "." + #CRLF$
    lenghtTTS  = Len(textToSend)
    retValue   = sslSendData(sslSession, *rtnError, @textToSend, lenghtTTS)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, "sslSendData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; receive response from 'end of data' command
    serverReply = Space(replySize)
    retValue    = sslRecvData(sslSession, *rtnError, @serverReply, @bytesRecvd, replySize)
    If retValue = #CRYPT_OK
        cmdString = PeekS(@serverReply, 3)
        cmdNumber = Val(cmdString)    
    Else
        PokeS(*rtnError, "sslRecvData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; doesn't matter what we have received, we're going to quit anyway
    textToSend = "QUIT" + #CRLF$
    lenghtTTS  = Len(textToSend)
    retValue   = sslSendData(sslSession, *rtnError, @textToSend, lenghtTTS)
    If retValue <> #CRYPT_OK
        PokeS(*rtnError, "sslSendData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; receive response from 'quit' command
    serverReply = Space(replySize)
    retValue    = sslRecvData(sslSession, *rtnError, @serverReply, @bytesRecvd, replySize)
    If retValue = #CRYPT_OK
        cmdString = PeekS(@serverReply, 3)
        cmdNumber = Val(cmdString)    
    Else
        PokeS(*rtnError, "sslRecvData ERROR: " + lookupError(retValue))
        ProcedureReturn retValue
    EndIf
; now end the ssl session
    If sslSession
        CryptDestroySession(sslSession)
    EndIf
    ProcedureReturn 0
EndProcedure
Define emailBody.s
Define errReturn.s = Space(255)
Define retValue.i, bytesRecvd.i
Define mailHost.s         = "smtp.gmail.com"               ; SMTP Host
Define lenMailHost.i      = Len(mailHost)
Define mailPort.i         = 465
Define mailFrom.s         = "my_email@gmail.com"
Define userName.s         = "my_email@gmail.com"           ; your gmail account
Define lenUserName.i      = Len(userName)
Define password.s         = "somepassword"                 ; your password
Define lenPassword.i      = Len(password)
Define mailTo.s           = "user@user.com"
emailBody = "From: "    + Mailfrom  + #CRLF$
emailBody + "To: "      + MailTo    + #CRLF$
emailBody + "Subject: " + "64 bit Gmail Test using SSL Encryption" + #CRLF$
emailBody + "Dear Sir, this email is brought to you courtesy of 64 bit SSL Encryption."
OpenConsole()
ConsoleColor(15,0)
; Initialize the crypt Library
Define initValue = CryptInit()
If initValue <> #CRYPT_OK
    errReturn = lookupError(initValue)
    PrintN(errReturn)
    End
EndIf
retValue = SendPage(@mailHost, lenMailHost, mailPort, @userName, lenUserName, @password, lenPassword, @mailfrom, @mailTo, @emailBody, @errReturn)
If retValue <> #CRYPT_OK
    PrintN(errReturn)
Else
    PrintN("EMail sent successfully!")
EndIf
CryptEnd() ; Close the Library
Input()
CloseConsole()
; IDE Options = PureBasic 4.61 (Windows - x64)
; ExecutableFormat = Console
; CursorPosition = 5
; Folding = -
; Executable = email_ssl64.exe
; CompileSourceDirectory
; Compiler = PureBasic 4.61 (Windows - x64)