# Display IMAP server message when writing a command to it

**URL:** <https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291>\
**Category:** Specific Domains\
**Tags:** networking\
**Created:** [March 1, 2021, 10:52pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291 "2021-03-01T22:52:36Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![Chen](https://avatars.discourse-cdn.com/v4/letter/c/3d9bf3/32.png) [@Chen](https://discourse.julialang.org/u/Chen)\
**Post date:** [March 1, 2021, 10:52pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/1 "2021-03-01T22:52:37Z")

</div>

I’m trying to hack out some codes to fetch emails from IMAP server.

I’m able to connect to an IMAP server and send a message to the server  
I changed the user name and the password in the code below, because I do not wan to put my user name and password on a public forum.

```julia
julia> using Sockets

julia> imap = connect("imap.139.com", 143)
TCPSocket(RawFD(0x00000049) open, 0 bytes waiting)

julia> print(readline(imap))
* OK RICHINFO System IMap Server Ready (caixuns[3764603d6f0ab88-00000])

julia> write(imap, "01 AUTH user_foo password_bar")
30

```

So I am able to communicate by using `write()`. However, I do not get the nice message back from the IMAP server. Julia gave me the output of `30`, which I think is the length of the message `01 BAD errno:1404, imap command error` [1].

The question is, how do I receive the entire message from the server, instead of 30?

[1] I got the message by using telnet to test the IMAP server, not from within Julia.

---

<div class="post-metadata">

**Author:** ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)\
**Post date:** [March 1, 2021, 11:03pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/2 "2021-03-01T23:03:48Z")

</div>

I believe 30 here is the number of bytes written. What I think you want to do after the `write()` is a `readline(imap)` when will read a single line back from the server.

EDIT: Thinking about it, since the interface seems to be text you probably want to do:

```julia
println(imap, "01 AUTH user_foo password_bar")

```

Instead of `write` so that a `\n` is sent to the server, it may be waiting for that to complete the line of text.

---

<div class="post-metadata">

**Author:** ![Chen](https://avatars.discourse-cdn.com/v4/letter/c/3d9bf3/32.png) [@Chen](https://discourse.julialang.org/u/Chen)\
**Post date:** [March 1, 2021, 11:12pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/3 "2021-03-01T23:12:52Z")

</div>

Thank you!

`println()` did not return any output. But it could be that `println()` was the better option.

I found this old post from 2019: [Connect to secure server using secure(SSL) sockets(plan tcp client)](https://discourse.julialang.org/t/connect-to-secure-server-using-secure-ssl-sockets-plan-tcp-client/28465). Maybe @universe68 got the model right, that there needs to be a two threads, where one thread listens to the output from the server (in my case IMAP server)?

---

<div class="post-metadata">

**Author:** ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)\
**Post date:** [March 1, 2021, 11:16pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/4 "2021-03-01T23:16:59Z")

</div>

Two threads wouldn’t hurt, but technically you only need them if the server is sending you asynchronous events. If the server **only** sends you a message in response to you sending it a message then 1 thread is fine. (Also assuming you can tell how many lines the server is sending back in response.)

So you need to do a `println()` to send a request to the server and a `readline()` to get the server’s response. If the imap server sends back multiple lines in response then you would have to do multiple `readline`s to get the full response back.

With multiple threads you would send a response, then have to wait on your response thread for it to get the message back, which is basically what you are doing with println/readline pairs.

---

<div class="post-metadata">

**Author:** ![Chen](https://avatars.discourse-cdn.com/v4/letter/c/3d9bf3/32.png) [@Chen](https://discourse.julialang.org/u/Chen)\
**Post date:** [March 1, 2021, 11:21pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/5 "2021-03-01T23:21:35Z")

</div>

The challenge is that `readline()` does not give anything after `println()` or `write()`. Sorry I didn’t manage to convey this issue.

So after `println()` or `write()`, if I use `readline(imap)`, I do not get any response.

Could you maybe try to run this piece of codes? The user name and password are made up, but they suffice for the purpose of testing, because the problem right now is not able getting any message back after first `println()` or `write()`. Of course the message will be an error message, if one manages to obtain it.

```julia
using Sockets
imap = connect("imap.139.com", 143)
readline(imap)
println(imap, "01 AUTH someuser someepassword")
readline(imap)

```

---

<div class="post-metadata">

**Author:** ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)\
**Post date:** [March 1, 2021, 11:23pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/6 "2021-03-01T23:23:05Z")

</div>

I ran the code and readline() is returning which means the server didn’t send a response back…let me play with it.

---

<div class="post-metadata">

**Author:** ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)\
**Post date:** [March 1, 2021, 11:24pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/7 "2021-03-01T23:24:15Z")

