Webmail Client

Although I’m a supporter and user of the Squirrelmail webmail package, I was given an assignment for csci4131 – Internet Programming during the spring of 2006 to implement a pop3-ssl/smtp-ssl webmail client in Python/CGI.

#!/soft/python-2.4-bin/python
 
import cgitb
 
cgitb.enable()
 
import smtplib, getpass, sys, poplib, time, os, cgi, email, email.Message
 
if os.environ.get('SSL_PROTOCOL', '') == '':
  print 'Location: https://' + os.environ['SERVER_NAME'] + os.environ['REQUEST_URI'] + "\\n\\n"
 
 
######## support functions
 
def printHeaders(title, scriptname):
  print "Content-Type: text/html"
  print """
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <title>%s</title>
  <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1" />
  <style type="text/css">
    body {
      font-family: Arial, Helvetica, Sans-Serif;
      font-size: 13px;
    }
 
    h1 {
      text-align: center;
    }
 
    h2 {
      color: #990000;
    }
 
    div#headers {
      background-color: grey;
    }
 
    div#headers span {
      display: block;
      float: left;
      width: 15%%;
      text-align: right;
      font-weight: bold;
    }
 
    div#headers span + span {
      display: block;
      float: left;
      width: 85%%;
      text-align: left;
      font-weight: normal;
    }
 
  </style>
  <script type=\\"text/javascript\\">
  <!--""" % (title)
  if scriptname == "loginScript":
    print loginScript
  elif scriptname == "updateForm":
    print "var table = ["
    for record in table:
      print "['%s %s. %s', '%s', '%s', '%s, %s', '%s']," % (record[1], record[2], record[3], record[4], record[5], record[6], record[7], record[8])
    print "]"
    print updateForm
  print """  -->
  </script>
</head>
<body>
<h1>Csci4131 Lab11 - David R. Hedges, #2836226</h1>"""
 
def printFooters():
  print """
</body>
</html>"""
 
def connectPop(u_server, u_name, u_pass):
  try:
    pop = poplib.POP3_SSL(u_server)
    pop.user(u_name)
    pop.pass_(u_pass)
    return pop
  except:
    print "<h2>Unable to connect to server.</h2>"
    printPg1("none")
    return 0
 
 
## page 1
## this function prints the content of the first page (login). the one variable
## 'error' is used to set the display: property of the error message for the
## login page. acceptable values are "inline" (if the error message should be
## displayed), or "none" (if the error message should not be diaplayed).
################
 
def printPg1(error):
  print"""<form action="webmail.cgi" method="post" onsubmit="if (!validateInput()) {return false;}">
<h2>Login <span style="display: %s;">(Error: Invalid username/password)</span></h2>
 
<div style="width: 250px; text-align: right; line-height: 24px;">
Server: <input type="text" name="server" size="20" value="jinxx060.email.umn.edu" /><br />
Username: <input type="text" name="username" size="20" value="jinxx060" /><br />
Password: <input type="password" name="password" size="20" /><br />
<input type="hidden" name="postPageNumber" value="1" />
 
<input type="submit" name="submit" value="Login" /> <input type="reset" name="Reset" value="Clear" />
</div>
</form>""" % (error)
 
 
## print inbox
## the generic function to connect to a pop server(ssl) and print the list of
## messages contained in the inbox
#############
 
