Chapter 35

Creating a Custom, Integrated Application with Multiple Protocols

by Antonio Miguel Ferreira


CONTENTS

This chapter describes the creation of a Web application that deals with multiple Internet protocols. It covers the basics of building such an application and gives you some ideas for enhancements that could be used on your own multi-protocol based applications.

The application described here is called WebPOP, a Web-based mail reader.

A Multi-Protocol Application

Careful planning of a multi-protocol application should be finished before you start to write the program code. If you take some time to think about the application you want to write, you could save many hours of work. Here is a checklist you can follow to write your own multi-protocol applications:

A Multi-Protocol Application on the Web

Web servers and Web browsers exchange information using the HyperText Transfer Protocol (HTTP), but several Internet services are based on other well-known protocols such as SMTP (mail), NNTP (news), FTP (file transfer), and so on. Fortunately, there are ways to exchange information between different servers and to present it all under the same Web-based interface. The integration of several protocols in one application requires a careful design process from the programmer and, most important of all, a good knowledge of the protocols and languages used. A common characteristic of multi-protocol Web-based applications is the use of the CGI (Common Gateway Interface) specifications. Unless an application uses a proprietary Application Programming Interface (API), it probably uses the CGI because it is the standard way of communication between a Web server and a custom application.

CGI applications can be written in virtually any computer language, such as Perl and C; it is just a matter of using their communication possibilities (using the sockets library, for example, available for all major platforms) in order to write an application that talks with different servers and outputs the results in HTML format. This is the idea behind multi-protocol applications based on the Web: Talk with other applications; treat the information they provide; and present the results in HTML.

One of the advantages of using CGI applications is that you don't have to worry about making a version for every known platform. Because the program is executed in the server-side, and because the output is in standard format (HTML, plain text, and so on), CGI is a good choice for creating multi-platform applications.

WebPOP

To illustrate the use of an integrated application with multiple protocols, presented here is a CGI program called WebPOP (POP stands for Post Office Protocol, the most common Internet protocol for mailbox contents retrieval). It is a very simple and practical mail-reading program that gets user input and presents results formatted in HTML so that the user's browser can display it correctly.

WebPOP was created to fill the need to offer Internet users an easy-to-use mail program. Internet and the World Wide Web are attracting a lot of new users, most of whom are not computer experts-some aren't even computer literate. These users need a very simple mail program that they can use to process a few messages per day. The use of a standard Web browser, along with WebPOP on the server-side, eliminates the need for a special purpose (platform-dependent) mail reader. These are generally good mail readers, but they frequently offer more possibilities than many newcomers need or want.

On the client side (the user side), the only piece of software needed to use such an application is a common Web browser. On the server side, the Web server must comply with the CGI specification, as do all well-known Web servers today.

WebPOP is a CGI application written in Perl (version 4.036), a very useful scripting language commonly used for CGI development. The complete source code of this application is listed at the end of the chapter. If Perl does not come with your operating system, you should consider installing it. To try it out, take a look at the Web site whose address is


http://www.perl.com

NOTE
You might need the help of the Webmaster of your site if you do not have direct access to the cgi-bin directory (in which you put programs and scripts that will be executed in the Web). Also, it is important to know the exact locations (paths) of the finger, grep, and smail applications as well as the name of your mail server. See Listing 35.1 for more details.

Entities

An entity is a program executing a particular function; it is independent from other software components on the system. There are five entities that participate in the functioning of the application presented in this chapter: the CGI application itself (WebPOP), the user's Web browser, the Web server, the POP3 server, and the SMTP server. See Figure 35.1 for an illustration of the participating entities.

Figure 35.1 : The entities and protocols of the integrated application.

Protocols Used

In order to provide the functionality expected from a mail reader, your application must be able to use multiple protocols:

At the same time, the Web browser and server communicate with each other using the standard HTTP protocol. The integration of all these protocols is done via CGI, the standard specification that permits the dialog between the Web server and the application. Finally, WebPOP formats the results in HTML and sends them to the Web server that is responsible for forwarding it to the user's Web browser. The protocols are also illustrated in Figure 35.1.

Mail Functions

A mail program must offer some core functions-it must be able to

The following list shows the functions in detail:

user authenticationA mailbox contains messages that are private to a given user. The mail program must confirm the identity of a user before retrieving that user's messages. The authentication function is the process of asking the user her username and password, and then verifying the information provided against a list of authorized users.
check mailbox contentsWhen the user wants to read incoming mail, the mail program checks the electronic mailbox and presents a list of the messages sent to that user.
read a mail messageWhen the user wants to read a particular message, the mail program displays it on the screen.
send a mail messageWhen the user wants to send a message to someone, either a reply or a new message, the mail program forwards it to its destination.

WebPOP Functioning

Having defined the entities, protocols, and core functions that the integrated application must offer, take a look at the overall functioning. The CGI application must receive orders from the Web server (transmitted by the Web browser, because it is the only entity taking initiatives) and must communicate with the SMTP and POP3 servers. The results obtained are sent back to the Web server and finally to the Web browser in HTML format.

In addition to operating under normal circumstances (consulting a mailbox or sending messages), the application must also operate in abnormal situations, such as facing an unknown user, receiving a bad password or, more critically, dealing with a communications error. In fact, you must try to make the application as robust as possible because it will probably be used by many people at the same time, day and night. Special care should be taken to predict every possible execution sequence of a Web application, under both normal and abnormal circumstances. If possible, test the application using different browsers, too, and check to see that everything runs as expected.

See Figure 35.2 for an illustration of the functioning of the application.

Figure 35.2 : The functioning of the application.

