James.Cridland.net

Blackberry e-mail

I have a lovely little Blackberry, which I've been given by work. They have set it up so that the browser on it is available for me to use: and I use things like my very lovely 'PocketWeb' service to read newspapers and things.

This is all very well, but I can't read my home e-mail on it: only work stuff. Which is no good. No, I decided, now was the time to write a little mail client which would run fine on the Blackberry browser - indeed, a mail client designed for the ikkle screen. I decided to write a mail client which would allow me to read e-mail, reply to e-mail and send new bits of e-mail.

So I have.

Things you need
An IMAP mail server.
PHP compiled with the IMAP libraries.
This script is optimised for the little colour Blackberry, with fonts set as "Millbank Tall" and size set as "7". Just use the 'additional configuration' panel when logging on to change the amount of text you see to fit your screen.

This won't work like the main Blackberry mail system: because it's not. Don't expect to use it in places with no GPRS coverage.

It does work, too, as a web-based e-mail system. Except it looks rubbish.

<?php

////////////////////////////////////////////////////
// Ikkle tiny e-mail system intended for the browser within Blackberry machines
//
// v0.3, May 2 04
// - Added lots more anti-cache stuff
// - Now correctly decodes MIME subject headers
// - Now automatically shows TEXT/PLAIN portions of Mime emails, with
//   selectable HTML part instead
// - Extra configuration for length and width of screen
// - Better handling of incorrect IMAP server password
// - Increased speed with GZIP
// - Added hotlinks to plaintext body
//
// v0.2a, Mar 14 04
// - changed <strike> for deleted mail to something that works on Blackberries
//
// v0.2, Mar 14 04
// - fixed bug where mailbox has less than 14 pieces of mail
// - fixed bug where mail showed up even when deleted
// - added 'type=password' to the password field
// - added 'You have no mail' when you, er, have no mail
//
// http://james.cridland.net/code
//
// You're free to use this, but please keep this URL intact
////////////////////////////////////////////////////

////////////////////////////////////////////////////
// Tiddly configs:
// This site - for setting the cookie.
// Example: "www.yahoo.com";

$site = "www.yahoo.com";

// Where will this script run?
// You need to include this here.
// Example "/mail/"

$where = "/mail/";

// Advanced configuration. You don't need to fiddle unless you want to.
$heightofpage = "8";
$widthofpage = "45";
// These are the defaults for the height and width of the page. Users
// can still change them should they wish.

// That's all the config you need
////////////////////////////////////////////////////

//Compress this page
ob_start( 'ob_gzhandler' );
//Set no caching
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
//Add correct encoding charset to enable validation
//header ("Content-Type: text/html; charset=ISO-8859-1");

if ($action==setcookie) {
    
//Set the cookie
    
setcookie("mailusername",$setmailusername,time()+60*60*24*365,$where,$site);
    
setcookie("mailpassword",$setmailpassword,time()+60*60*24*365,$where,$site);
    
setcookie("mailemail",$setmailemail,time()+60*60*24*365,$where,$site);
    
setcookie("mailname",$setmailname,time()+60*60*24*365,$where,$site);
    
setcookie("mailserver",$setmailserver,time()+60*60*24*365,$where,$site);
    
setcookie("widthofpage",$setwidthofpage,time()+60*60*24*365,$where,$site);
    
setcookie("heightofpage",$setheightofpage,time()+60*60*24*365,$where,$site);
    
//...and also set the variables as if they've been set by the cookie
    
$mailusername=$setmailusername;
    
$mailpassword=$setmailpassword;
    
$mailemail=$setmailemail;
    
$mailname=$setmailname;
    
$mailserver=$setmailserver;
    
$widthofpage=$setwidthofpage;
    
$heightofpage=$setheightofpage;
    
$action="list";
}

if (
$action==logout) {
    
//Log out only deletes the mail password cookie
    
setcookie("mailpassword",'',time()-1000,$where,$site);
    
$action="loggedout";
}

