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)