Here is a description of the steps described in Figure 35.2:

  1. Original HTTP request is received from Web browser.
  2. Server runs CGI script with GET method.
  3. CGI script sends back initial form to Web server.
  4. Server forwards initial form to browser.
  5. User authenticates himself and requests list of messages in mailbox.
  6. Server runs CGI script with POST method.
  7. CGI script talks to POP3 server for client authentication and retrieval of messages (if authentication is successful).
  8. POP3 server returns list of messages.
  9. CGI script creates a list box and sends the new form to the Web server.
  10. Web server forwards message list form to the browser.
  11. User asks to read a message from the list.
  12. Server runs CGI script with POST method.
  13. CGI script talks to POP3 server for message body retrieval.
  14. POP3 server returns message body.
  15. CGI script creates a page with the message and sends it back to the server.
  16. Web server forwards the message page to the browser.
  17. User asks to send a message.
  18. Web server runs CGI script with POST method.
  19. CGI script talks to SMTP server in order to send the message.

Getting Data from the User

The user of the CGI application must provide information concerning himself or his mailbox. This is entered in the data fields of HTML forms; and when completed, the form is sent (submitted) to the application. The form is then decoded by a function of the cgi-lib.pl library, called ReadParse. It takes a reference to a hash table (you can think of a hash table as an array in which elements are related two by two, the key and the key's corresponding value) as an argument and retrieves the hash table in which each form field name is a key. The value of a field is the value associated with the hash key.

In the application, the hash table that stores the form fields data is called input. Every form is sent to the Web server with the POST method, which means the function ReadParse must read data from standard input (instead of reading the environment variable QUERY_STRING, if the method GET were used). This function is one of the general CGI application functions found in the useful cgi-lib.pl, which is included in this book's CD-ROM.

The following is an example of a form using the SendNewMessage function:


     $fromaddr = $input{'login'}.'@'.$mailserver;

     print <<EOM;

<HTML>

<HEAD>

<TITLE>New message</TITLE>

<BODY BACKGROUND="$pathBackground">

</HEAD>

<BODY>

<FORM ACTION="$url" METHOD=POST>

<P>

<CENTER>

<B>De:</B> $fromaddr<P>

<B>Para (email):</B> <INPUT VALUE="$oldTo" NAME=to SIZE=50><P>

<B>Assunto:</B> <INPUT VALUE="$oldSubject" NAME=subject SIZE=50><P>

<INPUT TYPE=image SRC="$pathButtonSend" NAME=send BORDER=0

WIDTH=$bwidth HEIGHT=$bheight>

<INPUT TYPE=image SRC="$pathButtonReturnMailbox" NAME=return BORDER=0

WIDTH=$bwidth HEIGHT=$bheight>

<P>

<TEXTAREA ROWS=18 COLS=70 NAME=body>$body</TEXTAREA>

</CENTER>

<INPUT TYPE="hidden" VALUE="$input{'login'}" NAME=login>

<INPUT TYPE="hidden" VALUE="$input{'password'}" NAME=password>

</FORM>

</BODY>

</HTML>

Using Hidden Form Fields To Pass Arguments

When a Web browser requests a document from a Web server, a connection (using the HTTP-over TCP-protocol) must be established between the two parties. After the required page is sent to the browser, the connection and any information concerning this connection are lost.

But, each time the user asks the CGI script to perform an action (such as deleting a message from the electronic mailbox), there is data that the CGI script must know, such as the user's login name or password; otherwise, it cannot identify the correct mailbox.

Due to the design of HTTP protocol described earlier (which is transaction-oriented), a Web server is stateless. WebPOP is also stateless, which means that no user information is kept by the application (on a separate file or database) during the interval between connections. If you want to store information between two consecutive requests from the same user, some sort of keep-alive mechanism needs to be provided between the browser and the server in order to keep a connection as long as needed. You can also use the functionality provided by cookies, which are special bits of data constructed by the server that the browser keeps and sends to the server at each request).

It is up to the browser, or to the documents transferred between client and server, to keep any relevant user information. To save data, I have chosen to use hidden form fields (invisible to the user) on an HTML page. The form itself is sent to the CGI script with the POST method, read from stdin (stdin stands for standard input, and represents the "place" from which input data is obtained), and then decoded in order for all relevant data to be obtained. This way, you can transparently keep every important data field on the user's side; it will be sent to WebPOP each time there is a request on behalf of the user.

Sending Results Back to the Browser

Output from the CGI script is always in HTML format so that the browser can display it correctly. WebPOP is capable of producing a limited number of HTML pages. In normal functioning, one of the following pages is displayed on the browser:

Figure 35.3 : WebPOP's initial page.

Additionally, the following are some status pages that report successful operation or errors:

Before receiving and displaying a page, the browser must identify its contents. The page contents are identified by the CGI application, which sends the MIME-type header Content-Type: text/html, followed by two newline characters \n before any HTML tags.

Dealing with the Protocols

Having seen how the browser, server, and CGI application communicate, now look at the use of the protocols POP3 and SMTP. The finger protocol is also used by the application in order to find the user's name to include it in the From field of outgoing messages.

Talking with the POP3 Mail Server

The Post Office Protocol version 3 (RFC1725) was created to permit mail clients (who are not permanently connected to the Internet) to access remote mailboxes in an easy and useful fashion. The application uses some of the most important commands that a POP3 server understands:

The POP3 server listens, by default, to port 110. A client must establish a TCP connection with it in order to start a dialog using these commands. Each command issued to the POP3 server must be followed by a CRLF (Carriage Return and Line Feed) pair (\r\n).

Look at an example of a connection (message request) between WebPOP and the POP3 server (POPGetMessage function):


print remoteHost "USER $input{'login'}\r\n";

&POPReplyOK;

print remoteHost "PASS $input{'password'}\r\n";

&POPReplyOK;

print remoteHost "LIST\r\n";

if (&POPReplyOK) {

    $nbMessages = &POPCountMessages();

    if ($messageN <= $nbMessages) {

        print remoteHost "RETR $messageN\r\n";

        &POPGetMessageHeader;

        &POPGetMessageBody;

    } else {

        &ShowSimpleForm($errorOutMessageTitle, $errorOutMessageBody);

    }

} # else there's something wrong because there are no messages

