CurlMBS - IMAP - Save sent email

In my apps I’m using CurlMBS to send emails; it works fine.
Now I’d like to implement my code to save sent email in IMAP sent folder.
Starting from https://www.mbsplugins.de/archive/2017-12-14/Upload_email_to_Sent_folder_vi, I’ve implemented with success the code and now I can save sent emails, but with some IMAP server (also Gmail), I’ve got this error:

Upload failed (at start/before it took off)

Googling looking for a solution, I found these info about the error:

25 failed upload “command”
and
FTP couldn’t STOR file. The server denied the STOR operation, used for FTP uploading.
and
Upload failed. The server refused to accept or store the file that curl tried to send to it. This is usually due to wrong access rights on the server but can also happen due to out of disk space or other resource constraints. This error can happen for many protocols.

Testing with Gmail, to exclude any simple errors:
Trying with wrong username/password, the error is: 67 - Login denied
Trying with wrong IMAP url as:

myCurlMBS.OptionURL = "imaps://Ximap.gmail.com/Sent"

instead

myCurlMBS.OptionURL = "imaps://imap.gmail.com/Sent"

I’ve got the error: 6 - Could not resolve host: Ximap.gmail.com
so it seems the app can connect to the server, but there’s something wrong when the code try to upload the email into the IMAP sent folder.

I tried to set different CurlMBS options, but I can’t solve the issue.

Any ideas?

Thanks in advance
Pietro

Please try “imap://imap.gmail.com/Sent” without X and without s for the URL.

Using “imap://imap.gmail.com/Sent”, the error is:

Lasterror: 56
LasterrorMessage: response reading failed
LasterrorText: Failure when receiving data from the peer

What port do you use?

What if you put in the s back to the URL?

[quote=412129:@Christian Schmitz]What port do you use?

What if you put in the s back to the URL?[/quote]

myCurlMBS.OptionPort = 993

993 with TLS is normally with TLS, so you use “imap://” as URL prefix and OptionSSLVersion with 6 for TLS und OptionUseSSL mit 3.

From debug:

OptionURL: imap://imap.gmail.com/Sent
OptionPort: 993
OptionSSLVersion: 6
OptionUseSSL: 3

Lasterror: 56
LasterrorMessage: response reading failed
LasterrorText: Failure when receiving data from the peer

Can Curl do a log of the communication with the server?

Is this relevant: https://curl.haxx.se/mail/lib-2016-03/0045.html

Setting:

myCurlMBS.CollectDebugData = True

DebugData is:

  Trying 74.125.143.108...