if (
$action==fulllogout) {
    
//Full logout deletes everything
    
setcookie("mailusername",'',time()-1000,$where,$site);
    
setcookie("mailpassword",'',time()-1000,$where,$site);
    
setcookie("mailemail",'',time()-1000,$where,$site);
    
setcookie("mailname",'',time()-1000,$where,$site);
    
setcookie("mailserver",'',time()-1000,$where,$site);
    
setcookie("widthofpage",'',time()-1000,$where,$site);
    
setcookie("heightofpage",'',time()-1000,$where,$site);
    
$action="fullloggedout";
}

//Start the HTML page
echo "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"><HTML><HEAD><TITLE>Blackberry e-mail</TITLE></HEAD><BODY>";

//And now check for the actions.

if ($action=="privacy") {
    echo
"<P><B>Privacy info</b><HR><P>This service does not use any databases, and does not store your information in any way.</p><P>All login information is stored in a cookie on your own machine, and not stored on this server.</p><P>All activity with this server is not sent securely. You use this at your own risk.</p><P><a href=$SCRIPT_NAME?".time().">Log in</a></p></body></html>";
    die;
}

if (
$action=="loggedout") {
    echo
"<P><B>Logged out.</b><HR><P>All your details except your password still live in a cookie on your Blackberry, for ease of logging in next time. To delete them all, <a href=$SCRIPT_NAME?action=logoutfull&".time()."'>click this</a>.</p><P>Have a good day!</p><P><a href='$SCRIPT_NAME?".time()."'>Log in</a></p></body></html>";
    die;
}

if (
$action=="fullloggedout") {
    echo
"<P><B>Full log-out.</b><HR><P>All your details are now removed.</p><P>As a further precaution, you can delete all cookies on your Blackberry - click the thumbwheel, choose 'Options', then 'General Properties', then click the thumbwheel again to choose 'Delete cookie cache'.<P>Have a good day!</p><P><a href='$SCRIPT_NAME?".time()."'>Log in</a></p></body></html>";
    die;
}

if (!isset(
$mailpassword)) {
// If your password is not set, then the system should ask you to log on.
if (!isset($widthofpage)) {$widthofpage = "45";}
if (!isset(
$heightofpage)) {$heightofpage = "8";}
    echo
"<form action='$SCRIPT_NAME?".time()."' method=post>
    <B>Log on</b> | <a href='$SCRIPT_NAME?action=privacy'>privacy</a><hr>
    IMAP mailserver:
    <input name=setmailserver value='$mailserver'><BR>
    Username:
    <input name=setmailusername value='$mailusername'><BR>
    Password:
    <input type=password name=setmailpassword><BR>
    Display email:
    <input name=setmailemail value='$mailemail'><BR>
    Display name:
    <input name=setmailname value='$mailname'><BR>
    <input type=hidden name=action value=setcookie><input type=submit value='Log in'><HR>
    Optional configuration:
    Screen width:
    <input name=setwidthofpage value='$widthofpage'><BR>
    Screen height:
    <input name=setheightofpage value='$heightofpage'><BR>
    To change optional configuration, log out again.
    </form></body></html>"
;
    die;
}

// Connect to the mailbox.
$mbox = @imap_open ("{".$mailserver.":143}INBOX", $mailusername, $mailpassword) or die("I cannot connect.<BR><BR><a href='$SCRIPT_NAME?action=logout&".time()."'>Log out to try connecting again</a>.");

if (!isset(
$action)) {$action="list";}
if (isset(
$read)) {$action="read";}
if (isset(
$delete)) {$action="delete";}
if (isset(
$send)) {$action="send";}