print remoteHost "QUIT\r\n";

&POPReplyOK;

The function that retrieves and presents the message list is POPListMessageBox, similar to the preceding function, but using the LIST command. Besides talking with the POP3 server, this function has to deal with the formatting of the messages list box. There were several choices in the presentation of the list of mail messages:

For your application, this final choice was taken in order to limit the message list to a pre-defined size. Otherwise, lots of messages in a mailbox would produce an HTML page too big to fit on a computer screen.

See Figure 35.4 for an example of the resulting page.

Figure 35.4 : The contents of a mailbox.

Sending Mail

Mail is sent using the Simple Mail Transfer Protocol-SMTP (RFC821) server, which will forward any mail to the destination specified in the mail header. An SMTP server is somewhat more complicated than a POP3 server because it offers a lot more functionality; but, in your application, you use it indirectly in order to simplify functioning. In fact, mail is sent by invocation of either sendmail or smail, two well-known UNIX mail-handling systems. The function SendMessage in your application is responsible for the mailer invocation:


    $destaddr = $input{'to'};

    $fromaddr = $input{'login'}.'@'.$mailserver;

    $subject = $input{'subject'};

    $body = $input{'body'};

    if ($destaddr eq '' || $fromaddr eq '' || $subject eq '' || $body eq '') {

        &ShowSimpleForm($noFieldsTitle, $noFieldsBody);

    }

    else {

        $userName=&GetUserName($input{'login'});

        open(MAIL,"| $sendmail \"$destaddr\"");

        print MAIL <<EOM;

From: $fromaddr ($userName)

To: $destaddr

Reply-To: $fromaddr

Subject: $subject

X-Mail-Program: WebPOP - Web/Mail interface

$body

EOM

    close(MAIL);

As you might have noticed, the message sent to the SMTP server is composed of To, From, Subject, Reply-To, and custom X-Mail-Program fields and a message body. The mail-handling system understands those fields (in particular the To field) and forwards the message.

Finding the User's Name

The preceding GetUserName function executes the finger program with the From address as the argument and parses the results in order to search the complete user's name. It would also be possible to communicate with the SMTP server through port 25 and issue the VRFY command, but I found the use of finger simpler (although it might not work on systems that refuse finger connections).

Installing the CGI Script

Installing WebPOP is a relatively simple task.

  1. Copy webpop.pl (see Listing 35.1) to your cgi-bin directory.
  2. Copy the GIF files, representing the buttons, to a directory within your document root hierarchy. Place them in a directory called Images/webpop, for example.
  3. Change the paths in every variable $path... in the ##### HTML Text and Buttons ##### section of webpop.pl. You can also customize the messages the user sees by changing all the text variables in the same section. Change the information concerning the URL of the CGI application and paths to system binaries, in the section ##### Paths, binaries and system specific information ##### (adapt $url, $sendmail, $mailserver, $grep, and $finger to reflect your system's settings).
  4. Run your browser and launch the URL you have chosen for WebPOP. In the example, it is

http://www.esoterica.pt/cgi-bin/webpop.pl

The file install.txt on the CD-ROM explains the installation process. There is one part of the listing that contains hard-coded values, generally found in socket.ph or socket.h.


$AF_INET = 2;                          # Internet protocols,

	specified in "socket.h"

$SOCK_STREAM = 1;                      # Semantic (sequenced,

	two-way, reliable)

$PROTOCOL = 0;                         # Protocol used to support

	the semantic chosen (TCP)

If you have problems or just want to improve portability of the application, you should consider including socket.ph in you Perl script (using the require directive).

Performance

WebPOP is an interpreted application that depends on the Perl interpreter of your system (either version 4 or version 5). As with every interpreted application, it is somewhat slower than it could be if it were executed using a binary format (precompiled). If you have a fast and resource-full system or do not plan to have many people using your application, this kind of functioning is just fine. But if you have either a surcharged server or plan to have many people using your application, you should probably consider porting the program to a language that can be compiled, such as C. That would limit the performance hit of the application on your system.

Ideas for Improvements

The CGI application presented in this chapter is a simple one and is intended to be a practical example. Several improvements could be made to it-in fact, most of the possible improvements listed here will probably appear in a future version of WebPOP. Anyway, feel free to change the code to suit your own needs or expectations. Here are some ideas:

The Complete Listing

You can also find Listing 35.1 on the CD-ROM included with this book.


Listing 35.1. WEBPOP.pl-A simple HTML to POP3-mail interface.

#!/usr/bin/perl



###########################################################################

# webpop.pl 1.3sp - A simple HTML to POP3-mail interface                  #

#                                                                         #

# How does it work?                                                       #

# This program acts as a WWW to POP3 interface. It gets user login and    #

# password from a HTML form (form A) and then returns the user mail list, #

# from the POP3 server. This list is another HTML form (form B), so that  #

# the user can pick a message to read, reply to, or delete. If the user   #

# asks to read a message, another request is sent to the POP3 server,     #

# now returning a HTML form (form C) with the message body. When in       #

# form B, the user can request to delete or reply to a message, too.      #

# That's it, no more bells or whistles.                                   #

#                                                                         #

# Todo list:                                                              #

# - Support MIME / Attachments mail                                       #

# - Save message options                                                  #

# - Support for URLs on messages                                          #

#                                                                         #

# Antonio Ferreira                                                        #

# amcf@esoterica.pt                                                       #

#                                                                         #

# January 1996                                                            #

###########################################################################



require '/usr/lib/cgi-lib.pl';                  # The useful cgi-lib



######################### Variables ########################



##### HTML Text and Buttons #####

$formATitle = 'WebPOP - Electronic mail';                       

 # Title of form A (input form)

$formBTitle = 'Mailbox of';                                     

 # Title of form B (list of messages)

$formBHeading = 'Mailbox of';                                   

 # Heading of form B preceding user's login

