In the third
edition, PyMailCGI was upgraded to use the newmailtools
module package of
Chapter 13
, employ the PyCrypto package for
passwords if it is installed, support viewing and sending message
attachments, and run more efficiently. All these are inherited by
version 3.0 as well.
We’ll meet these new features along the way, but the last two of
these merit a few words up front. Attachments are supported in a
simplistic but usable fashion and use existingmailtools
package code for much of their
operation:
For
viewing
attachments, message
parts are split off the message and saved in local
files on the server. Message view pages are then augmented with
hyperlinks pointing to the temporary files; when clicked, they open
in whatever way your web browser opens the selected part’s file
type.
For
sending
attachments, we use the HTML
upload techniques presented near the end of
Chapter 15
. Mail edit pages now have
file-upload controls, to allow a maximum of three attachments.
Selected files are uploaded to the server by the browser with the
rest of the page as usual, saved in temporary files on the server,
and added to the outgoing mail from the local files on the server bymailtools
. As described in the
note in the preceding section, sent attachments can only be
compatibly encoded text in version 3.0, not binary, though this
includes encodable HTML files.
Both schemes would fail for multiple simultaneous users, but since
PyMailCGI’s
configuration
file
scheme (described later in this chapter) already limits it to a single
username, this is a reasonable constraint. The links to temporary files
generated for attachment viewing also apply only to the last message
selected, but this works if the page flow is followed normally.
Improving this for a multiuser scenario, as well as adding additional
features such as PyMailGUI’s local file save and open options, are left
as exercises.
For efficiency, this version of PyMailCGI also avoids repeated
exhaustive mail downloads. In the prior version, the full text of all
messages in an inbox was downloaded every time you visited the list page
and every time you selected a single message to view. In this version,
the list page downloads only the header text portion of each message,
and only a single message’s full text is downloaded when one is selected
for viewing. In addition, the headers fetch limits added tomailtools
in the fourth edition of this book
are applied automatically to limit download time (earlier mails outside
the set’s size are ignored).
Even so, the list page’s headers-only download can be slow if you
have many messages in your inbox (and as I confessed in
Chapter 14
, I have thousands in one of mine). A
better solution would somehow cache mails to limit reloads, at least for
the duration of a browser session. For example, we might load headers of
only newly arrived messages, and cache headers of mails already fetched,
as done in the PyMailGUI client of
Chapter 14
.
Due to the lack of state retention in CGI scripts, though, this
would likely require some sort of server-side database. We might, for
instance, store already fetched message headers under a generated key
that identifies the session (e.g., with process number and time) and
pass that key between pages as a cookie, hidden form field, or URL query
parameter. Each page would use the key to fetch cached mail stored
directly on the web server, instead of loading it from the email server
again. Presumably, loading from a local cache file would be faster than
loading from a network connection to the mail server.
This would make for an interesting exercise, too, if you wish to
extend this system on your own, but it would also result in more pages
than this chapter has to spend (frankly, I ran out of time for this
project and real estate in this chapter long before I ran out of
potential
enhancements).
Much of
the “action” in PyMailCGI is encapsulated in shared
utility modules, especially one calledcommonhtml.py
. As you’ll see in a moment, the
CGI scripts that implement user interaction don’t do much by themselves
because of this. This architecture was chosen deliberately, to make
scripts simple, avoid code redundancy, and implement a common
look-and-feel in shared code. But it means you must jump between files
to understand how the whole system works.
To make this example easier to digest, we’re going to explore its
code in two chunks: page scripts first, and then the utility modules.
First, we’ll study screenshots of the major web pages served up by the
system and the HTML files and top-level Python CGI scripts used to
generate them. We begin by following a send mail interaction, and then
trace how existing email is read and then processed. Most implementation
details will be presented in these sections, but be sure to flip ahead
to the utility modules listed later to understand what the scripts are
really doing.
I should also point out that this is a fairly complex system, and
I won’t describe it in exhaustive detail; as for PyMailGUI and
Chapter 14
, be sure to read the source code along
the way for details not made explicit in the narrative. All of the
system’s source code appears in this chapter, as well as in the book’s
examples distribution package, and we will study its key concepts here.
But as usual with case studies in this book, I assume that you can read
Python code by now and that you will consult the example’s source code
for more details. Because Python’s syntax is so close to “executable
pseudo
code,” systems are sometimes
better described in Python than in English once you have the overall
design in mind.
The HTML pages
and CGI scripts of PyMailCGI can be installed on any web
server to which you have access. To keep things simple for this book,
though, we’re going to use the same policy as in
Chapter 15
—we’ll be running the Python-coded
webserver.py
script from
Example 16-1
locally, on the same
machine as the web browser client. As we learned at the start of the
prior chapter, that means we’ll be using the server domain name
“localhost” (or the equivalent IP address, “127.0.0.1”) to access this
system’s pages in our browser, as well as in theurllib.request
module.
Start this server script on your own machine to test-drive the
program. Ultimately, this system must generally contact a mail server
over the Internet to fetch or send messages, but the web page server
will be running locally on your computer.
One minor twist here: PyMailCGI’s code is located in a directory
of its own, one level down from the
webserver.py
script. Because of that, we’ll start the web server here with an
explicit directory and port number in the command line used to launch
it:
C:\...\PP4E\Internet\Web>webserver.py PyMailCgi 8000
Type this sort of command into a command prompt window on Windows
or into your system shell prompt on Unix-like platforms. When run this
way, the server will listen for URL requests on machine “localhost” and
socket port number 8000. It will serve up pages from the
PyMailCgi
subdirectory one level below the script’s
location, and it will run CGI scripts located in the
PyMailCgi\cgi-bin
directory below that. This works
because the script changes its current working directory to the one you
name when it starts up.
Subtle point: because we specify a unique port number on the
command line this way, it’s OK if you simultaneously run another
instance of the script to serve up the prior chapter’s examples one
directory up; that server instance will accept connections on port 80,
and our new instance will handle requests on port 8000. In fact, you can
contact either server from the same browser by specifying the desired
server’s port number. If you have two instances of the server running in
the two different chapters’ directories, to access pages and scripts of
the prior chapter, use a URL of this form:
http://localhost/languages.html
http://localhost/cgi-bin/languages.py?language=All
And to run this chapter’s pages and scripts, simply use URLs of
this form:
http://localhost:8000/pymailcgi.html
http://localhost:8000/cgi-bin/onRootSendLink.py
You’ll see that the HTTP and CGI log messages appear in the window
of the server you’re contacting. For more background on why this works
as it does, see the
introduction
to network socket addresses in
Chapter 12
and
the discussion of URLs in
Chapter 15
.
If you do install this example’s code on a different server,
simply replace the “localhost:8000/cgi-bin” part of the URLs we’ll use
here with your server’s name, port, and path details. In practice, a
system such as PyMailCGI would be much more useful if it were installed
on a remote server, to allow mail processing from any web
client.
[
66
]
As with PyMailGUI, you’ll have to edit themailconfig.py
module’s settings to use this
system to read your own email. As provided, the email server information
is not useful for reading email of your own; more on this in a
moment
.
Carry-On Software
PyMailCGI works as planned and illustrates more CGI and email
concepts, but I want to point out a few caveats up front. This
application was initially written during a two-hour layover in
Chicago’s O’Hare airport (though debugging took a few hours more). I
wrote it to meet a specific need—to be able to read and send email
from any web browser while traveling around the world teaching Python
classes. I didn’t design it to be aesthetically pleasing to others and
didn’t spend much time focusing on its efficiency.
I also kept this example intentionally simple for this book. For
example, PyMailCGI doesn’t provide nearly as many features as the
PyMailGUI program in
Chapter 14
, and it
reloads email more than it probably should. Because of this, its
performance can be very poor if you keep your inbox large.
In fact, this system almost cries out for more advanced state
retention options. As is, user and message details are passed in
generated pages as hidden fields and query parameters, but we could
avoid reloading mail by also using server-side deployment of the
database techniques described in
Chapter 17
. Such extensions might
eventually bring PyMailCGI up to the functionality of PyMailGUI,
albeit at some cost in code complexity. Even so, this system also
suffers from the Python 3.1 attachments limitation described earlier,
which would need to be addressed as well.
Again, you should consider this system a
prototype
and a work in progress; it’s not yet
software worth selling, and not something that you’ll generally want
to use as is for mail that’s critical to you. On the other hand, it
does what it was intended to do, and you can customize it by tweaking
its Python source code—something that can’t be said of all software
sold.
[
66
]
One downside to running a local
webserver.py
script that I noticed during
development for this chapter is that on platforms where CGI scripts
are run in the same process as the server, you’ll need to stop and
restart the server every time you change an imported module.
Otherwise, a subsequent import in a CGI script will have no effect:
the module has already been imported in the process. This is not an
issue on Windows today or on other platforms that run the CGI as a
separate, new process. The server’s classes’ implementation varies
over time, but if changes to your CGI scripts have no effect, your
platform my fall into this category: try stopping and restarting the
locally running web server.
Let’s start off
by implementing a main page for this example. The file shown
in
Example 16-2
is primarily used
to publish links to the Send and View functions’ pages. It is coded as a
static HTML file, because there is nothing to generate on the fly
here.
Example 16-2. PP4E\Internet\Web\PyMailCgi\pymailcgi.html
Read Programming Python (163 page) Page 163 Read Book Online,Top Vampire Books Read Online Free PyMailCGI
A POP/SMTP Web Email Interface
Version 3.0 June 2010 (2.0 January 2006)
Actions |
Overview
|
Notes Caveats: PyMailCgi 1.0 was initially written during a 2-hour layover at Also note that if you use these scripts to read your own email, PyMailCgi New in Version 2: PyMailCGI now supports viewing and sending New in Version 3: PyMailCGI now runs on Python 3.X (only), Also see:
|
The file
pymailcgi.html
is the system’s root
page and lives in a
PyMailCgi
subdirectory which is
dedicated to this application and helps keep its files separate from other
examples. To access this system, start your locally running web server as
described in the preceding section and then point your browser to the
following URL (or do the right thing for whatever other web server you may
be using):
http://localhost:8000/pymailcgi.html
If you do, the server will ship back a page such as that captured in
Figure 16-2
, shown rendered in the Google
Chrome web browser client on Windows 7. I’m using Chrome instead of
Internet Explorer throughout this chapter for variety, and because it
tends to yield a concise page which shows more details legibly. Open this
in your own browser to see it live—this system is as portable as the Web,
HTML, and Python-coded CGI scripts.
Figure 16-2. PyMailCGI main page
Now, before you
click on the “View…” link in
Figure 16-2
expecting to read your own email, I
should point out that by default, PyMailCGI allows anybody to send email
from this page with the Send link (as we learned earlier, there are no
passwords in SMTP). It does not, however, allow arbitrary users on the
Web to read their email accounts without either typing an explicit and
unsafe URL or doing a bit of installation and configuration.
This is on purpose, and it has to do with
security constraints; as we’ll see later,
PyMailCGI
is written such that it never
associates your email username and password together without encryption.
This isn’t an issue if your web server is running locally, of course,
but this policy is in place in case you ever run this system remotely
across the Web.
By default, then, this page is set up to read the email account
shown in this book—address
[email protected]
—and
requires that account’s POP password to do so. Since you probably can’t
guess the password (and wouldn’t find its email all that interesting if
you could!), PyMailCGI is not incredibly useful as shipped. To use it to
read your email instead, you’ll want to change its
mailconfig.py
mail configuration file to
reflect your mail account’s details. We’ll see this file later; for now,
the examples here will use the book’s POP email account; it works the
same way, regardless of which account it
accesses.