<?php

/**
 * POP3 stream access
 *
 * Creates POP3 URL handlers for accessing POP3 accounts and emails.
 *
 * LICENSE: Free for non-commercial usage. For commercial usage, contact the author.
 *
 * @author Rick Hodger <rick@fuzzi.org.uk>
 * @version 1.2
 * @package pop3_stream
 * @example example.php
 *
 * HISTORY
 * 1.0 - Initial release
 * 1.1 - Implemented stream_stat to allow usage with copy() and file().
 *     - For directory reading, we now close the connection immediatly. This avoids
 *       problems with locking POP3 servers.
 * 1.2 - Removed imap_uid() call, it causes errors.
 *     - Added Secure POP3 for gmail usage, via pop3s://
 * 1.3 - Experimental IMAP support. Does not work at this time.
 */

class pop3_stream {

    var 
$ih;
    var 
$host;
    var 
$port;
    var 
$username;
    var 
$password;
    var 
$path;
    
// stuff for dir reading
    
var $dir;
    var 
$pos;
    
// stuff for file reading
    
var $data;
    var 
$gotbody;
    var 
$size;
    var 
$time;

    
/* Ignore the $mode, we're only interested in the path */
    
function stream_open($path,$mode,$options,$opened_path) {
        
// parse URL
        
$url=parse_url($path);
        
$this->host=$url['host'];
        if (!empty(
$url['port'])) {
            
$this->port=$url['port'];
        } else {
            if (
$url['scheme']==='imap') {
                
$this->port=143;
            } else {
                
$this->port=110;
            }
        }
        
$this->username=$url['user'];
        
$this->password=$url['pass'];
        
$this->path=substr($url['path'],1);

        
// do we have a path?
        
if (empty($this->path) && $mode!=='np') {
            return 
false;
        }

        
// open IMAP connection
        
switch($url['scheme']) {
            case 
"pop3s":
                
$this->ih=imap_open("{".$this->host.":".$this->port."/pop3/ssl/novalidate-cert}INBOX",$this->username,$this->password);
                break;
            case 
"pop3":
                
$this->ih=imap_open("{".$this->host.":".$this->port."/pop3}INBOX",$this->username,$this->password);
                break;
            case 
"imap":
                
$this->ih=imap_open("{".$this->host.":".$this->port."}INBOX",$this->username,$this->password);
                break;
        }
        if (
$this->ih) {
            if (!empty(
$this->path)) {
                list(
$stats,)=imap_fetch_overview($this->ih,$this->path);
                
$this->size=$stats->size;
                
$this->time=strtotime($stats->date);
            }
            return 
true;
        } else {
            return 
false;
        }
    }

    function 
stream_close() {
        
imap_close($this->ih);
    }

    
/* Smart reader, at first it only downloads the header to memory, but if a read request is made
       beyond the header, we download the rest of the body */
    
function stream_read($count) {
        
// smart... only download the header WHEN data is requested
        
if (empty($this->data)) {
            
$this->pos=0$this->gotbody=false;
            
$this->data imap_fetchheader($this->ih,$this->path);
        }
        
// only download the body once we read past the header
        
if ($this->gotbody==false && ($this->pos+$count strlen($this->data))) {
            
$this->gotbody=true;
            
$this->data .= imap_body($this->ih,$this->path);
            
$this->size=strlen($this->data);
        }
        if (
$this->pos >= $this->size) {
            return 
false;
        } else {
            
$d=substr($this->data,$this->pos,$count);
            if (
$this->pos+$count strlen($this->data)) {
                
$this->pos=strlen($this->data);
            } else {
                
$this->pos $this->pos+$count+1;
            }
            return 
$d;
        }
    }

    
/* Can't write to POP3 */
    
function stream_write($data) {
        return 
false;
    }

    function 
stream_eof() {
        if (
$this->pos == $this->size) {
            return 
true;
        } else {
            return 
false;
        }
    }

    function 
stream_tell() {
        return 
$this->pos;
    }

    function 
stream_seek($offset,$whence) {
        switch(
$whence) {
            case 
SEEK_SET:
                
$this->pos $offset;
                break;
            case 
SEEK_CUR:
                
$this->pos $this->pos+$offset;
                break;
            case 
SEEK_END:
                
$this->pos $this->size+$offset;
                break;
        }
    }

    function 
stream_stat() {
        
$keys = array(
            
'dev'     => 0,
            
'ino'     => 0,
            
'mode'    => 33216,
            
'nlink'   => 0,
            
'uid'     => 0,
            
'gid'     => 0,
            
'rdev'    => 0,
            
'size'    => $this->size,
            
'atime'   => $this->time,
            
'mtime'   => $this->time,
            
'ctime'   => $this->time,
            
'blksize' => 0,
            
'blocks'  => 0
        
);
        return 
$keys;
    }

    function 
dir_opendir($path,$options) {
        
// setting mode to 'np' avoids the path check
        
if ($this->stream_open($path,'np',$options,'')) {
            
$this->dir imap_num_msg($this->ih);
            
$this->pos=1;
            
$this->stream_close();
            return 
true;
        } else {
            return 
false;
        }
    }

    function 
dir_closedir() {
        
// do nothing.
    
}

    function 
url_stat($path,$flags) {
        if (
$this->stream_open($path,'','','')) {
            
$stats=array();
            list(
$stats,)=imap_fetch_overview($this->ih,$this->path);
            
$time=strtotime($stats->date);
            
$keys = array(
                
'dev'     => 0,
                
'ino'     => 0,
                
'mode'    => 33216,
                
'nlink'   => 0,
                
'uid'     => 0,
                
'gid'     => 0,
                
'rdev'    => 0,
                
'size'    => $stats->size,
                
'atime'   => $time,
                
'mtime'   => $time,
                
'ctime'   => $time,
                
'blksize' => 0,
                
'blocks'  => 0
            
);
            
$this->stream_close();
            return 
$keys;
        } else {
            return 
false;
        }        
    }

    function 
dir_readdir() {
        if (
$this->pos>$this->dir) {
            return 
false;
        } else {
            
$x=$this->pos;
            
$this->pos++;
        }
        return 
$x;
    }

    function 
dir_rewinddir() {
        
$this->pos=1;
    }

    
/* Delete an email from the mailbox */
    
function unlink($path) {
        if (
$this->stream_open($path,'','','')) {
            
imap_delete($this->ih,$this->path);
            
imap_expunge($this->ih);
            
$this->stream_close();
            return 
true;
        } else {
            return 
false;
        }
    }

}

stream_wrapper_register('pop3','pop3_stream')
    or die(
'Failed to register POP3 protocol!');
stream_wrapper_register('pop3s','pop3_stream')
    or die(
'Failed to register secure POP3 protocol!');
stream_wrapper_register('imap','pop3_stream')
    or die(
'Failed to register IMAP protocol!');
?>