$formCTitle = 'Message';                                        

 # Title of form C (message)

$formErrorTitle = 'Error';                                      

 # Title of error forms

$error1 = 'Error 1: gethostbyname';                             

 # Body of form reporting error on gethostbyname function

$error2 = 'Error 2: socket';                                    

 # Body of form reporting error on socket function

$error3 = 'Error 3: connect';                                   

 # Body of form reporting error on connect function

$userInfoTitle = 'Unknown user info';                           

 # Title of form reporting missing user info (login, password)

$userInfoBody = 'You must enter your login and password!';      

 # Body of form reporting missing user info (login, password)

$wrongLoginPassword = 'Wrong login or password!';               

 # Body of form reporting bad login or password

$noMailBody = 'Sorry, you have no mail...';                     

 # Body of form reporting no mail

$errorOutMessageTitle = 'Invalid message number';               

 # Title of form reporting invalid message number

$errorOutMessageBody = 'The requested message does not exist!'; 

 # Body of form reporting invalid message number

$noFieldsTitle = 'Uncompleted form';                             

 # Title of form reporting 'missing fields on message'

$noFieldsBody = 'You must fill in the form!';                      

 # Body of form reporting incomplete form

$deletedMessageTitle = 'Deleted message';                       

 # Title of form reporting deleted message

$deletedMessageBody = 'The message was deleted!';               

 # Body of form reporting deleted message

$sentMessageTitle = 'Message sent';                             

 # Title of form reporting sent message

$sentMessageBody = 'Your message was sent!';                    

 # Body of form reporting sent message

$fromLine = 'From';                                             

 # String introducing "From" field on a form

$toLine = 'To';                                                 

 # String introducing "To" field on a form

$subjectLine = 'Subject';                                       

 # String introducing "Subject" field on a form

$dateLine = 'Date';                                             

 # String introducing "Date" field on a form

$pathBackground = '/bg/esot_bg.gif';                            

 # Path to background GIF

$pathLogo = '/Imagens/webpop/webpop.gif';                       

 # Path to webpop logo GIF

$pathButtonHelp ='/Imagens/webpop/help.gif';                    

 # Path to button Help GIF

$pathButtonOpenMailbox ='/Imagens/webpop/open.gif';             

 # Path to button OpenMailbox GIF

$pathButtonReturnMailbox = '/Imagens/webpop/return.gif';        

 # Path to button ReturnToMailBox GIF

$pathButtonSendNewMessage = '/Imagens/webpop/sendn.gif';        

 # Path to SendNewMessage button GIF

$pathButtonSend = '/Imagens/webpop/send.gif';                   

 # Path to Send button GIF

$pathButtonReadMessage = '/Imagens/webpop/read.gif';            

 # Path to ReadMessage button GIF

$pathButtonReplyMessage = '/Imagens/webpop/reply.gif';          

 # Path to ReplyMessage button GIF

$pathButtonDeleteMessage = '/Imagens/webpop/delete.gif';        

 # Path to DeleteMessage button GIF

$lwidth = 197;                                                  # Width of logo

$lheight = 68;                                                  

 # Height of logo

$bwidth = 70;                                                   

 # Width of buttons

$bheight = 37;                                                  

 # Height of buttons



##### Paths, binaries and system specific information #####

$url = 'http://www.esoterica.pt/cgi-bin/webpop.pl';             # webpop URL

$sendmail = '/usr/bin/smail';                                   

 # Path and parameters for the mailer

#$sendmail = '/usr/bin/sendmail -t -n';                         

 # Path and parameters for the mailer

$mailserver = 'mail.esoterica.pt';                              

 # Complete default POP3 mail server hostname

$POP3_Port = 110;                                               

 # Port which the POP3 server is listening to

$grep = '/usr/bin/grep';                                        

 # Path for the grep program

$finger = '/usr/bin/finger';                                    

 # Path for the finger client program



$AF_INET = 2;                                                   

 # Internet protocols, specified in "socket.h"

$SOCK_STREAM = 1;                                               

 # Semantic (sequenced, two-way, reliable)

$PROTOCOL = 0;                                                  

 # Protocol used to support the semantic chosen (TCP)



$template = 'S n a4 x8';                                        

 # Template used to pack lists of values



########################## Start of Main Program ##########################



&ReadParse(*input);             

 # cgi-lib, constructs list of key=value form data

print &PrintHeader();           

 # cgi-lib, prints header "Content-type: text/html\n\n"



if (&MethGet()) {               # GET was used, so...

    &InitialForm();         # ... retrieve the initial form

} else {                        # POST was used, so process POP3 query

    $messageN = $input{'messageN'};

    if ((!$input{'login'} || !$input{'password'}) && (!$input{'help.x'})) {        

 # No info given

        &ShowSimpleForm($userInfoTitle, $userInfoBody);

    } else {

        if ((defined $input{'open.x'}) || (defined $input{'return.x'})) {     

 # User asked to list messages (needs: login, password)

            &ShowMessageList();

        } elsif (defined $input{'read.x'}) {         

 # User asked to read a message (needs: login, password, msgnumber)

            &ShowMessage();

        } elsif (defined $input{'sendn.x'}) {        

 # User asked to send a new message (needs: login, password)

            &SendNewMessage();

        } elsif (defined $input{'reply.x'}) {        

 # User asked to reply to a message (needs: login, password, msgnumber)

            &ReplyMessage();

        } elsif (defined $input{'send.x'}) {         

 # User asked to send to a message (needs: login, password)

            &SendMessage();

        } elsif (defined $input{'delete.x'}) {       

 # User asked to delete a message (needs: login, password, msgnumber)

            &DeleteMessage();

        } elsif (defined $input{'help.x'}) {         # User asked for help

            &HelpUser();

        }

    }

}



exit(0);



########################## End of Main Program ##########################



#################### Start of subroutines definitions ###################



##### Shows the initial form asking for user's login and password #####

