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
  • 2 months later...

I have downloaded your library, to circumvent the issues of a certified Windows 7 environment that has outdated TLS1.0 and SSL2.0 support only.
It works pretty great.
One remark though, it might be the API has been altered and extended during the releaseperiod of this library and today, but you have build the post mechanism based on the formadd() function while the developers highly advise to use the Mime functions instead, as the Formadd() function(s) are deprecated.





I may try to add these functions myself, but i have only time for this in a couple of weeks.

Link to post
Share on other sites

Thanks for the info @lbsl

I did start writing those when I was playing around with the smtp stuff (for attachment options) but never got around to doing a real test with them. Here's what I had if you wanted to do any testing.

Func Curl_Mime_Init($Handle)
    ;curl_mime *curl_mime_init(CURL *easy_handle);
    Return DllCall($g_hlibcurl, (@AutoItX64 ? "ptr" : "ptr:cdecl"), "curl_mime_init", "ptr", $Handle)[0]
EndFunc   ;==>Curl_Mime_Init

Func Curl_Mime_Addpart($MimeHandle)
    ;curl_mimepart *curl_mime_addpart(curl_mime *mime);
    Return DllCall($g_hlibcurl, (@AutoItX64 ? "ptr" : "ptr:cdecl"), "curl_mime_addpart", "ptr", $MimeHandle)[0]
EndFunc   ;==>curl_mime_addpart

Func Curl_Mime_Subparts($MimePart, $MimeHandle)
    ;CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts);
    Return DllCall($g_hlibcurl, (@AutoItX64 ? "int" : "int:cdecl"), "curl_mime_subparts", "ptr", $MimePart, 'ptr', $MimeHandle)[0]
EndFunc   ;==>curl_mime_subparts

Func Curl_Mime_Name($MimePart, $sName)
    ;CURLcode curl_mime_name(curl_mimepart *part, const char *name);
    Return DllCall($g_hlibcurl, (@AutoItX64 ? "int" : "int:cdecl"), "curl_mime_name", "ptr", $MimePart, 'str', $sName)[0]
EndFunc   ;==>curl_mime_name

Func Curl_Mime_Filedata($MimePart, $sFilename)
    ;CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename);
    Return DllCall($g_hlibcurl, (@AutoItX64 ? "int" : "int:cdecl"), "curl_mime_filedata", "ptr", $MimePart, 'str', $sFilename)[0]
EndFunc   ;==>curl_mime_filedata

Func Curl_Mime_Filename($MimePart, $sFilename)
    ;CURLcode curl_mime_filename(curl_mimepart *part, const char *filename);
    Return DllCall($g_hlibcurl, (@AutoItX64 ? "int" : "int:cdecl"), "curl_mime_filename", "ptr", $MimePart, 'str', $sFilename)[0]
EndFunc   ;==>curl_mime_filename

Func Curl_Mime_Data($MimePart, $vData, $iSize = -1)
    ;CURLcode curl_mime_data(curl_mimepart *part, const char *data, size_t datasize);
    Local $Type = IsString($vData) ? 'str' : 'ptr'
    If IsDllStruct($vData) Then
        $Type = 'struct*'
        $iSize = DllStructGetSize($vData)
    Return DllCall($g_hlibcurl, (@AutoItX64 ? "int" : "int:cdecl"), "curl_mime_data", "ptr", $MimePart, $Type, $vData, 'uint_ptr', $iSize)[0]
EndFunc   ;==>curl_mime_data

Func Curl_Mime_Type($MimePart, $sMimetype)
    ;CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype);
    Return DllCall($g_hlibcurl, (@AutoItX64 ? "int" : "int:cdecl"), "curl_mime_type", "ptr", $MimePart, 'str', $sMimetype)[0]
EndFunc   ;==>curl_mime_type

Func Curl_Mime_Encoder($MimePart, $sEnc)
    ;CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding);
    Return DllCall($g_hlibcurl, (@AutoItX64 ? "int" : "int:cdecl"), "curl_mime_encoder", "ptr", $MimePart, 'str', $sEnc)[0]
EndFunc   ;==>curl_mime_encoder

Func Curl_Mime_Headers($MimePart, $pSLIST, $iTakeOwnership = 1)
    ;CURLcode curl_mime_headers(curl_mimepart *part, struct curl_slist *headers, int take_ownership);
    Return DllCall($g_hlibcurl, (@AutoItX64 ? "int" : "int:cdecl"), "curl_mime_headers", "ptr", $MimePart, 'ptr', $pSLIST, 'int', $iTakeOwnership)[0]
EndFunc   ;==>curl_mime_headers


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...