</div>

Okay the IMAP protocol is terminated with `\r\n` so you need to do:

```julia
write(imap, "01 AUTH someuser someepassword\r\n")

```

---

<div class="post-metadata">

**Author:** ![Chen](https://avatars.discourse-cdn.com/v4/letter/c/3d9bf3/32.png) [@Chen](https://discourse.julialang.org/u/Chen)\
**Post date:** [March 1, 2021, 11:24pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/8 "2021-03-01T23:24:20Z")

</div>

Thank you! It worked!

Using `\r\n` did the trick!

By the way, I made a mistake in the IMAP command. I should have used `login`, instead of `auth`.

---

<div class="post-metadata">

**Author:** ![Chen](https://avatars.discourse-cdn.com/v4/letter/c/3d9bf3/32.png) [@Chen](https://discourse.julialang.org/u/Chen)\
**Post date:** [March 1, 2021, 11:30pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/9 "2021-03-01T23:30:55Z")

</div>

By the way, I found it challenging to obtain multi-line response from an IMAP server.

For example, the previous code only returns one line of response. If I run `readline(imap)` twice, in the second time, `readline()` tries to read a line until the connection is broken.

I tried to use this `while()` loop, however, the loop would still hang until the connection is broken.

```julia
while (! eof(imap))
   print(readline(imap))
end

```

I just thought it would be a good opportunity to try to get your opinion on this here. But I can also open a new thread to ask this question!

---

<div class="post-metadata">

**Author:** ![Chen](https://avatars.discourse-cdn.com/v4/letter/c/3d9bf3/32.png) [@Chen](https://discourse.julialang.org/u/Chen)\
**Post date:** [March 1, 2021, 11:42pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/10 "2021-03-01T23:42:19Z")

</div>

Sorry. Gotta go to bed. To be continued tomorrow.

---

<div class="post-metadata">

**Author:** ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)\
**Post date:** [March 1, 2021, 11:48pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/11 "2021-03-01T23:48:05Z")

</div>

A quick look at the example of the protocol here [Internet Message Access Protocol - Wikipedia](https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol)  
It seems like each command is terminated with a “[id] OK [something] completed” so what I would probably do is write a method like:

```julia
function readresponse(imap, id, cmd)
    full = Vector{String}()
    done = false

    while !done 
        line = readline(imap)
        push!(full, line)
        parsed = split(line)
        if parsed[2] == "BAD"
            done = true
        elseif length(parsed) == 4 && parsed[1] == id && 
               parsed[2] == "OK" && parsed[3] == uppercase(cmd) &&
               parsed[5] == "completed"
            done = true
        end
    end

    return full 
end

```

That should just read the response lines and stop if the response indicates an error or when it sees the completed line.

You might also want to update the `if` to detect the the “Ready” line you get when you connect to the server.

---

<div class="post-metadata">

**Author:** ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)\
**Post date:** [March 2, 2021, 12:22am UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/12 "2021-03-02T00:22:24Z")

</div>

Another option, and I know this now goes threads and I tried to steer you away from that, but this is kind of clean, and I like clean…

```julia
struct Response
    id::String
    error::String
    data::Vector{String}
    Response(id, err::String) = new(id, err, Vector{String}())
    Response(id, data) = new(id, "", data)
end

using Sockets
imap = connect("imap.139.com", 143)
readline(imap)

responses = Channel() do ch
    current = Vector{String}()

    while !eof(imap) do
        line = readline(imap)
        parse = split(line)

        if length(parse) >= 2 && parse[2] == "BAD"
            put!(ch, Response(parse[1], line)
        elseif length(parse) > 2 && parse[2] == "OK" && parse[end] == "completed"
            put!(ch, Response(parse[1], current)
            current = Vector{String}()
        else
            push!(current, line)
        end            

    end
end

function imapwrite(id, cmd, data)
    write(imap, "$id $cmd $data\r\n");
    response = take!(responses)
    @assert response.id == id "Messages desynchronized, got $(response.id) expecting $id"
    @assert response.error == "" response.error
    return response.data
end

imapwrite("01", "auth", "someuser someepassword")
inbox = imapwrite("02", "select", "inbox")
dump(inbox)

```

I’m not 100% there are no errors in there, I just wrote it in discourse so I make no claims about correctness. It would also probably be better if the `imapwrite()` method generated the next ID but I’ll leave that to you.

---

<div class="post-metadata">

**Author:** ![Chen](https://avatars.discourse-cdn.com/v4/letter/c/3d9bf3/32.png) [@Chen](https://discourse.julialang.org/u/Chen)\
**Post date:** [March 2, 2021, 9:20pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/13 "2021-03-02T21:20:36Z")

</div>

Thank you!

I never used threading on Julia. I’m studying the codes!

---

<div class="post-metadata">

**Author:** ![Chen](https://avatars.discourse-cdn.com/v4/letter/c/3d9bf3/32.png) [@Chen](https://discourse.julialang.org/u/Chen)\
**Post date:** [March 2, 2021, 10:09pm UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/14 "2021-03-02T22:09:41Z")

</div>

> [@pixel27](#):
>
> ```julia
> function readresponse(imap, id, cmd)
> full = Vector{String}()
> done = false
> 
> while !done 
> line = readline(imap)
> push!(full, line)
> parsed = split(line)
> if parsed[2] == "BAD"
> done = true
> elseif length(parsed) == 4 && parsed[1] == id && 
> parsed[2] == "OK" && parsed[3] == uppercase(cmd) &&
> parsed[5] == "completed"
> done = true
> end
> end
> 
> return full 
> end
> 
> ```

Your codes were quite good. I only needed to modify one place.

For example, here is the response from the IMAP server for a login command:

```julia
01 OK login completed

```

This means that the server only responded the action word of the command, in this case, `login`. And it is not in uppercase. It is not certain whether an other server would return `LOGIN` instead. Therefore, it’d be safer to use lowercase on both sides when comparing the strings.

Another implication is that `parsed[4]` needs to be compared with `completed`.

Because of that, the `elseif` part of your function needs to be written as

```julia
        elseif length(parsed) == 4 && parsed[1] == id && 
               parsed[2] == "OK" &&
               lowercase(parsed[3]) == lowercase(split(cmd)[1]) &&
               parsed[4] == "completed"
            done = true

```

I tested the function after modification and it runs well.

I haven’t extensively tested it yet.

I gotta go to bed, and will read the threading version and get back to you.

---

<div class="post-metadata">

**Author:** ![Marius\_Kruger](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius_kruger/32/904_2.png) [@Marius\_Kruger](https://discourse.julialang.org/u/Marius_Kruger)\
**Post date:** [February 16, 2023, 8:02am UTC](https://discourse.julialang.org/t/display-imap-server-message-when-writing-a-command-to-it/56291/15 "2023-02-16T08:02:20Z")

</div>

It would be nice to start writing a julia imap client package based on this. I might be tempted to start one day if nobody beats me to it…