sub InitialForm {

    print <<EOM;

<HTML>

<HEAD>

<TITLE>$formATitle</TITLE>

<BODY BACKGROUND="$pathBackground">

</HEAD>

<BODY>

<P ALIGN=center><IMG SRC="$pathLogo" BORDER=0 WIDTH=$lwidth

 HEIGHT=$lheight></P>

<P>

<FORM ACTION="$url" METHOD=POST>

<CENTER>

<PRE>

<B>   Login</B>: <INPUT VALUE="$input{'login'}" NAME=login SIZE=12><BR>

<B>Password</B>: <INPUT VALUE="$input{'password'}" TYPE="password"

 NAME=password SIZE=12>

</PRE>

</CENTER>

<P>

<CENTER>

<INPUT TYPE=image SRC="$pathButtonOpenMailbox" NAME=open BORDER=0

 WIDTH=$bwidth HEIGHT=$bheight>

<INPUT TYPE=image SRC="$pathButtonHelp" NAME=help BORDER=0 WIDTH=$bwidth

 HEIGHT=$bheight>

</CENTER>

</FORM>

</BODY>

</HTML>

EOM

}





#########################################################################

# Subroutines for option "open", that is, ShowMessageList               #

#########################################################################



##### Gets and displays the list of mail messages #####

sub ShowMessageList {

    if (($name, $aliases, $addrtype, $len, $remote_addr) =

 gethostbyname($mailserver)) {

    if (socket(remoteHost, $AF_INET, $SOCK_STREAM, $PROTOCOL)) {

            $remoteStruct = pack ($template, $AF_INET, $POP3_Port,

 $remote_addr);

            if (connect(remoteHost, $remoteStruct)) {

                &POPListMessageBox();

            } else {

                &ShowSimpleForm($formErrorTitle, $error3);

            }

            close(remoteHost);

        } else {

            &ShowSimpleForm($formErrorTitle, $error2);

        }

    } else {

        &ShowSimpleForm($formErrorTitle, $error1);

    }

}



##### Talks with POP server, gets and prints the list of messages #####

sub POPListMessageBox {

    local($nbMessages);

    select(remoteHost); $| = 1;

    select(STDOUT); $| = 1;



    print <<EOM;

<HTML>

<HEAD>

<TITLE>$formBTitle $input{'login'}</TITLE>

<BODY BACKGROUND="$pathBackground">

</HEAD>

<BODY>

<H1 ALIGN=center>$formBHeading <I>$input{'login'}</I></H1>

<FORM ACTION="$url" METHOD=POST>

<P>

EOM

    $nbMessages = 0;

    $_ = <remoteHost>;

    print remoteHost "USER $input{'login'}\r\n";

    if (&POPReplyOK) {

        print remoteHost "PASS $input{'password'}\r\n";

        if (&POPReplyOK) {

            print remoteHost "LIST\r\n";

            if (&POPReplyOK) {                      

 # If 0 messages POP returns -ERR, so it skips this if

                $nbMessages=&POPCountMessages();

                if ($nbMessages > 0) {          

 # If POP replied +OK after LIST, but there are no messages

                    print "<CENTER>You have $nbMessages message";          

 # User can have one or more messages

                    ($nbMessages>1) ? print "s" : print "";

                    print "!<P>";

                    if ($nbMessages>10) {

                        print "<SELECT NAME=\"messageN\" SIZE=10>";

                    } else {

                        print "<SELECT NAME=\"messageN\" SIZE=$nbMessages>";

                    }

                    $messageN = 0;

                    while ($messageN < $nbMessages) {

                        $messageN = $messageN +1;

                        print remoteHost "TOP $messageN 50\r\n";        

 # 50 lines should be enough to get the headers

                        &POPGetMessageHeaders();

                    }

                } else {

                    print "$noMailBody";

                }

            } else {

                print "$noMailBody";

            }

        } else {

            print "$wrongLoginPassword";

            $nbMessages = -1;

        }

    } else {

        print "$wrongLoginPassword";

        $nbMessages = -1;

    }

    print remoteHost "QUIT\r\n";

    &POPReplyOK;

    if ($nbMessages > 0) {

        print "</SELECT>";

    }

    if ($nbMessages > -1 ) {

    print <<EOM;

<P>

<INPUT TYPE=image SRC="$pathButtonSendNewMessage" NAME=sendn BORDER=0

 WIDTH=$bwidth HEIGHT=$bheight>

EOM

    }

    if ($nbMessages > 0) {              # Only if there are messages

        print <<EOM;

<INPUT TYPE=image ALIGN=top SRC="$pathButtonReadMessage" NAME=read BORDER=0

 WIDTH=$bwidth HEIGHT=$bheight>

<INPUT TYPE=image ALIGN=top SRC="$pathButtonReplyMessage" NAME=reply BORDER=0

 WIDTH=$bwidth HEIGHT=$bheight>

<INPUT TYPE=image ALIGN=top SRC="$pathButtonDeleteMessage" NAME=delete BORDER=0

 WIDTH=$bwidth HEIGHT=$bheight><BR>

EOM

    }

    print <<EOM;

<INPUT TYPE="hidden" NAME=login VALUE=$input{'login'}>

<INPUT TYPE="hidden" NAME=password VALUE=$input{'password'}>

</CENTER>

</FORM></BODY></HTML>

EOM

}



##### Gets headers (From, Date, ...) from message sent by POP server #####

