Jump to content

Curl UDF - libcurl with x64 support

Recommended Posts

I modified the example above to also get the date,from,to in addition to the subject only. 

For anyone who has some subject lines that look like this below, I found this article: https://dmorgan.info/posts/encoded-word-syntax/

Subject: =?UTF-8?B?VG9wIFdhdGNoYXRob24gcGlja3Mgd2UgdGhpbmsgeW91J2xsIA==?=

Link to post
Share on other sites
Posted (edited)

This works for the base64 encoded subjects, but I'm not finding much info for the quopri encoding that article talks about (outside of python). I dont think any of the emails im working with are encoded with quopri so not sure its that common.

Func _encoded_word_syntax_test()
    $sTest = "=?UTF-8?B?VG9wIFdhdGNoYXRob24gcGlja3Mgd2UgdGhpbmsgeW91J2xsIA==?="
    $aEWS = StringRegExp($sTest, '=\?(.*)\?(.)\?(.*)\?=', 3); seperates charset, encoding, encoded-text
    If $aEWS[1] = "B" Then; base64 encoded
        $sDecode = BinaryToString(_Base64Decode($aEWS[2]))
        ConsoleWrite($sDecode & @CRLF)
        ;quopri encoded

Func _Base64Decode($sB64)
    Local $iLen = DllCall("Crypt32.dll", "bool", "CryptStringToBinaryA", "str", $sB64, "dword", 0, "dword", 1, "ptr", NULL, "dword*", 0, "ptr", 0, "ptr", 0)[5]
    Local $tDecoded = DllCall("Crypt32.dll", "bool", "CryptStringToBinaryA", "str", $sB64, "dword", 0, "dword", 1, "struct*", DllStructCreate("byte[" & $iLen & "]"), "dword*", $iLen, "ptr", 0, "ptr", 0)[4]
    Return DllStructGetData($tDecoded, 1)
EndFunc   ;==>_Base64Decode



Actually this is better for the multi-line subjects extracting only base64 encoded text.

Also noticing there's something weird about the single quote character (') being used in a subject. It translates like its a special character or something

$sTest = "Subject: =?utf-8?B?V2VsY29tZSB0byBYRklOSVRZIENvbm5lY3QgRW1haWwhIExldA==?=" & @CRLF & _
        @TAB & "=?utf-8?B?4oCZcyBnZXQgc3RhcnRlZA==?="

$aEWS = StringRegExp($sTest, '(?m)^(?:Subject\:\h|\h)?=\?.*\?B\?(.*)\?=$', 3) ; grabs only the base64 portion from each line
If Not @error Then
    Local $sDecode
    For $sEnc In $aEWS
        $sDecode &= BinaryToString(_Base64Decode($sEnc))
    ConsoleWrite($sDecode & @CRLF)



Fix for the single quote weirdness is to use the charset specified (utf-8 for this one) in the binarytostring() call options

$sDecode &= BinaryToString(_Base64Decode($sEnc), 4)


Edited by Beege
Link to post
Share on other sites
Posted (edited)

I was able to get the imap append example working to save sent items. This was a real pain in the ass due to that example being wrong in multiple ways so I got to rant a little bit. 

For starters it states twice that we are sending an email when we are not, we are just appending one.