def printInbox(u_server, u_name, u_pass):
  try:
    pop = poplib.POP3_SSL(u_server)
    pop.user(u_name)
    pop.pass_(u_pass)
  except:
    print "<h2>Unable to connect to server.</h2>"
    printPg1("none")
  else:
    numMessages = len(pop.list()[1])
    msgList = []
    msg = email.Message
    j = 0
    print """<form action="webmail.cgi" method="post">
<input type="submit" name="Compose" value="Compose" /> <input type="submit" name="Delete" value="Delete" /> <input type="submit" name="Logout" value="Logout" />
<table style="width: 100%;">
  <thead>
    <tr style="background-color: #114477; color: white;">
      <th>Check</th>
      <th>From</th>
      <th>Subject</th>
      <th>Date</th>
      <th>Size</th>
    </tr>
  </thead>
  <tbody>"""
    for i in range(numMessages):
      msgList.append(pop.top(i+1,0)[1])
      msgid = pop.uidl(i+1)
      ptr = msgid.index(str(i+1))
      msgid = msgid[ptr+2:]
 
      #####3
      # hack to skip using unique ids for the moment
      ####
      msgid = i+1
 
      msgString = ""
      for k in msgList[j]:
        msgString += k + '\\n'
      msg = email.message_from_string(msgString)
      msgList[j] = msg
      print """    <tr>
      <td><input type="checkbox" name="delete" value="%s" /></td>
      <td>%s</td>
      <td><input type="submit" name="%s" value="%s" style="background-color: transparent; border: 0; border-bottom: 1px dotted black; color: blue;" /></td>
      <td>%s</td>
      <td>%s B</td>
    </tr>""" % (msgid, cgi.escape(msg.get("From"), True), msgid, msg.get("Subject"), msg.get("Date"), pop.top(i+1,0)[2])
      j += 1
    print """  </tbody>
</table>
<input type="hidden" name="postPageNumber" value="2" />
<input type="hidden" name="server" value="%s" />
<input type="hidden" name="username" value="%s" />
<input type="hidden" name="password" value="%s" />
</form>""" % (u_server, u_name, u_pass)
    pop.quit()
 
 
def readMsg(u_server, u_name, u_pass, msgid):
  try:
    pop = poplib.POP3_SSL(u_server)
    pop.user(u_name)
    pop.pass_(u_pass)
  except:
    print "<h2>Unable to connect to server.</h2>"
    printPg1("none")
  else:
    msg = pop.retr(msgid)
    msgtext = ""
    for i in msg[1]:
      msgtext += i + "\\n"
    pop.quit()
 
    #now the message is stored in msgtext in a format that from_string() will like
    emsg = email.message_from_string(msgtext)
 
    print """<div style="width: 100%%;">
  <form action="webmail.cgi" method="post">
  <div style="width: 100%%; background-color: #114477; color: white;"><input type="submit" name="Inbox" value="Inbox" style="background-color: transparent; color: white; border: 0; border-bottom: 1px dotted white;" /> <input type="submit" name="Reply" value="Reply" style="background-color: transparent; color: white; border: 0; border-bottom: 1px dotted white;" /></div>
  <div id="headers"><span>Subject:&nbsp;</span> <span>%s</span><input type="hidden" name="subject" value="%s" /><br />
    <span>From:&nbsp;</span> <span>%s</span><input type="hidden" name="from" value="%s" /><br />
    <span>To:&nbsp;</span> <span>%s</span><br />
    <span>Date:&nbsp;</span> <span>%s</span><br />
    <span>Size:&nbsp;</span> <span>%s Bytes</span><br />
  </div>""" % (cgi.escape(emsg.get("Subject", "No Subject"), True), cgi.escape(emsg.get("Subject", "No Subject"), True), cgi.escape(emsg.get("From", ""), True), cgi.escape(emsg.get("From", ""), True), cgi.escape(emsg.get("To", ""), True), emsg.get("Date"), msg[2])
    if emsg.is_multipart():
      multimsg = email.message_from_string(str(emsg.get_payload(0)))
      if multimsg.is_multipart():
        multimsg = email.message_from_string(str(multimsg.get_payload(0, True)))
      if multimsg.get("Content-Transfer-Encoding") == "base64":
        print """  <pre>%s</pre><textarea name="body" rows="1" cols="76" style="display: none;">%s</textarea>""" % (multimsg.get_payload().decode("base64"), multimsg.get_payload())
      else:
        print """  <pre>%s</pre><textarea name="body" rows="1" cols="76" style="display: none;">%s</textarea>""" % (multimsg.get_payload(), multimsg.get_payload())
    else:
      print "  <pre>%s</pre>" % emsg.get_payload()
    print """  <input type="hidden" name="postPageNumber" value="3" />
  <input type="hidden" name="server" value="%s" />
  <input type="hidden" name="username" value="%s" />
  <input type="hidden" name="password" value="%s" />
  </form>\\n</div>""" % (u_server, u_name, u_pass)
 