sub POPGetMessageHeaders {

    local($from, $date, $subject, $replyto);

    $_ = <remoteHost>;

    while ($_ !~ /^\.\r\n/) {                       

     # Seeks . in one line to end

        if ($_ =~ /^From: (.+)$/) {             # Seeks "From: ..."

            ($from) = /^From: (.+)$/;

            ($from) =~ s/</&lt;/g;          # Replace <'s

            ($from) =~ s/>/&gt;/g;          # Replace >'s

            ($from) =~ s/\r//;              # Replace carriage return's

            ($from) =~ s/\n//;              # Replace newline's

            $_ = $from;

            ($replyto) = /&lt;(.+)&gt;/;

            if ($replyto eq '') {

                $replyto=$from;

            }

        } elsif ($_ =~ /^Subject: (.+)$/) {

            ($subject) = /^Subject: (.+)$/;

            ($subject) =~ s/</&lt;/g;       # Replace <'s

            ($subject) =~ s/>/&gt;/g;       # Replace >'s

            ($subject) =~ s/\r//;           # Replace carriage return's

            ($subject) =~ s/\n//;           # Replace newline's

        } elsif ($_ =~ /^Date: (.+) .+$/) {

            ($date) = /^Date: (.+) .+$/;

        }

        $_ = <remoteHost>;

    }

    # Fixed size list items

    $from = substr($from,0,40);

    $_ = $date;

    ($date) = /.* ([0-9]+ [a-zA-Z]+ [0-9]+) .*$/;

    $date = substr($date,0,11);

    $subject = substr($subject,0,24);

    $selected = ($messageN==1 ? "SELECTED" : "");

    print "<OPTION VALUE=$messageN $selected>$from <> $subject <> $date\n";

}



##### Counts the number of messages available from the POP3 server #####

sub POPCountMessages {

    local($nbMessages);

    $nbMessages = 0;



    while (<remoteHost> !~ /^\.\r\n/) {

        $nbMessages = $nbMessages + 1;

    }

    return $nbMessages;

}





#########################################################################

# Subroutines for option "read", that is, ShowMessage                   #

#########################################################################



##### Gets and displays the a given message #####

sub ShowMessage {

    if (($name, $aliases, $addrtype, $len, $remote_addr) =

 gethostbyname($mailserver)) {

        if (socket(remoteHost, $AF_INET, $SOCK_STREAM, $PROTOCOL)) {

            $remoteStruct = pack ($template, $AF_INET, $POP3_Port,

 $remote_addr);

            if (connect(remoteHost, $remoteStruct)) {

                &POPGetMessage();

                $messageBody = $body;

                $body =~ s/</&lt;/g;

                $body =~ s/>/&gt;/g;

                $body =~ s/"/&quot;/g;

                print "<HTML>";

                print $messageHeader.$body;

                print <<EOM;

</PRE>

<HR>

<FORM ACTION="$url" METHOD=POST>

<CENTER>

<INPUT TYPE=image SRC="$pathButtonReturnMailbox" NAME=return BORDER=0

 WIDTH=$bwidth HEIGHT=$bheight>

<INPUT TYPE=image SRC="$pathButtonReplyMessage" NAME=reply BORDER=0

 WIDTH=$bwidth HEIGHT=$bheight>

<INPUT TYPE=image SRC="$pathButtonDeleteMessage" NAME=delete BORDER=0

 WIDTH=$bwidth HEIGHT=$bheight>

</CENTER>

<INPUT TYPE="hidden" VALUE="$input{'login'}" NAME=login>

<INPUT TYPE="hidden" VALUE="$input{'password'}" NAME=password>

<INPUT TYPE="hidden" VALUE=$messageN NAME=messageN>

</FORM>

</BODY>

</HTML>

EOM

            } else {

                &ShowSimpleForm($formErrorTitle, $error3);

            }

            close(remoteHost);

        } else {

            &ShowSimpleForm($formErrorTitle, $error2);

        }

    } else {

        &ShowSimpleForm($formErrorTitle, $error1);

    }

}



##### Asks POP server to retrieve a message #####

sub POPGetMessage {

    select(remoteHost); $| = 1;

    select(STDOUT); $| = 1;

    $_ = <remoteHost>;

    print remoteHost "USER $input{'login'}\r\n";

    &POPReplyOK;                                            

 # This time, no need to check login or password...

    print remoteHost "PASS $input{'password'}\r\n";

    &POPReplyOK;                                            

 # ... because it was WEBPOP that sent it!

    print remoteHost "LIST\r\n";

    if (&POPReplyOK) {

        $nbMessages = &POPCountMessages();

        if ($messageN <= $nbMessages) {

            print remoteHost "RETR $messageN\r\n";

            &POPGetMessageHeader;

            &POPGetMessageBody;

        } else {

            &ShowSimpleForm($errorOutMessageTitle, $errorOutMessageBody);

        }

    } # else there's something wrong because there are no messages

    print remoteHost "QUIT\r\n";

    &POPReplyOK;

}



##### Gets the Header of the message sent by the POP server #####

sub POPGetMessageHeader {

    local($from, $subject, $date, $replyto);

    do {

        $_ = <remoteHost>;

        if ($_ =~ /^From: (.+)$/) {

            ($from) = /^From: (.+)$/;

            ($from) =~ s/</&lt;/g;

            ($from) =~ s/>/&gt;/g;

            ($from) =~ s/\r//;

            ($from) =~ s/\n//;

        } elsif ($_ =~ /^Subject: (.+)$/) {

            ($subject) = /^Subject: (.+)$/;

            ($subject) =~ s/</&lt;/g;

            ($subject) =~ s/>/&gt;/g;

            ($subject) =~ s/\r//;

            ($subject) =~ s/\n//;

        } elsif ($_ =~ /^Date: (.+)$/) {

            ($date) = /^Date: (.+) .+$/;

            ($date) =~ s/</&lt;/g;

            ($date) =~ s/>/&gt;/g;

            ($date) =~ s/\r//;

            ($date) =~ s/\n//;

        }

    } until ($_ =~ /^\r\n/);

    $_ = $from;

    ($replyto) = /.*&lt;(.+)&gt;.*/;

    if ($replyto eq '') {

        $replyto = $from;

    }

    $messageHeader = "<HEAD><TITLE>$subject</TITLE>

 <BODY BACKGROUND=\"$pathBackground\"></HEAD>

<BODY>

<H2>$fromLine: $from</H2>

<H3>$subjectLine: $subject<BR>

$dateLine: $date</H3><HR><PRE>";

    $oldTo = $from;

    $oldSubject = "Re: ".$subject;

}



