FORTH: POP3 nanoclean. Part 2

the second part will try to write a minimal POP3 client. He will be able to connect to the server, login to the mailbox to know how many letters and download the latest. For illustration, it will be enough.

It is sufficient to know of the POP3 Protocol. A couple of teams need to PASS USER login STAT to get the number of letters in a box and RETR to retrieve the letter. We won't delete the message with the DELE command. The QUIT command will give out of politeness.
Look at the team more closely. You may notice that they can be divided into two groups: commands with no arguments a NOOP QUIT RSET, and STAT commands with argument: DELE LIST RETR USER PASS APOP TOP. And the last three as argument require text strings. Strictly speaking, all commands require a string argument. In one case it is an empty string, the other string consists of decimal digits, the third of arbitrary characters.

Consider the simplest case, a parameterless command. It is possible to write in the forehead

S" NOOP" sockt WriteSocketLine THROW and so 4 times. 
S" DELE" sockt WriteSocket THROW S > D (D.) sockt WriteSocketLine THROW 
3 times too

( TOP APOP won't implement, but USER PASS is not going to split hairs. )


But we also write on the Fort, and in it there is a graceful mechanism for creating defining words. Use it.
For starters, take sockt WriteSocketLine THROW and buf bufsize sockt ReadSocket THROW TO bytes_read in a separate definition. Too lazy to have to write so many letters.
: >sockt 
sockt WriteSocketLine THROW ;

: sockt>
buf bufsize sockt ReadSocket THROW TO bytes_read ;



And begin to create creating

: pop3_noarg 
CREATE LATEST , 
DOES> @ COUNT >sockt ; 

: pop3_noargs 
Pop3_noarg 0 DO LOOP ;

4 pop3_noargs NOOP QUIT RSET STAT 


Thus, three lines, we identified four similar teams. Let me explain a little more detail what's happening here.
After the colon defines the new word pop3_noarg, with the following action: to select from the input stream characters bounded by whitespace, to use them to create on top of a dictionary word with the action VARIABLE. This all makes the word CREATE, in the moment the words pop3_noarg. Following the LATEST word puts on stack the address of the name field-defined words, such as NOOP. The comma compiles the value from the stack to the top of the dictionary. If next was not part of the DOES>, the call is a NOOP would be put on the stack an address, razmanova we would receive the address of a counted string, turning that into a string address counter word COUNT we could print it with the word TYPE.
The result would see NOOP. Words seems very difficult, hurry to try it yourself at the command prompt Fort.br> What next? Next, we present the most mysterious word of the Fort DOES>. It is enough to realize that this word changes the action by default, the words created using the CREATE action, which we wish. Everything that is written after DOES> is a generic activity for all of the words defined via pop3_noarg. Here it is to get the address and counter of the row name word and sending it to the socket.

: pop3_digiarg 
CREATE LATEST , 
DOES> @ >R <# S>D #S BL HOLD R > COUNT HOLDS # > > sockt ;

: pop3_digiargs pop3_digiarg 0 DO LOOP ;

3 pop3_digiargs DELE LIST RETR 


Very similar to the previous group of words with a noticeable difference in the part of the DOES>. It is clear that something is sent to the socket. But what?
Here we use another one great opportunity of the Fort. We have a beautiful word (D.) converts a double-precision number (64 bits) from the stack to a string. And we could easily use, but we'd have to write something like

S" LIST" WriteSocket sockt S > D (D.) >sockt


Not terrible, but somehow dull.

Since we decided to make beautiful, why not look for other solutions. If you look at the source code of the Fort, and find the definition (D.) you can see what it consists of <# #S #>. These three words invented by Chuck Moore and laid out conversion of a number to a string in three phases. The first training transformation second — the actual transformation, and the third is the results result. Due to the fact that the conversion number is with the younger discharge, and the print goes from a higher level — to convert need some temporary buffer where will be stored the numbers and how to get the result. The size of the buffer is taken with a good margin. The word HOLD just removes the next character from the stack into the buffer. HOLDS, the plural of HOLD does the same thing with multiple characters, aka a string. A couple of words >R R> is used as a light improvised implementation of local variables when you need a value from the stack hold for a time.

Now look at the server responses. There are only two: +OK or -ERR, which can go and can not go any text or numbers. Would be lovely if the server responses themselves somehow did all the work. So we don't have to jump through the endless branchings. And Fort can we afford it.
Imagine a buffer, which saved the server's response as an input stream for interpretation. The word EVALUATE, we can interpret an arbitrary string. Use this.
The server's response always starts with +OK or -ERR. Attach same meaning in these responses.

Easiest with -ERR. In case of error, it would be nice to bring the remainder of the string to the terminal and to interrupt the execution of the program.

 : -ERR 1 PARSE TYPE CR ABORT ; 


PARSE is used to obtain the interpreted tail of the string.

OK not so simple. He always POPs up if the command was processed successfully. Tells us all is well, but what is the context of this okay?
The first +OK reports a successful connection to the server. We need to accept this fact and bring to the terminal greeting the server.
The second and third and OK occur in the procedure login. Just ignore the rest of the line.
Fourth +OK appears in response to a STAT for him, for +Okey, accompanied by two numbers. The first is the number of messages in the mailbox. The second is the number of parrots occupied them. Parrots we don't need. So we need to convert the text number from the server response in the number on the stack, and the rest of the line is ignored.
Fifth +OK tell me what will be behind him we need the letter. There is one caveat in the Protocol. First, the interval between the server's response and sending the letter. Second, the two numbers after the +OK and the message number and its size in parrots.
Sixth +OK, can't wait, and you can handle as well as the first.
So, +OK should be able three different actions.
Fort eliminates the need for flags and complex logic. It is the so-called vector of words. Words, the effect of which can be assigned at any desired moment.

VECT +OK

: do_type 1 PARSE TYPE CR ;
: do_ignore 1 PARSE 2DROP ;
: do_number 1 PARSE ?SLITERAL ;
: do_msg 
sockt> 
BEGIN buf bytes_read TYPE CR 
bytes_read bufsize - 0< 0= WHILE sockt> REPEAT do_ignore ;



We now have everything to assemble the code together.

Code

~nn/lib/sock2.f 

0 VALUE sockt
0 VALUE bytes_read
0 VALUE num_of_msgs

CONSTANT bufsize 8192
bufsize ALLOCATE THROW CONSTANT buf

MODULE:

: >sockt 
sockt WriteSocketLine THROW ;

: sockt>
buf bufsize sockt ReadSocket THROW TO bytes_read ;

response 
sockt>
buf bytes_read EVALUATE ;


: pop3_noarg 
CREATE LATEST , 
DOES> @ COUNT > sockt response ; 

: pop3_noargs 
Pop3_noarg 0 DO LOOP ;

4 pop3_noargs NOOP QUIT RSET STAT


: pop3_digiarg 
CREATE LATEST , 
DOES> @ >R <# S>D #S BL HOLD R > COUNT HOLDS # > > sockt response ;

: pop3_digiargs pop3_digiarg 0 DO LOOP ;

3 pop3_digiargs DELE LIST RETR


: user S" USER username" >sockt response ;
: S pass" PASS password" >sockt response ;


VECT +OK

: do_type 1 PARSE TYPE CR ;
: do_ignore 1 PARSE 2DROP ;
: do_number 1 PARSE ?Do_ignore SLITERAL ;
: do_msg 
sockt> 
BEGIN buf bytes_read TYPE CR 
bytes_read bufsize - 0< 0= WHILE sockt> REPEAT do_ignore ;

: -Do_type ERR, ABORT ;


: start_sockets 
SocketsStartup THROW
CreateSocket THROW TO sockt ;

: connect_to_host
GetHostIP THROW 
110 sockt ConnectSocket THROW 
response ;

\ Below executable code. Above - necessary definitions.

start_sockets

'do_type TO +OK 

S" pop.server.com" connect_to_host 

'do_ignore TO +OK

user pass 

'do_number TO +OK 

STAT TO num_of_msgs

'do_msg TO +OK 
num_of_msgs RETR 

'do_type TO +OK
QUIT


This code is easy to add, so he drained all or some number of messages from the server are stored on the local disk was removed from the server, etc. Here I tried to demonstrate important programming in the forth language, the principle of decomposition, developed and described by Leo Brodie, and some existing in the language tools to solve problems.

FORTH: nanosilver and nanoclay. Part 1

PS: When writing a topic Habr several injured.
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

Briefly on how to make your Qt geoservice plugin

Database replication PostgreSQL-based SymmetricDS

Developing for Sailfish OS: notifications for example apps for taking notes