2

I am writing a script which can send E-Mails with LuaSocket SMTP. Now I would like to make my script also be able to read my E-Mails.

I have an E-Mail account at a service which supports IMAP.

IMAP host and port: example.com:143
My E-Mail: doesnotexist@example.com
The password: notsaying

How can I make my script fetch an E-Mail from that service so I can display its contents?

Edit:

I have tried imap4 from https://github.com/vrld/imap4.lua but does not seem to be finished.

I have tried this example:

require 'luarocks.require'

local imap4 = require 'imap4'

local username = "doesnotexist@example.com"
local password = "notsaying"

local connection = imap4('example.com', 143)

print(table.concat(connection:capability(), ', '))
assert(connection:isCapable('IMAP4rev1'))

connection:login(username, password)

for mb, info in pairs(connection:lsub()) do
    local stat = connection:status(mb, {'MESSAGES', 'RECENT', 'UNSEEN'})
    print(mb, stat.MESSAGES, stat.RECENT, stat.UNSEEN)
end

local info = connection:examine('INBOX')
print(info.exist, info.recent)

for _,v in pairs(connection:fetch('UID BODY.PEEK[HEADER.FIELDS (From Date Subject)]', (info.exist-3)..':*')) do
    print(v.id, v.UID, v.BODY.value)
end

connection:logout()

I only added username and password and changed the server and port from the original example and changed the most recent to fetch from 4 to 3. I have also removed the comments, as they are many. Other than those I did not modify anything else, it is basically the same example.

It seems to log in correctly using my credentials and the correct server and port, but it has some kind of problem which I am unable to debug.

The above example outputs:

IMAP4rev1, CHILDREN, NAMESPACE
INBOX.Sent  3   0   0
INBOX   4   0   0
4   0
lua: example.lua:24: attempt to index field 'BODY' (a nil value)
stack traceback:
    example.lua:24: in main chunk
    [C]: ?

It shows that I have 4 E-Mails in my inbox and 3 in my sent, which is correct.

  • 1
    Have you tried anything? Have you read the documentations? Did you try some code and failed? – hjpotter92 Jun 17 '14 at 09:22
  • The comment in the original example mentions "List info on the 4 most recent mails" when it refers to `(info.exist-4)..':*'`. Since you only have four emails in your inbox maybe you should try a lower number than 4 ... – siffiejoe Jun 17 '14 at 10:41
  • You are right, updated my question. I changed -4 to -3. Now there is a new problem, it does not find the field "BODY". I have verified that all 4 E-Mails have a body. –  Jun 17 '14 at 11:08
  • may be you can use just pop3 optionaly with tls (https://github.com/moteus/lua-pop3)? I use this lib in service which polls mail server. Also you can recv rfc822 via imap and parse it using pop3.message class. i can post example tomorrow – moteus Jun 17 '14 at 15:52
  • Unfortunately I am unable to use POP3, as the server does not support it. An example of the pop3.message class to parse it would be highly appreciated. –  Jun 17 '14 at 16:17

2 Answers2

1

This example use imap4 library to get message and pop3.message to parse.

local imap4   = require "imap4"
local Message = require "pop3.message"

local connection = imap4('imap.qip.ru', 143)

assert(connection:isCapable('IMAP4rev1'))

connection:login('****', '****')

-- Select INBOX with read only permissions.
local info = connection:examine('INBOX')
print(info.exist, info.recent)

-- List info on the 4 most recent mails.
for _,v in pairs(connection:fetch('RFC822', (info.exist-4)..':*')) do
    print("-------------------------")
    local msg = Message(v.RFC822)
    print("ID:         ", msg:id())
    print("subject:    ", msg:subject())
    print("to:         ", msg:to())
    print("from:       ", msg:from())
    print("from addr:  ", msg:from_address())
    print("reply:      ", msg:reply_to())
    print("reply addr: ", msg:reply_address())
    print("trunc:      ", msg:is_truncated())
    for i,v in ipairs(msg:full_content()) do
        if v.text then  print("  ", i , "TEXT: ", v.type, #v.text)
        else print("  ", i , "FILE: ", v.type, v.file_name or v.name, #v.data) end
    end
end

-- close connection
connection:logout()
moteus
  • 2,187
  • 1
  • 13
  • 16
  • Thank you, I will accept this answer. I'm not very experienced with IMAP, this answer helped me get it working with a (apparently) non-standard compliant IMAP server, or at least to me it seems non-standard. I will post an answer below explaining why the example did not work in my question and how to fetch without RFC822 if it does not work with the server. –  Jun 18 '14 at 11:23
0

Try moeus's solution first. Only bother with my example if the server you need to use does not support RFC822 or is generally not standard compliant.

In this answer I'll explain briefly why the original example did not work and also give an example without RFC822 as the server that I have to work with does not understand the RFC822, and many other keywords that it should understand according to the IMAP 4 standard

In the example I had to change:

connection:fetch('UID BODY.PEEK[HEADER.FIELDS (From Date Subject)]', (info.exist-3)..':*'))

to:

connection:fetch('(UID BODY.PEEK[HEADER.FIELDS (From Date Subject)])', (info.exist-3)..':*'))

Notice the parentheses, that's why the example did not work.

Moteus's answer with RFC822 does not work for me, because the server I have to work with is non-standard. Here's how I got it to work:

local imap4   = require "imap4"
local Message = require "pop3.message"

local connection = imap4('imap.qip.ru', 143)

assert(connection:isCapable('IMAP4rev1'))

connection:login('****', '****')

-- Select INBOX with read only permissions.
local info = connection:examine('INBOX')
print(info.exist, info.recent)

-- List info on the 4 most recent mails.
for _,v in pairs(connection:fetch('(UID BODY.PEEK[HEADER.FIELDS (Subject To From Date)])', (info.exist-4)..':*')) do
    print("-------------------------")
    local msg = Message(v.BODY.value)
    print("ID:         ", msg:id())
    print("subject:    ", msg:subject())
    print("to:         ", msg:to())
    print("from:       ", msg:from())
    print("from addr:  ", msg:from_address())
    print("reply:      ", msg:reply_to())
    print("reply addr: ", msg:reply_address())
    print("trunc:      ", msg:is_truncated())
    for i,v in ipairs(msg:full_content()) do
        if v.text then  print("  ", i , "TEXT: ", v.type, #v.text)
        else print("  ", i , "FILE: ", v.type, v.file_name or v.name, #v.data) end
    end
end

-- close connection
connection:logout()

Unlike with Moteus's example, you will have to fetch the text separately.

You can fetch the text of a mail like so:

connection:fetch('(BODY.PEEK[TEXT])', id)
Community
  • 1
  • 1