##### Gets the Body of the message sent by the POP server #####

sub POPGetMessageBody {

    do {

        $_  = <remoteHost>;

        $body = $body.$_;

    } until ($_ =~ /^\.\r\n/);

}





#########################################################################

# Subroutines for option "sendnew", that is, SendNewMessage             #

#########################################################################



##### Asks for data (header+body) and then sends the message #####

sub SendNewMessage {

    $fromaddr = $input{'login'}.'@'.$mailserver;

    print <<EOM;

<HTML>

<HEAD>

<TITLE>New message</TITLE>

<BODY BACKGROUND="$pathBackground">

</HEAD>

<BODY>

<FORM ACTION="$url" METHOD=POST>

<P>

<CENTER>

<B>De:</B> $fromaddr<P>

<B>Para (email):</B> <INPUT VALUE="$oldTo" NAME=to SIZE=50><P>

<B>Assunto:</B> <INPUT VALUE="$oldSubject" NAME=subject SIZE=50><P>

<INPUT TYPE=image SRC="$pathButtonSend" NAME=send BORDER=0 

 WIDTH=$bwidth HEIGHT=$bheight>

<INPUT TYPE=image SRC="$pathButtonReturnMailbox" NAME=return BORDER=0 

 WIDTH=$bwidth HEIGHT=$bheight>

<P>

<TEXTAREA ROWS=18 COLS=70 NAME=body>$body</TEXTAREA>

</CENTER>

<INPUT TYPE="hidden" VALUE="$input{'login'}" NAME=login>

<INPUT TYPE="hidden" VALUE="$input{'password'}" NAME=password>

</FORM>

</BODY>

</HTML>

EOM

}



##### Uses the mailer defined to send a message #####

sub SendMessage {

    $destaddr = $input{'to'};

    $fromaddr = $input{'login'}.'@'.$mailserver;

    $subject = $input{'subject'};

    $body = $input{'body'};



    if ($destaddr eq '' || $fromaddr eq '' || $subject eq '' || $body eq '') {

        &ShowSimpleForm($noFieldsTitle, $noFieldsBody);

    }

    else {

        $userName=&GetUserName($input{'login'});

        open(MAIL,"| $sendmail \"$destaddr\"");

        print MAIL <<EOM;

From: $fromaddr ($userName)

To: $destaddr

Reply-To: $fromaddr

Subject: $subject

X-Mail-Program: WebPOP - Web/Mail interface



$body

EOM

        close(MAIL);

        print <<EOM;

<HTML>

<HEAD>

<TITLE>$sentMessageTitle</TITLE>

<BODY BACKGROUND="$pathBackground">

</HEAD>

<BODY>

<H1 ALIGN=center>$sentMessageBody</H1>

<P>

<B>$fromLine: </B> $fromaddr<BR>

<B>$toLine: </B> $destaddr<BR>

<B>$subjectLine: </B> $subject

<P>

<FORM ACTION="$url" METHOD=POST>

<CENTER>

<INPUT TYPE=image SRC="$pathButtonReturnMailbox" NAME=return BORDER=0

 WIDTH=$bwidth HEIGHT=$bheight><BR>

</CENTER>

<INPUT TYPE="hidden" VALUE="$input{'login'}" NAME=login>

<INPUT TYPE="hidden" VALUE="$input{'password'}" NAME=password>

</FORM>

</BODY>

</HTML>

EOM

    }

}





#########################################################################

# Subroutines for option "reply", that is, ReplyMessage                 #

#########################################################################



##### Reads from POP server, presents reply-form and sends the message #####

sub ReplyMessage {

    if (($name, $aliases, $addrtype, $len, $remote_addr) =

 gethostbyname($mailserver)) {

        if (socket(remoteHost, $AF_INET, $SOCK_STREAM, $PROTOCOL)) {

            $remoteStruct = pack ($template, $AF_INET, $POP3_Port, $remote_addr);

            if (connect(remoteHost, $remoteStruct)) {

                &POPGetMessage();

                $body = "> ".$body;

                ($body) =~ s/\n/\n> /g;

            } else {

                &ShowSimpleForm($formErrorTitle, $error3);

            }

            close(remoteHost);

        } else {

            &ShowSimpleForm($formErrorTitle, $error2);

        }

    } else {

        &ShowSimpleForm($formErrorTitle, $error1);

    }

    &SendNewMessage();

}





#########################################################################

# Subroutines for option "delete", that is, DeleteMessage               #

#########################################################################



##### Deletes a message #####

sub DeleteMessage {

    if (($name, $aliases, $addrtype, $len, $remote_addr) =

 gethostbyname($mailserver)) {

        if (socket(remoteHost, $AF_INET, $SOCK_STREAM, $PROTOCOL)) {

            $remoteStruct = pack ($template, $AF_INET, $POP3_Port,

 $remote_addr);

            if (connect(remoteHost, $remoteStruct)) {

                &POPDeleteMessage();

            } else {

                &ShowSimpleForm($formErrorTitle, $error3);

            }

            close(remoteHost);

        } else {

            &ShowSimpleForm($formErrorTitle, $error2);

        }

    } else {

        &ShowSimpleForm($formErrorTitle, $error1);

    }

}



##### Deletes a message using the POP server #####