/* <DESC>
 * IMAP example showing how to send emails
 * </DESC>
 /* This is a simple example showing how to send mail using libcurl's IMAP
 * capabilities.

They clarify this in the RFC

Note: The APPEND command is not used for message delivery,
      because it does not provide a mechanism to transfer [SMTP]
      envelope information.

Then we get to the guts of the example. This is wrong

curl_easy_setopt(curl, CURLOPT_URL, "imap://imap.example.com/100");

And will give you errors like this

;< A002 OK Logged in
;> A003 APPEND Sent/3 (\Seen) {125}
;< A003 NO [TRYCREATE] Mailbox doesn't exist: Sent/3 (0.001 + 0.000 secs).
;> A004 LOGOUT
;< * BYE Logging out

I ended up down a rabbit hole with TRYCREATE. If you read the rfc for that, it seems like everything is kinda doing what its suppose to be only the email client isn't re-asking to create the email.. 

If the destination mailbox does not exist, a server MUST return an
 error, and MUST NOT automatically create the mailbox.  Unless it
 is certain that the destination mailbox can not be created, the
 server MUST send the response code "[TRYCREATE]" as the prefix of
 the text of the tagged NO response.  This gives a hint to the
 client that it can attempt a CREATE command and retry the APPEND
 if the CREATE is successful.


Finally I found this link from 2016 reporting basically the same problem I'm seeing and got the syntax right. Same syntax I got in all the other examples. I should have just guessed. :angry:

; #FUNCTION# ====================================================================================================================
; Name ..........: Example_Email_Append
; Description ...: Appends an email to a directory. (Use to save sent emails in sent folder)
; Parameters ....: $sIMAPServer         - imap server address. ex: imap.comcast.net
;                  $sUser               - username/email address
;                  $sPass               - password
;                  $sTo                 - recepient address
;                  $sFrom               - sender address
;                  $sSubject            - email subject
;                  $sBody               - email body
;                  $sDirName            - Directory Name ex: Outbox, Sent Items, Sent
; Remarks .......: This does not send an email. Just creates a copy
; ===============================================================================================================================
Func Example_Email_Append($sIMAPServer, $sUser, $sPass, $sTo, $sFrom, $sSubject, $sBody, $sDirName = 'Sent')

    ;Build email
    Local $sHeaders = "To: <" & $sTo & ">" & @CRLF
    $sHeaders &= "From: <" & $sFrom & ">" & @CRLF
;~  $sHeaders &= "CC: <" & $sCC & ">" & @CRLF
;~  $sHeaders &= "BCC: <" & $sBCC & ">" & @CRLF
    $sHeaders &= "Subject: " & $sSubject & @CRLF

    Local $sEmail = $sHeaders & @CRLF & $sBody

    Local $Curl = Curl_Easy_Init()
    If Not $Curl Then Return

    Curl_Easy_Setopt($Curl, $CURLOPT_VERBOSE, 1) ;
    Curl_Easy_Setopt($Curl, $CURLOPT_USERNAME, $sUser) ;
    Curl_Easy_Setopt($Curl, $CURLOPT_PASSWORD, $sPass) ;
    Curl_Easy_Setopt($Curl, $CURLOPT_CAINFO, @ScriptDir & '\curl-ca-bundle.crt') ;
    Curl_Easy_Setopt($Curl, $CURLOPT_WRITEFUNCTION, Curl_DataWriteCallback())
    Curl_Easy_Setopt($Curl, $CURLOPT_WRITEDATA, $Curl)

    ;request next uid
    Curl_Easy_Setopt($Curl, $CURLOPT_CUSTOMREQUEST, 'STATUS "' & $sDirName & '" (UIDNEXT)')
    Curl_Easy_Setopt($Curl, $CURLOPT_URL, "imaps://" & $sIMAPServer)
    Local $Code = Curl_Easy_Perform($Curl)
    If $Code = $CURLE_OK Then

        Local $aNextUID = StringRegExp(BinaryToString(Curl_Data_Get($Curl)), 'UIDNEXT\h(\d+)', 3)
        If Not @error Then
            ConsoleWrite('next uid = ' & $aNextUID[0] & @CRLF)

            Curl_Easy_Reset($Curl) ; clear all previous options

            Curl_Easy_Setopt($Curl, $CURLOPT_VERBOSE, 1) ;
            Curl_Easy_Setopt($Curl, $CURLOPT_USERNAME, $sUser) ;
            Curl_Easy_Setopt($Curl, $CURLOPT_PASSWORD, $sPass) ;
            Curl_Easy_Setopt($Curl, $CURLOPT_CAINFO, @ScriptDir & '\curl-ca-bundle.crt') ;

            ;Set email data and callback
            Curl_Data_Put($Curl, $sEmail)
            Curl_Easy_Setopt($Curl, $CURLOPT_UPLOAD, 1)
            Curl_Easy_Setopt($Curl, $CURLOPT_READFUNCTION, Curl_DataReadCallback())
            Curl_Easy_Setopt($Curl, $CURLOPT_READDATA, $Curl)
            Curl_Easy_Setopt($Curl, $CURLOPT_INFILESIZE, StringLen($sEmail)) ;set filesize
            ;Curl_Easy_Setopt($Curl, $CURLOPT_CONNECTTIMEOUT,20); very helpful when troubleshooting. default is 300 secs

            $sDirName = StringReplace($sDirName, ' ', '%20') ; replace spaces
            Curl_Easy_Setopt($Curl, $CURLOPT_URL, "imaps://" & $sIMAPServer & "/" & $sDirName & ";UID=" & $aNextUID[0]) ;append email
            Local $Code = Curl_Easy_Perform($Curl)
            If $Code = $CURLE_OK Then
                ConsoleWrite('append OK' & @CRLF)
                ConsoleWrite(Curl_Easy_StrError($Code) & @LF)
                ConsoleWrite('append failed' & @CRLF)
            ConsoleWrite('extract uidnext failed' & @CRLF)
        ConsoleWrite('request uidnext failed' & @CRLF)

    Curl_Easy_Cleanup($Curl) ;
EndFunc   ;==>Example_Email_Append


Edited by Beege
Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Create New...