DMail Milestone 1.0
Drupal Mail Client
imap.lib
Go to the documentation of this file.
00001 <?php
00002 // $Id: 5996b2c311c737287ab1ba4325f2a79e978eb685 $
00011 class IMAP {
00012   protected $host = 'localhost';
00013   protected $port = 143;
00014   protected $service = 'imap';
00015   protected $encrypt = 'none';
00016   protected $validate = FALSE;
00017   protected $mbdelim = '.';
00018   protected $mbuser = array('user' => 'mbuser', 'pass' => 'mbpass');
00019   protected $mbreadonly = FALSE;
00020   protected $mbflags = NULL;
00021   protected $mbconnstr = NULL;
00022   protected $mbox = NULL;
00023   protected $mbres = NULL;
00024   private $mode = NULL;
00025   private $check = NULL;
00026 
00059   function __construct ($user, $pass, $host = NULL, $port = NULL, $service = NULL, $mb_delim = NULL, $encrypt_mode = NULL, $validate_cert = NULL, $readonly = NULL) {
00060     $this->mbuser = array('user' => $user, 'pass' => $pass);
00061     if ($host) $this->host = $host;
00062     if ($port) $this->port = $port;
00063     if ($service) $this->service = $service;
00064     $this->encrypt = $encrypt_mode;
00065     $this->encrypt = ($this->encrypt === 'none' || is_null($this->encrypt)) ? FALSE : $this->encrypt;
00066     if ($validate_cert) $this->validate = ($validate_cert) === 'yes' ? TRUE : FALSE;
00067     if ($mb_delim) $this->mbdelim = $mb_delim;
00068     if ($readonly) $this->mbreadonly = ($readonly) === 'yes' ? TRUE : FALSE;
00069     $this->mbflags = '/service=' . $this->service;
00070     if ($this->mbreadonly) {
00071       $this->mbflags .= '/readonly';
00072     }
00073     if ($this->encrypt) {
00074       $this->mbflags .= '/' . $this->encrypt;
00075     }
00076     if ($this->validate) {
00077       $this->mbflags .= '/validate-cert';
00078     }
00079     else {
00080       $this->mbflags .= '/novalidate-cert';
00081     }
00082   }
00083 
00087   function __destruct() {
00088     $this->close();
00089   }
00090 
00100    function __set($param, $value) {
00101      switch ($param) {
00102        case 'mbox':
00103        case 'mailbox': {
00104          $this->mbox = $value;
00105        } break;
00106      }
00107    }
00108 
00130   function open($mode = 0, $force = FALSE) {
00131     if (!$this->mbconnstr) {
00132       $this->mbconnstr = '{' . $this->host . ':' . $this->port . $this->mbflags . '}';
00133     }
00134     $user =& $this->mbuser['user'];
00135     $pass =& $this->mbuser['pass'];
00136     if ($this->mbreadonly) {
00137       $mode |= OP_READONLY;
00138     }
00139     if ($mode & OP_HALFOPEN) {
00140       $mb = NULL;
00141     }
00142     else {
00143       $mb = $this->mbox;
00144     }
00145     if (!$force && $this->mbres) {
00146       imap_reopen($this->mbres, $this->mbconnstr . $mb, $mode);
00147       $error = $this->error();
00148       if ($error->type == 'CLOSED') {
00149         return  $this->open($mode, TRUE);
00150       }
00151     }
00152     else {
00153       if ($force && !$mode) {
00154         $mode == $this->mode;
00155       }
00156       else {
00157         $this->mode = $mode;
00158       }
00159       $this->mbres = imap_open($this->mbconnstr . $mb, $user, $pass, $mode);
00160     }
00161     if ($errors = imap_errors()) {
00162       foreach ($errors as $error) {
00163         drupal_set_message($error, 'error');
00164       }
00165     }
00166     return  $this->mbres;
00167   }
00168 
00175   function close() {
00176     $return = NULL;
00177     if (is_resource($this->mbres)) $return = imap_close($this->mbres);
00178     return $return;
00179   }
00180 
00195   function check($mbox_name = NULL) {
00196     $this->check_mbox($mbox_name);
00197     return imap_check($this->mbres);
00198   }
00199 
00225   function status($mbox_name = NULL, $options = SA_ALL) {
00226     $this->check_mbox($mbox_name);
00227     $mbox = $this->mbconnstr . $this->mbox;
00228     return imap_status($this->mbres, $mbox, $options);
00229   }
00230 
00263   function fetch_overview($messages, $mbox_name = NULL) {
00264     $this->check_mbox($mbox_name);
00265     watchdog('imap debug', print_r($messages, true));
00266     return imap_fetch_overview($this->mbres, $messages, 0);
00267   }
00268 
00294   function list_headers($mbox_name) {
00295     $headers = array();
00296     $this->check_mbox($mbox_name);
00297     $check = imap_check($this->mbres);
00298     $headers = imap_fetch_overview($this->mbres, "1:{$check->Nmsgs}", 0);
00299     return $headers;
00300   }
00301 
00302   /*
00303    * Delete the mailbox item.
00304    *
00305    * @param $msgno
00306    * - The message sequence number.
00307    *
00308    * @return
00309    * - The result of the delete, TRUE or FALSE.
00310    */
00311   function delete_item($msgno) {
00312     return imap_delete($this->mbres, $msgno);
00313   }
00314 
00326   function copy_item($msgno, $to_folder) {
00327     return imap_mail_copy($this->mbres, $msgno, $to_folder);
00328   }
00329 
00341   function move_item($msgno, $to_folder) {
00342     return imap_mail_move($this->mbres, $msgno, $to_folder);
00343   }
00344 
00354   function expunge($mbox_name = NULL) {
00355     $this->check_mbox($mbox_name);
00356     return imap_expunge($this->mbres);
00357   }
00358 
00449   function headerinfo($msgno) {
00450     static $except = FALSE;
00451     $fetch = imap_headerinfo($this->mbres, $msgno);
00452     $error = $this->error();
00453     if ($error) {
00454       switch ($error->type) {
00455         case 'CLOSED': {
00456           if (!$except) {
00457             $this->open(0, TRUE);
00458             $except = TRUE;
00459             return $this->headerinfo($msgno);
00460           }
00461           else {
00462             throw new Exception($error->text);
00463           }
00464         } break;
00465         case 'WARNING': {
00466           //ignore.
00467         } break;
00468         default: {
00469           throw new Exception($error->text);
00470         } break;
00471       }
00472     }
00473     $except = FALSE;
00474     return $fetch;
00475   }
00476 
00486   function fetchheader($msgno) {
00487     static $except = FALSE;
00488     $fetch = imap_fetchheader($this->mbres, $msgno);
00489     $error = $this->error();
00490     if ($error) {
00491       switch ($error->type) {
00492         case 'CLOSED': {
00493           if (!$except) {
00494             $this->open(0, TRUE);
00495             $except = TRUE;
00496             return $this->fetchheader($msgno);
00497           }
00498           else {
00499             throw new Exception($error->text);
00500           }
00501         } break;
00502         case 'WARNING': {
00503           //ignore.
00504         } break;
00505         default: {
00506           throw new Exception($error->text);
00507         } break;
00508       }
00509     }
00510     $except = FALSE;
00511     return $fetch;
00512   }
00513 
00547   function fetchstructure($msgno) {
00548     static $except = FALSE;
00549     $fetch = imap_fetchstructure($this->mbres, $msgno);
00550     $error = $this->error();
00551     if ($error) {
00552       switch ($error->type) {
00553         case 'CLOSED': {
00554           if (!$except) {
00555             $this->open(0, TRUE);
00556             $except = TRUE;
00557             return $this->fetchstructure($msgno);
00558           }
00559           else {
00560             throw new Exception($error->text);
00561           }
00562         } break;
00563         case 'WARNING': {
00564           //ignore.
00565         } break;
00566         default: {
00567           throw new Exception($error->text);
00568         } break;
00569       }
00570     }
00571     $except = FALSE;
00572     return $fetch;
00573   }
00574 
00587   function bodystruct($msgno, $partno) {
00588     static $except = FALSE;
00589     $fetch = imap_bodystruct($this->mbres, $msgno, $partno);
00590     $error = $this->error();
00591     if ($error) {
00592       switch ($error->type) {
00593         case 'CLOSED': {
00594           if (!$except) {
00595             $this->open(0, TRUE);
00596             $except = TRUE;
00597             return $this->bodystruct($msgno, $partno);
00598           }
00599           else {
00600             throw new Exception($error->text);
00601           }
00602         } break;
00603         case 'WARNING': {
00604           //ignore.
00605         } break;
00606         default: {
00607           throw new Exception($error->text);
00608         } break;
00609       }
00610     }
00611     $except = FALSE;
00612     return $fetch;
00613   }
00614 
00625   function list_mbox($mbox) {
00626     $mboxes = imap_getmailboxes($this->mbres, $this->mbconnstr, $mbox);
00627     if (count($mboxes) === 0) {
00628       return FALSE;
00629     }
00630     elseif (count($mboxes) > 1) {
00631       throw new Exception('Invalid mailbox specification.');
00632     }
00633     $mbx =& $mboxes[0];
00634     $status = imap_status($this->mbres, $this->mbconnstr . $mbox, SA_ALL);
00635     foreach ((array)$status as $key => $stat) {
00636       $mbx->$key = $stat;
00637     }
00638     return $mbx;
00639   }
00640 
00647   function list_mboxes() {
00648     $mboxes = imap_getmailboxes($this->mbres, $this->mbconnstr, '*');
00649     $return = array();
00650     foreach ($mboxes as $mbox) {
00651       list(,$mbox) = explode('}', $mbox->name);
00652       $return[] = $this->list_mbox($mbox);
00653     }
00654     return $return;
00655   }
00656 
00677   function fetch_item($msgno, $mime_part = 'TEXT/PLAIN', $parts = NULL) {
00678     if (is_null($parts)) {
00679       $structure = imap_fetchstructure($this->mbres, $msgno);
00680       $parts = $this->parts($structure, $msgno);
00681       return $this->fetch_item($msgno, $mime_part, $parts);
00682     }
00683     else {
00684       $ret = NULL;
00685       foreach ($parts as $part) {
00686         if ($part->mime_type === $mime_part) {
00687           $ret .= "\r\n\r\n" . $part->text;
00688         }
00689         elseif ($mime_part == 'ATTACHMENT' &&
00690           $part->disposition == 'attachment')
00691         {
00692           $att = new stdClass;
00693           $att->subtype = $att->filename = $att->name = NULL;
00694           $att->subtype = $part->subtype;
00695           if ($part->dparameters) {
00696             $att->{$part->dparameters[0]->attribute} = $part->dparameters[0]->value;
00697           }
00698           if ($part->parameters) {
00699             $att->{$part->parameters[0]->attribute} = $part->parameters[0]->value;
00700           }
00701           $att->contents = $part->text;
00702           $ret[] = $att;
00703         }
00704       }
00705       return $ret;
00706     }
00707   }
00708 
00724   private function parts($structure, $msgno, $partno = FALSE) {
00725     if ($structure) {
00726       $parts = array();
00727       if (!$structure->parts) {
00728         if ($partno === FALSE) {
00729           $partno = '1';
00730         }
00731         $body = imap_fetchbody($this->mbres, $msgno, $partno);
00732         $bodystruct = imap_bodystruct($this->mbres, $msgno, $partno);
00733         $body = $this->part_decode($bodystruct->encoding, $body);
00734         if ($bodystruct->ifparameters) {
00735           foreach ($bodystruct->parameters as $parameter) {
00736             if (strtolower($parameter->attribute) == 'charset') {
00737               $body = iconv($parameter->value, 'utf-8', $body);
00738               break;
00739             }
00740           }
00741         }
00742         $part = new stdClass;
00743         $part->partno = $partno;
00744         $part->mime_type = $this->mime_type($bodystruct);
00745         $part->text = $body;
00746         $part->bytes = $bodystruct->bytes;
00747         $part->subtype = $bodystruct->ifsubtype ? $bodystruct->subtype : NULL;
00748         $part->disposition = $bodystruct->ifdisposition ? $bodystruct->disposition : NULL;
00749         $part->dparameters = $bodystruct->ifdparameters ? $bodystruct->dparameters : NULL;
00750         $part->parameters = $bodystruct->ifparameters ? $bodystruct->parameters : NULL;
00751         $parts[$partno] = $part;
00752       }
00753       else {
00754         while (list($index, $substructure) = each($structure->parts)) {
00755           if ($partno) {
00756             $prefix = $partno . '.';
00757           }
00758           else {
00759             $prefix = NULL;
00760           }
00761           $data = $this->parts($substructure, $msgno, $prefix . ($index + 1));
00762           $parts += $data;
00763         }
00764       }
00765       return $parts;
00766     }
00767     return FALSE;
00768   }
00769 
00783   private function part_decode($encoding, $text) {
00784     switch ($encoding) {
00785       case 3: {
00786         return imap_base64($text);
00787       } break;
00788       case 4: {
00789         return quoted_printable_decode($text); // This resolved and issue of NULL being returned with imap_qprint
00790         return imap_qprint($text);
00791       } break;
00792       default: {
00793         return $text;
00794       } break;
00795     }
00796   }
00797 
00808   private function mime_type($structure) {
00809     $primary_mime_types = array('TEXT', 'MULTIPART', 'MESSAGE', 'APPLICATION', 'AUDIO', 'IMAGE', 'VIDEO', 'OTHER');
00810     if ($structure->subtype) {
00811       $mime_type = $primary_mime_types[$structure->type] . '/' . $structure->subtype;
00812     }
00813     else {
00814       $mime_type = "TEXT/PLAIN";
00815     }
00816     return $mime_type;
00817   }
00818 
00831   private function error() {
00832     $text = imap_last_error();
00833     if ($text === FALSE) {
00834       return $text;
00835     }
00836     $parts = explode(' ', strtolower($text));
00837     switch ($parts[0]) {
00838       case 'warning:': {
00839         $type = 'WARNING';
00840       } break;
00841       case 'unexpected': {
00842         $type = 'WARNING';
00843       } break;
00844       case 'unterminated': {
00845         $type = 'WARNING';
00846       } break;
00847       case '[closed]': {
00848         $type = 'CLOSED';
00849       } break;
00850       case 'invalid': {
00851         if ($parts[1] === 'mailbox' && $parts[2] === 'list:' && $parts[3] === '<>') {
00852           $type = 'WARNING';
00853         }
00854         else {
00855           $type = 'UNKNOWN';
00856         }
00857       } break;
00858       case 'must': {
00859         if ($parts[1] === 'use' && $parts[2] === 'comma') {
00860           $type = 'WARNING';
00861         }
00862         else {
00863           $type = 'UNKNOWN';
00864         }
00865       } break;
00866       case 'no': {
00867         if ($parts[1] === 'body' && $parts[2] === 'information') {
00868           $type = 'WARNING';
00869         }
00870         else {
00871           $type = 'UNKNOWN';
00872         }
00873       } break;
00874       case 'store': {
00875         if ($parts[3] === 'read-only') {
00876           $type = 'WARNING';
00877         }
00878         else {
00879           $type = 'UNKNOWN';
00880         }
00881       } break;
00882       default: {
00883         $type = 'UNKNOWN';
00884       } break;
00885     }
00886     $error->type = $type;
00887     $error->text = $text;
00888     return $error;
00889   }
00890 
00903   private function check_mbox($mbox_name, $open_mode = OP_READONLY) {
00904     if ($mbox_name) {
00905       if ($mbox_name != $this->mbox) {
00906         $this->mbox = $mbox_name;
00907         $this->open($open_mode);
00908       }
00909     }
00910   }
00911 
00912 }
All Data Structures Files Functions Variables Enumerations