if (
$action=="send") {
//Sending a piece of mail
    
if ($send>0) {
           
$header = imap_headerinfo($mbox, $send, 32, 32);
            
$from = $header->from;
            foreach (
$from as $id => $object) {
               
$fromname = $object->personal;
               
$fromaddress = $object->mailbox . "@" . $object->host;
            if (
strlen($fromname)<2) {
               
$fromname = substr($fromaddress,0,strpos($fromaddress,'@'));
            }
        }
           
$subject = "Re: ".decode_iso88591($header->subject);
    }
    echo
"<form action=$SCRIPT_NAME method=post>";
    echo
"To: <input name=toname value='$fromname'><BR>Email: <input name=email value='$fromaddress'><BR>";
    echo
"Subject: <input name=subject value='$subject'><HR>";
    if (
$send>0) {
        
$body = imap_body($mbox, $send);
    }
    echo
"<TEXTAREA rows='$heightofpage' name=message></textarea>";
    echo
"<input type=hidden name=action value=send2>";
    echo
"<input type=submit value=Send>";
    echo
"</form>";
}

if (
$action=="send2") {
// Do the send.
    
mail("$toname <$email>", "$subject", stripslashes($message),
        
"From: $mailname <$mailemail>\r\n" .
         
"X-Mailer: jrc/Blackberry http://james.cridland.net/code/\r\n");
    
$action="list";
}

if (
$action=="delete") {
    
//Mark a piece of e-mail for deletion
    
imap_delete($mbox, $delete);
    
$action='expunge';
}

if (
$action=="expunge") {
    
//Delete all e-mail marked for deletion
    
imap_expunge($mbox);
    
$action='list';
}

if (
$action=="read") {
    
$header = imap_headerinfo($mbox, $read, 32, 32);
    
$from = $header->from;
    foreach (
$from as $id => $object) {
        
$fromname = $object->personal;
        
$fromaddress = $object->mailbox . "@" . $object->host;
        if (
strlen($fromname)<2) {
            
$fromname = substr($fromaddress,0,strpos($fromaddress,'@'));
        }
    }
    
$subject = decode_iso88591($header->subject);
    
$tools="<a href='$SCRIPT_NAME?delete=$read&nocache=".time()."'>delete</a> | <a href='$SCRIPT_NAME?send=$read&nocache=".time()."'>reply</a> | <a href='$SCRIPT_NAME?".time()."'>index</a>";
    if (!isset(
$html)) {
    if (
strlen(get_part($mbox, $read, "TEXT/HTML"))>1) {$tools=$tools." | <a href='$SCRIPT_NAME?read=$read&html=yes&".time()."'>HTML</a>"; }
    } else {
    if (
strlen(get_part($mbox, $read, "TEXT/PLAIN"))>1) {$tools=$tools." | <a href='$SCRIPT_NAME?read=$read&".time()."'>text</a>"; }
    }
    echo
"<small><a href='mailto:$fromaddress'>$fromname</a><BR>$subject<HR>$tools</small><hr>";
    if (!isset(
$html)) {
    
$body = get_part($mbox, $read, "TEXT/PLAIN");
    
//Make this look nice and spot any links
    
$body = ereg_replace("\n","<br>",$body);
    
$body = preg_replace("/([^\w\/])(www\.[a-z0-9\-]+\.[a-z0-9\-]+)/i","$1http://$2",    $body);
    
$body = preg_replace("/([\w]+:\/\/[\w-?&;#~=\.\/\@]+[\w\/])/i","<A TARGET=\"_blank\" HREF=\"$1\">$1</A>", $body);
    
$body = preg_replace("/([\w-?&;#~=\.\/]+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?))/i","<A    HREF=\"mailto:$1\">$1</A>",$body);
    if (
strlen($body)<1) {$body="There is no plaintext version of this mail.";}
    } else {
    
$body = get_part($mbox, $read, "TEXT/HTML");
    }
    echo
$body;

    
    echo
"<hr><small>$tools</small>";
}