TCP_NODELAY set
Connected to imap.gmail.com (74.125.143.108) port 993 (#0)
response reading failed
Closing connection 0

993 is the port for connecting. Don’t your need 587 or 465 for sending?

With a part of code I send the email using SMTP on port 587 or 465 and this part of code works fine. After the email is sent, I need to save sent email into the IMAP sent folder, so I need to connect to the imap server on port 993.

curl.OptionURL = "imaps://imap.gmail.com/Sent" curl.OptionPort = 993 curl.OptionSSLVersion = 6 curl.OptionUseSSL = 3

This works well here and gets me me error 67 for wrong password.

Log is like this:

[quote] Trying 2a00:1450:400c:c0c::6c…
TCP_NODELAY set
Trying 74.125.71.109…
TCP_NODELAY set
Connected to imap.gmail.com (74.125.71.109) port 993 (#0)
TLSv1.3 (OUT), TLS handshake, Client hello (1):
TLSv1.3 (IN), TLS handshake, Server hello (2):
TLSv1.2 (IN), TLS handshake, Certificate (11):
TLSv1.2 (IN), TLS handshake, Server key exchange (12):
TLSv1.2 (IN), TLS handshake, Server finished (14):
TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
TLSv1.2 (OUT), TLS handshake, Finished (20):
TLSv1.2 (IN), TLS handshake, Finished (20):
SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
Server certificate:
subject: C=US; ST=California; L=Mountain View; O=Google LLC; CN=imap.gmail.com
start date: Oct 9 13:08:00 2018 GMT
expire date: Jan 1 13:08:00 2019 GMT
issuer: C=US; O=Google Trust Services; CN=Google Internet Authority G3
SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.

  • OK Gimap ready for requests from 217.248.123.224 o5-v6mb26082344ljc
    A001 CAPABILITY
  • CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 XYZZY SASL-IR AUTH=XOAUTH2 AUTH=PLAIN AUTH=PLAIN-CLIENTTOKEN AUTH=OAUTHBEARER AUTH=XOAUTH
    A001 OK Thats all she wrote! o5-v6mb26082344ljc
    A002 AUTHENTICATE PLAIN eHh4AHh4eAB4eHg=
    A002 NO [AUTHENTICATIONFAILED] Invalid credentials (Failure)
    Closing connection 0
    TLSv1.2 (OUT), TLS alert, close notify (256):[/quote]

my test code:

[code]dim curl as new CURLSMBS

curl.CollectOutputData = true
curl.OptionUsername = “xxx”
curl.OptionPassword = “xxx”
curl.OptionCustomRequest = “FETCH 1:* FLAGS”
curl.OptionVerbose = true
curl.CollectDebugData = true
curl.CollectOutputData = true
curl.OptionURL = “imaps://imap.gmail.com/Sent”
curl.OptionPort = 993
curl.OptionSSLVersion = 6
curl.OptionUseSSL = 3

dim e as integer = curl.Perform

MsgBox "Error code: "+str(e)[/code]

After many many test… I found the trouble; the critical part is the imap folder name set into OptionURL.

First of all, the IMAP sent folder name is localized therefore it depends from the used language. After this, as if that was not enough, on different servers with the same language, the sent folder name could be different, because there’s not a standard.

So, how to know the sent folder? The following code scan the imap server and return a folder list:

dim curl as new CURLSMBS

curl.CollectOutputData = true
curl.OptionURL = "imaps://imap.gmail.com/"
curl.OptionUsername = "yourUserName"
curl.OptionPassword = "yourPassword"
dim dq as String = chr(34)
dim command as String = "LIST " + dq + dq + " " + dq + "*" + dq
curl.OptionCustomRequest = command

dim e as integer = curl.Perform
dim folderList() as String

if e<>0 then
  MsgBox "Error code: "+str(e)
else
  dim content as string = curl.OutputData
  if content.Encoding = nil then
    content = DefineEncoding(content, encodings.ISOLatin1)
  end if
  content = ReplaceLineEndings(content, EndOfLine)
  dim lines() as string = Split(content, EndOfLine)
  dim preFolderMarker as String = dq+"/"+dq
  for each line as string in lines
    if NthField(line," ",1) = "*" then
      folderList.Append(ReplaceAll(trim(NthField(line, preFolderMarker, 2)),dq,""))
    end if
  next
  MsgBox join(folderList,EndOfLine)
end if

Based on the language of Gmail interface, you can obtain different results and the sent folder could be:

Spanish -> [Gmail]/Enviados
Deutsch -> [Gmail]/Gesendet
Italian -> [Gmail]/Posta inviata
English -> [Gmail]/Sent Mail

Now you have the correct imap sent folder name and you have to use it (url encoded) into the “OptionURL” parameter, so if you have for example Gmail set in english your “OptionURL” will be:

curl.OptionURL = "imaps://imap.gmail.com/" + EncodeURLComponent("[Gmail]/Sent Mail")

and if you debug your code you’ll see the curl.OptionURL as

imaps://imap.gmail.com/%5BGmail%5D%2FSent%20Mail

That’s all.
Thanks at all for your contributions.

NOTE: Be careful to do tests with Gmail and use only a test account; trying to do my tests, Google has disable my account because they have traced some suspected activities. After that, I have done the procedure to re-enable the account.

Well, can’t you query list of files and isn’t there a flag with each folder including one which makes a folder as sent folder?

The folder name depends from the language and I haven’t find a standard way. After that, in my app I want to have the feature to configure the user profile browsing its imap account and choose the folder in which to save email sent from my app.

I get an output here like this:

[quote]* LIST (\HasChildren) “.” INBOX

  • LIST (\HasNoChildren \Sent) “.” INBOX.Sent[/quote]

with a query ending in / for the IMAP server.

So my inbox is named INBOX.Sent and marked with \Sent in the flags.

[quote=412197:@Christian Schmitz]I get an output here like this:

with a query ending in / for the IMAP server.

So my inbox is named INBOX.Sent and marked with \Sent in the flags.[/quote]
This is true in your inbox, but not in all. If you try for example in Gmail, you will get a different result.

In Gmail the Inbox and Sent Messages mailboxes are localised. The flags are different from the mailboxes and these are NOT localised.

From the query list you can use the flag with \Sent to get automatically the correct line, but into the curl.OptionURL you must put the mailbox name, not the flag, so what we are speaking for is only the method to retrieve the sent folder name.