sub POPDeleteMessage {

    select(remoteHost); $| = 1;

    select(STDOUT); $| = 1;

    $_ = <remoteHost>;

    print remoteHost "USER $input{'login'}\r\n";

    &POPReplyOK;

    print remoteHost "PASS $input{'password'}\r\n";

    &POPReplyOK;

    print remoteHost "LIST\r\n";

    if (&POPReplyOK) {

        $nbMessages = &POPCountMessages();

        if ($messageN <= $nbMessages) {

            print remoteHost "DELE $messageN\r\n";

            print <<EOM;

<HTML>

<HEAD>

<TITLE>$deletedMessageTitle</TITLE>

<BODY BACKGROUND="$pathBackground">

</HEAD>

<BODY>

<H2 ALIGN=center>$deletedMessageBody</H2>

<P>

<FORM ACTION="$url" METHOD=POST>

<CENTER>

<INPUT TYPE=image SRC="$pathButtonReturnMailbox" NAME=return BORDER=0

 WIDTH=$bwidth HEIGHT=$bheight><BR>

</CENTER>

<INPUT TYPE="hidden" VALUE="$input{'login'}" NAME=login>

<INPUT TYPE="hidden" VALUE="$input{'password'}" NAME=password>

</FORM>

</BODY>

</HTML>

EOM

        } else {

            &ShowSimpleForm($errorOutMessageTitle, $errorOutMessageBody);

        }

    } # else there's something wrong because there are no messages

    print remoteHost "QUIT\r\n";

    &POPReplyOK;

}





#########################################################################

# General subroutines                                                   #

#########################################################################



##### True if POP reply's '+OK', false otherwise #####

sub POPReplyOK {

    $_ = <remoteHost>;

    if ($_ =~ /^\+OK/) {

        return 1;

    } else {

        return 0;

    }

}



##### Shows a simple form with a title a short message #####

sub ShowSimpleForm {

        print <<EOM;

<HTML>

<HEAD>

<TITLE>$_[0]</TITLE>

<BODY BACKGROUND="$pathBackground">

</HEAD>

<BODY>

<H2>$_[1]</H2>

</BODY>

</HTML>

EOM

}



##### Gets user's name from finger information, using login as the key #####

sub GetUserName {

    local($tmp);

    $login = $_[0];

    $_ = '$finger $login@$mailserver | $grep \"Name:\"';

    ($tmp) = /.*Name: (.+).*$/;

    return $tmp;

}



##### Adds or removes charaters from a string in order to

 have a fixed length #####

sub fixedLength {

    if (length($_[0])<$_[1]) {

        foreach $num (1 .. ($_[1]-length($_[0]))) {

            $_[0] = $_[0]." ";

        }

    } elsif (length($_[0])>$_[1]) {

        $_[0] = substr($_[0],0,$_[1]);

    }

        return $_[0];

}



##### Shows help page #####

sub HelpUser {

    print <<EOM;

<HTML>

<HEAD>

<TITLE>WebPOP - Help</TITLE>

<BODY BACKGROUND="$pathBackground">

</HEAD>

<BODY>

<H1 ALIGN=center>WebPOP<BR><I>Help </I></H1>

<P>

Welcome to WebPOP, the web application that helps you manage your

electronic mail, in a pratical and simple manner! The author of this

application, <A HREF="http://www.esoterica.pt/amcf/">Ant-nio Ferreira</A>,

would like to know <A HREF="mailto:amcf@esoterica.pt">your opinion and

suggestions</A> on WebPOP.

<P>

About WebPOP:

<UL>

<LI><B>What is WebPOP?</B>

<BR>

It is an application that helps you manage your electronic mailbox

from a World-Wide Web page, which is an interface certainly familiar

to you. Besides reading your mail, you can also send messages to other

people on the Internet and/or delete old messages from your mailbox.

<P>

<LI><B>How does it work?</B>

<BR>

The initial screen asks for your identification (login and password).

Only that way you can have access to your mailbox!

<BR>

After entering the identification, you are given the list of messages

from your electronic mailbox. 3 of the 4 buttons at the bottom of the

list permit you to <B>Read</B>, <B>Reply</B>, or <B>Delete</B> the selected

message. If you wish to send a new message, you must click on the first

button, titled <B>Send new</B>.

<BR>

Finally, when you are reading a message, 2 additional buttons permit you

to <B>Reply</B> to that message (send a message with the same subject

to the person who wrote to you before) or <B>Delete</B> the message

(permanently erase it from your mailbox). If you wish to save a copy

of a particular message, you must use the option <B>Save as...</B> (or

an equivalent one) from your browser's File menu. The browser is the

program you are using to access the World-Wide Web.

<P>

<LI><B>Can I save my messages on my personal computer and organize my mail?</B>

<BR>

Yes. You can create, in your personal computer's hard disk, some

directories (folders), each one dedicated to a person or subject. You

should have your own mail organization strategy. Those folders can all be put

inside a main folder called <B>MAIL</B> or <B>WEBPOP</B>, for example.

Then, when you are reading a message from your browser's window, you can

choose the option <B>Save as...</B> and save the message in your hard disk,

inside the desired folder and with a suggestive name. After saving the

message, you can delete it from your mailbox... it is very important to do

so every once in a while (or every time you save a message) otherwise your

mailbox will grow forever, and accessing it will become progressively slower.

<P>

<LI><B>Is there any way to attach a file to a message?</B>

<BR>

Not really. The only way is to use the options <B>Cut</B> and <B>Paste</B>

from your operating system's Edit menu. This only works if your file is

not too big and is composed only of simple (ASCII) text.

<P>

<LI><B>If I send a message with WWW links (URL's) in the text, will it be

possible to click on them so that the browser executes the action indicated?</B>

<BR>

Not yet. This is a feature that will probably appear with the next version

of WebPOP. It will offer you the possibility to click on a URL inside a given

message, just as you would on a link inside an ordinary HTML page. Support for

MIME types (so that messages can be composed with different file formats) is

also a future feature.

</UL>

<P>

<A HREF="$url">Return to the initial form</A>

</BODY>

</HTML>

EOM

}


Summary

In this chapter you created an application that enables users to read their mail through Web pages. This application has served as an example of the principles and characteristics of multi-protocol applications. Not only have you created what can be a very useful application, but you have also gone through all the steps in the creation process of such applications and have acquired the know-how needed to proceed with the development of other applications.