if (
$action=="list") {
    
//List all the e-mails.
    
$status=imap_status($mbox,"{imap.spamcop.net:143}INBOX", SA_ALL);
    echo
$status->unseen."/".$status->messages." | <a href='$SCRIPT_NAME?action=send&".time()."'>new</a> | <a href='$SCRIPT_NAME?".time()."'>refresh</a>";
    if (!isset(
$start)) {$start = imap_num_msg($mbox);}
    
$end = ($start-$heightofpage);
    if (
$end<0) {$end=0;} else {echo " | <a href='$SCRIPT_NAME?action=list&start=".($end-1)."&".time()."'>more</a>";}
    
$note="";
    if (
$start=="0") {$note = "You have no mail.";}
    echo
"<HR>";
        for (
$i = $start; $i > $end; $i--)
        {
           
$header = imap_headerinfo($mbox, $i, 32, 32);
            
$from = $header->from;
            foreach (
$from as $id => $object) {
               
$fromname = $object->personal;
               
$fromaddress = $object->mailbox . "@" . $object->host;
            if (
strlen($fromname)<2) {
               
$fromname = substr($fromaddress,0,strpos($fromaddress,'@'));
            }
        }
        
$subject = decode_iso88591($header->subject);
        
$text="$fromname: $subject";
             if (
$header->Unseen == "U" || $header->Recent == "N") { echo "<B>";
             if (
strlen($text)>$widthofpage-5) {$text=substr($text,0,($widthofpage-7))."...";}
             } else {
             if (
$header->Deleted == "D") { echo "[D]"; $note="[D] - Deleted messages will be expunged if you delete further messages. <a href='$SCRIPT_NAME?action=expunge&nocache=".time()."'>Expunge now</a>";}
                 if (
strlen($text)>$widthofpage) {$text=substr($text,0,$widthofpage-2)."...";}
             }
            echo
"<a href='".$SCRIPT_NAME."?read=$i&".time()."'>$text</a>";

            if (
$header->Unseen == "U" || $header->Recent == "N") { echo "</b>"; }

            echo
"<br>";

        
flush();
        }

    echo
$note;
    echo
"<HR>".substr($mailemail,0,strpos($mailemail,"@")).": <a href='$SCRIPT_NAME?action=logout&".time()."'>logout</a>";
}

//Whatever we've been doing, close the mailbox.
imap_close($mbox);

//End the HTML page
echo "</BODY></HTML>";



//internal functions follow

//To decode MIME headers - from www.php.net
function decode_iso88591($string){
  if (
strpos(strtolower($string), '=?iso-8859-1')===false ) return $string;
  
$string=explode('?', $string);
  return
strtolower($string[2]) == 'q' ?quoted_printable_decode($string[3]):base64_decode($string[3]);
}

//To decode MIME parts - from http://www.linuxscope.net/articles/mailAttachmentsPHP.html

function get_mime_type(&$structure) {
   
$primary_mime_type = array("TEXT", "MULTIPART","MESSAGE", "APPLICATION", "AUDIO","IMAGE", "VIDEO", "OTHER");
   if(
$structure->subtype) {
       return
$primary_mime_type[(int) $structure->type] . '/' .$structure->subtype;
   }
       return
"TEXT/PLAIN";
   }
   function
get_part($stream, $msg_number, $mime_type, $structure = false,$part_number    = false) {
   
       if(!
$structure) {
           
$structure = imap_fetchstructure($stream, $msg_number);
       }
       if(
$structure) {
           if(
$mime_type == get_mime_type($structure)) {
               if(!
$part_number) {
                   
$part_number = "1";
               }
               
$text = imap_fetchbody($stream, $msg_number, $part_number);
               if(
$structure->encoding == 3) {
                   return
imap_base64($text);
               } else if(
$structure->encoding == 4) {
                   return
imap_qprint($text);
               } else {
               return
$text;
           }
       }
   
        if(
$structure->type == 1) /* multipart */ {
           while(list(
$index, $sub_structure) = each($structure->parts)) {
               if(
$part_number) {
                   
$prefix = $part_number . '.';
               }
               
$data = get_part($stream, $msg_number, $mime_type, $sub_structure,$prefix .    ($index + 1));
               if(
$data) {
                   return
$data;
               }
           }
// END OF WHILE
           
} // END OF MULTIPART
       
} // END OF STRUTURE
       
return false;
   }
// END OF FUNCTION

?>

Download this code