def composeMsg(u_server, u_name, u_pass, msg_addr, msg_subject, msg_body):
  if u_name.find("@") == -1 and u_server.find("umn.edu") > -1:
    u_name += "@umn.edu"
  if msg_subject == None:
    msg_subject = ""
  print """<div style="width: 100%%;">
  <form action="webmail.cgi" method="post">
    <div style="width: 100%%; background-color: #114477; color: white;"><input type="submit" name="Inbox" value="Inbox" style="background-color: transparent; color: white; border: 0; border-bottom: 1px dotted white;" /> <input type="submit" name="Send" value="Send" style="background-color: transparent; color: white; border: 0; border-bottom: 1px dotted white;" /></div>
    <div id="headers"><span>To:&nbsp;</span> <span><input type="text" name="to" value="%s" size="40" style="border: 1px solid black;" /></span><br />
      <span>From:&nbsp;</span> <span><input type="text" name="from" value="%s" size="40" style="border: 1px solid black;" /></span><br />
      <span>Subject:&nbsp;</span> <span><input type="text" name="subject" value="%s" size="40" style="border: 1px solid black;" /></span><br />
      <div style="clear: both; height: 1px">&nbsp;</div>
    </div>""" % (cgi.escape(msg_addr, True), u_name, cgi.escape(msg_subject, True))
  print """    <div style="width: 100%%; margin-top: 20px;"><textarea name="body" rows="10" cols="76">%s</textarea><br /><input type="submit" name="Send" value="Send" /></div>""" % msg_body
  print """  <input type="hidden" name="postPageNumber" value="4" />
  <input type="hidden" name="server" value="%s" />
  <input type="hidden" name="username" value="%s" />
  <input type="hidden" name="password" value="%s" />
  </form>\\n</div>""" % (u_server, u_name, u_pass)
 
 
def sendMessage(u_server, u_name, u_pass, msg_to, msg_from, msg_subject, msg_body):
  from email.MIMEText import MIMEText
  from email.MIMEBase import MIMEBase
  from email.MIMEMultipart import MIMEMultipart
  from email import Utils, Encoders
 
  #hack to set smtp server to smtp.umn.edu if the user is using a umn email addy
  if u_server.find("email.umn.edu") > -1:
    smtp_server = "smtp.umn.edu"
  else:
    smtp_server = u_server
 
  msg = MIMEMultipart()
  msg['To'] = msg_to
  msg['From'] = msg_from
  msg['Subject'] = msg_subject
  msg['Date'] = Utils.formatdate(localtime = 1)
  msg['Message-ID'] = Utils.make_msgid()
 
  text = MIMEText(msg_body, _subtype='plain')
  #binary = MIMEBase('text', 'plain')
  #binary.set_payload(msg_body)
  #Encoders.encode_base64(binary)
 
  msg.attach(text)
  #msg.attach(binary)
 
  try:
    smtp = smtplib.SMTP(smtp_server)
    smtp.starttls()
    smtp.ehlo()
    smtp.login(u_name, u_pass)
    smtp.sendmail(msg_from, msg_to, msg.as_string())
  except:
    print """<h2>Sending message failed.</h2><div>Please <a href="javascript:history.back()" title="back">go back</a> and try again.</div>"""
  else:
    print """<form action="webmail.cgi" method="post">
  <h2>Message sent</h2>
  <div>Click <input type="submit" name="here" value="here" style="background-color: transparent; border: 0; border-bottom: 1px dotted black; color: blue;" /> to go back to the inbox.</div>
  <input type="hidden" name="postPageNumber" value="1" />
  <input type="hidden" name="server" value="%s" />
  <input type="hidden" name="username" value="%s" />
  <input type="hidden" name="password" value="%s" />
</form>""" % (u_server, u_name, u_pass)
 
 
######## end support functions
#####################
 
##################
## scripts
 
global loginScript
loginScript = "  function validateInput() {\\n  if (document.forms[0].username.value == '') {\\n    alert(\\"Please enter a username.\\");\\n    return false;\\n  }\\n  if (document.forms[0].password.value == '') {\\n    alert(\\"Please enter a password.\\");\\n    return false;\\n  }\\n  return true;\\n}"
 
 
 
#####################
## end scripts
 
 
 
form = cgi.FieldStorage()
postPageNumber = form.getvalue("postPageNumber")
 
if postPageNumber == None:
  postPageNumber = "0"
 
if postPageNumber == "0":
  printHeaders("Login", "loginScript")
  printPg1("none")
  printFooters()
 
elif postPageNumber == "1":
  u_server = form.getvalue("server")
  u_name = form.getvalue("username")
  u_pass = form.getvalue("password")
 
  printHeaders("Webmail - Inbox", "")
  printInbox(u_server, u_name, u_pass)
  printFooters()
 
elif postPageNumber == "2":
  u_server = form.getvalue("server")
  u_name = form.getvalue("username")
  u_pass = form.getvalue("password")
 
  #check to see which button user clicked
  if form.getvalue("Compose") == "Compose":
    action = "compose"
  elif form.getvalue("Delete") == "Delete":
    action = "delete"
  elif form.getvalue("Logout") == "Logout":
    action = "logout"
  else:
    action = "read"
 
  ###########
  ## READ
  ###########
  if action == "read":
    for val in form:
      #msg id will be the name of the first populated form value
      msgid = val
      break
 
    printHeaders("Webmail - Read Message", "")
    readMsg(u_server, u_name, u_pass, msgid)
    printFooters()
 
 
  ###########
  ## DELETE
  ###########
  elif action == "delete":
    printHeaders("Webmail - Delete", "")
    pop = connectPop(u_server, u_name, u_pass)
    if pop != 0:
      for val in form.getlist("delete"):
        pop.dele(val)
      pop.quit()
      print """<form action="webmail.cgi" method="post">
  <h2>Messages successfully deleted.</h2>
  <div>Click <input type="submit" name="here" value="here" style="background-color: transparent; border: 0; border-bottom: 1px dotted black; color: blue;" /> to go back to the inbox.</div>
  <input type="hidden" name="postPageNumber" value="1" />
  <input type="hidden" name="server" value="%s" />
  <input type="hidden" name="username" value="%s" />
  <input type="hidden" name="password" value="%s" />
</form>""" % (u_server, u_name, u_pass)
    printFooters()
 
 
  ###########
  ## COMPOSE
  ###########
  elif action == "compose":
    printHeaders("Webmail - Compose", "")
    composeMsg(u_server, u_name, u_pass, "", "", "")
    printFooters()
 
  ############
  ## LOGOUT
  ############
  elif action == "logout":
    printHeaders("Logout Successful", "")
    print """<h2>Logout Successful</h2><div>You have been logged out of the webmail system. Click <a href="webmail.cgi">here</a> to log in again.</div>"""
    printFooters()
 
 
elif postPageNumber == "3":
  u_server = form.getvalue("server")
  u_name = form.getvalue("username")
  u_pass = form.getvalue("password")
 
  #check to see which button user clicked
  if form.getvalue("Inbox") == "Inbox":
    action = "inbox"
  elif form.getvalue("Reply") == "Reply":
    action = "reply"
 
 
  if action == "inbox":
    printHeaders("Webmail - Inbox", "")
    printInbox(u_server, u_name, u_pass)
    printFooters()
 
 
  ###########
  ## REPLY
  ###########
  elif action == "reply":
    msg_from = form.getvalue("from")
    msg_subject = form.getvalue("subject")
    msg_body = form.getvalue("body")
    printHeaders("Webmail - Reply", "")
    composeMsg(u_server, u_name, u_pass, msg_from, msg_subject, msg_body)
    printFooters()
 
elif postPageNumber == "4":
  u_server = form.getvalue("server")
  u_name = form.getvalue("username")
  u_pass = form.getvalue("password")
 
  #check to see which button user clicked
  if form.getvalue("Inbox") == "Inbox":
    action = "inbox"
  else:
    action = "send"
 
 
  if action == "inbox":
    printHeaders("Webmail - Inbox", "")
    printInbox(u_server, u_name, u_pass)
    printFooters()
 
  elif action == "send":
    msg_to = form.getvalue("to")
    msg_from = form.getvalue("from")
    msg_subject = form.getvalue("subject")
    msg_body = form.getvalue("body")
 
    printHeaders("Webmail - Sending Mail", "")
    sendMessage(u_server, u_name, u_pass, msg_to, msg_from, msg_subject, msg_body)
    printFooters()
 

Download this code: webmail.cgi