Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F120824562
rcube_imap.inc
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
78 KB
Referenced Files
None
Subscribers
None
rcube_imap.inc
View Options
<
?
php
/*
+-----------------------------------------------------------------------+
| program/include/rcube_imap.inc |
| |
| This file is part of the RoundCube Webmail client |
| Copyright (C) 2005-2007, RoundCube Dev. - Switzerland |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| IMAP wrapper that implements the Iloha IMAP Library (IIL) |
| See http://ilohamail.org/ for details |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
/*
* Obtain classes from the Iloha IMAP library
*/
require_once
(
'
lib
/
imap
.
inc
'
);
require_once
(
'
lib
/
mime
.
inc
'
);
/**
* Interface class for accessing an IMAP server
*
* This is a wrapper that implements the Iloha IMAP Library (IIL)
*
* @package Mail
* @author Thomas Bruederli <roundcube@gmail.com>
* @version 1.40
* @link http://ilohamail.org
*/
class
rcube_imap
{
var
$
db
;
var
$
conn
;
var
$
root_ns
=
''
;
var
$
root_dir
=
''
;
var
$
mailbox
=
'
INBOX
'
;
var
$
list_page
=
1
;
var
$
page_size
=
10
;
var
$
sort_field
=
'
date
'
;
var
$
sort_order
=
'
DESC
'
;
var
$
delimiter
=
NULL
;
var
$
caching_enabled
=
FALSE
;
var
$
default_charset
=
'
ISO
-
8859
-
1
'
;
var
$
default_folders
=
array
(
'
INBOX
'
);
var
$
default_folders_lc
=
array
(
'
inbox
'
);
var
$
cache
=
array
();
var
$
cache_keys
=
array
();
var
$
cache_changes
=
array
();
var
$
uid_id_map
=
array
();
var
$
msg_headers
=
array
();
var
$
capabilities
=
array
();
var
$
skip_deleted
=
FALSE
;
var
$
search_set
=
NULL
;
var
$
search_subject
=
''
;
var
$
search_string
=
''
;
var
$
search_charset
=
''
;
var
$
debug_level
=
1
;
var
$
error_code
=
0
;
/**
* Object constructor
*
* @param object DB Database connection
*/
function
__construct
(
$
db_conn
)
{
$
this
-
>
db
=
$
db_conn
;
}
/**
* PHP 4 object constructor
*
* @see rcube_imap::__construct
*/
function
rcube_imap
(
$
db_conn
)
{
$
this
-
>
__construct
(
$
db_conn
);
}
/**
* Connect to an IMAP server
*
* @param string Host to connect
* @param string Username for IMAP account
* @param string Password for IMAP account
* @param number Port to connect to
* @param string SSL schema (either ssl or tls) or null if plain connection
* @return boolean TRUE on success, FALSE on failure
* @access public
*/
function
connect
(
$
host
,
$
user
,
$
pass
,
$
port
=
143
,
$
use_ssl
=
null
)
{
global
$
ICL_SSL
,
$
ICL_PORT
,
$
IMAP_USE_INTERNAL_DATE
;
// check for Open-SSL support in PHP build
if
(
$
use_ssl
&&
in_array
(
'
openssl
'
,
get_loaded_extensions
()))
$
ICL_SSL
=
$
use_ssl
==
'
imaps
'
?
'
ssl
'
:
$
use_ssl
;
else
if
(
$
use_ssl
)
{
raise_error
(
array
(
'
code
'
=
>
403
,
'
type
'
=
>
'
imap
'
,
'
file
'
=
>
__FILE__
,
'
message
'
=
>
'
Open
SSL
not
available
;
'
),
TRUE
,
FALSE
);
$
port
=
143
;
}
$
ICL_PORT
=
$
port
;
$
IMAP_USE_INTERNAL_DATE
=
false
;
$
this
-
>
conn
=
iil_Connect
(
$
host
,
$
user
,
$
pass
,
array
(
'
imap
'
=
>
'
check
'
));
$
this
-
>
host
=
$
host
;
$
this
-
>
user
=
$
user
;
$
this
-
>
pass
=
$
pass
;
$
this
-
>
port
=
$
port
;
$
this
-
>
ssl
=
$
use_ssl
;
// print trace mesages
if
(
$
this
-
>
conn
&&
(
$
this
-
>
debug_level
&
8
))
console
(
$
this
-
>
conn
-
>
message
);
// write error log
else
if
(
!$
this
-
>
conn
&&
$
GLOBALS
[
'
iil_error
'
])
{
$
this
-
>
error_code
=
$
GLOBALS
[
'
iil_errornum
'
];
raise_error
(
array
(
'
code
'
=
>
403
,
'
type
'
=
>
'
imap
'
,
'
message
'
=
>
$
GLOBALS
[
'
iil_error
'
]),
TRUE
,
FALSE
);
}
// get server properties
if
(
$
this
-
>
conn
)
{
$
this
-
>
_parse_capability
(
$
this
-
>
conn
-
>
capability
);
if
(
!
empty
(
$
this
-
>
conn
-
>
delimiter
))
$
this
-
>
delimiter
=
$
this
-
>
conn
-
>
delimiter
;
if
(
!
empty
(
$
this
-
>
conn
-
>
rootdir
))
{
$
this
-
>
set_rootdir
(
$
this
-
>
conn
-
>
rootdir
);
$
this
-
>
root_ns
=
ereg_replace
(
'
[
\.\
/
]
$'
,
''
,
$
this
-
>
conn
-
>
rootdir
);
}
}
return
$
this
-
>
conn
?
TRUE
:
FALSE
;
}
/**
* Close IMAP connection
* Usually done on script shutdown
*
* @access public
*/
function
close
()
{
if
(
$
this
-
>
conn
)
iil_Close
(
$
this
-
>
conn
);
}
/**
* Close IMAP connection and re-connect
* This is used to avoid some strange socket errors when talking to Courier IMAP
*
* @access public
*/
function
reconnect
()
{
$
this
-
>
close
();
$
this
-
>
connect
(
$
this
-
>
host
,
$
this
-
>
user
,
$
this
-
>
pass
,
$
this
-
>
port
,
$
this
-
>
ssl
);
}
/**
* Set a root folder for the IMAP connection.
*
* Only folders within this root folder will be displayed
* and all folder paths will be translated using this folder name
*
* @param string Root folder
* @access public
*/
function
set_rootdir
(
$
root
)
{
if
(
ereg
(
'
[
\.\
/
]
$'
,
$
root
))
//(substr($root, -1, 1)==='/')
$
root
=
substr
(
$
root
,
0
,
-
1
);
$
this
-
>
root_dir
=
$
root
;
if
(
empty
(
$
this
-
>
delimiter
))
$
this
-
>
get_hierarchy_delimiter
();
}
/**
* Set default message charset
*
* This will be used for message decoding if a charset specification is not available
*
* @param string Charset string
* @access public
*/
function
set_charset
(
$
cs
)
{
$
this
-
>
default_charset
=
$
ch
;
}
/**
* This list of folders will be listed above all other folders
*
* @param array Indexed list of folder names
* @access public
*/
function
set_default_mailboxes
(
$
arr
)
{
if
(
is_array
(
$
arr
))
{
$
this
-
>
default_folders
=
$
arr
;
$
this
-
>
default_folders_lc
=
array
();
// add inbox if not included
if
(
!
in_array_nocase
(
'
INBOX
'
,
$
this
-
>
default_folders
))
array_unshift
(
$
this
-
>
default_folders
,
'
INBOX
'
);
// create a second list with lower cased names
foreach
(
$
this
-
>
default_folders
as
$
mbox
)
$
this
-
>
default_folders_lc
[]
=
strtolower
(
$
mbox
);
}
}
/**
* Set internal mailbox reference.
*
* All operations will be perfomed on this mailbox/folder
*
* @param string Mailbox/Folder name
* @access public
*/
function
set_mailbox
(
$
new_mbox
)
{
$
mailbox
=
$
this
-
>
_mod_mailbox
(
$
new_mbox
);
if
(
$
this
-
>
mailbox
==
$
mailbox
)
return
;
$
this
-
>
mailbox
=
$
mailbox
;
// clear messagecount cache for this mailbox
$
this
-
>
_clear_messagecount
(
$
mailbox
);
}
/**
* Set internal list page
*
* @param number Page number to list
* @access public
*/
function
set_page
(
$
page
)
{
$
this
-
>
list_page
=
(
int
)
$
page
;
}
/**
* Set internal page size
*
* @param number Number of messages to display on one page
* @access public
*/
function
set_pagesize
(
$
size
)
{
$
this
-
>
page_size
=
(
int
)
$
size
;
}
/**
* Save a set of message ids for future message listing methods
*
* @param array List of IMAP fields to search in
* @param string Search string
* @param array List of message ids or NULL if empty
*/
function
set_search_set
(
$
subject
,
$
str
=
null
,
$
msgs
=
null
,
$
charset
=
null
)
{
if
(
is_array
(
$
subject
)
&&
$
str
==
null
&&
$
msgs
==
null
)
list
(
$
subject
,
$
str
,
$
msgs
,
$
charset
)
=
$
subject
;
if
(
$
msgs
!
=
null
&&
!
is_array
(
$
msgs
))
$
msgs
=
split
(
'
,
'
,
$
msgs
);
$
this
-
>
search_subject
=
$
subject
;
$
this
-
>
search_string
=
$
str
;
$
this
-
>
search_set
=
(
array
)
$
msgs
;
$
this
-
>
search_charset
=
$
charset
;
}
/**
* Return the saved search set as hash array
* @return array Search set
*/
function
get_search_set
()
{
return
array
(
$
this
-
>
search_subject
,
$
this
-
>
search_string
,
$
this
-
>
search_set
,
$
this
-
>
search_charset
);
}
/**
* Returns the currently used mailbox name
*
* @return string Name of the mailbox/folder
* @access public
*/
function
get_mailbox_name
()
{
return
$
this
-
>
conn
?
$
this
-
>
_mod_mailbox
(
$
this
-
>
mailbox
,
'
out
'
)
:
''
;
}
/**
* Returns the IMAP server's capability
*
* @param string Capability name
* @return mixed Capability value or TRUE if supported, FALSE if not
* @access public
*/
function
get_capability
(
$
cap
)
{
$
cap
=
strtoupper
(
$
cap
);
return
$
this
-
>
capabilities
[
$
cap
];
}
/**
* Returns the delimiter that is used by the IMAP server for folder separation
*
* @return string Delimiter string
* @access public
*/
function
get_hierarchy_delimiter
()
{
if
(
$
this
-
>
conn
&&
empty
(
$
this
-
>
delimiter
))
$
this
-
>
delimiter
=
iil_C_GetHierarchyDelimiter
(
$
this
-
>
conn
);
if
(
empty
(
$
this
-
>
delimiter
))
$
this
-
>
delimiter
=
'
/
'
;
return
$
this
-
>
delimiter
;
}
/**
* Public method for mailbox listing.
*
* Converts mailbox name with root dir first
*
* @param string Optional root folder
* @param string Optional filter for mailbox listing
* @return array List of mailboxes/folders
* @access public
*/
function
list_mailboxes
(
$
root
=
''
,
$
filter
=
'
*
'
)
{
$
a_out
=
array
();
$
a_mboxes
=
$
this
-
>
_list_mailboxes
(
$
root
,
$
filter
);
foreach
(
$
a_mboxes
as
$
mbox_row
)
{
$
name
=
$
this
-
>
_mod_mailbox
(
$
mbox_row
,
'
out
'
);
if
(
strlen
(
$
name
))
$
a_out
[]
=
$
name
;
}
// INBOX should always be available
if
(
!
in_array_nocase
(
'
INBOX
'
,
$
a_out
))
array_unshift
(
$
a_out
,
'
INBOX
'
);
// sort mailboxes
$
a_out
=
$
this
-
>
_sort_mailbox_list
(
$
a_out
);
return
$
a_out
;
}
/**
* Private method for mailbox listing
*
* @return array List of mailboxes/folders
* @see rcube_imap::list_mailboxes()
* @access private
*/
function
_list_mailboxes
(
$
root
=
''
,
$
filter
=
'
*
'
)
{
$
a_defaults
=
$
a_out
=
array
();
// get cached folder list
$
a_mboxes
=
$
this
-
>
get_cache
(
'
mailboxes
'
);
if
(
is_array
(
$
a_mboxes
))
return
$
a_mboxes
;
// retrieve list of folders from IMAP server
$
a_folders
=
iil_C_ListSubscribed
(
$
this
-
>
conn
,
$
this
-
>
_mod_mailbox
(
$
root
),
$
filter
);
if
(
!
is_array
(
$
a_folders
)
||
!
sizeof
(
$
a_folders
))
$
a_folders
=
array
();
// write mailboxlist to cache
$
this
-
>
update_cache
(
'
mailboxes
'
,
$
a_folders
);
return
$
a_folders
;
}
/**
* Get message count for a specific mailbox
*
* @param string Mailbox/folder name
* @param string Mode for count [ALL|UNSEEN|RECENT]
* @param boolean Force reading from server and update cache
* @return int Number of messages
* @access public
*/
function
messagecount
(
$
mbox_name
=
''
,
$
mode
=
'
ALL
'
,
$
force
=
FALSE
)
{
$
mailbox
=
$
mbox_name
?
$
this
-
>
_mod_mailbox
(
$
mbox_name
)
:
$
this
-
>
mailbox
;
return
$
this
-
>
_messagecount
(
$
mailbox
,
$
mode
,
$
force
);
}
/**
* Private method for getting nr of messages
*
* @access private
* @see rcube_imap::messagecount()
*/
function
_messagecount
(
$
mailbox
=
''
,
$
mode
=
'
ALL
'
,
$
force
=
FALSE
)
{
$
a_mailbox_cache
=
FALSE
;
$
mode
=
strtoupper
(
$
mode
);
if
(
empty
(
$
mailbox
))
$
mailbox
=
$
this
-
>
mailbox
;
// count search set
if
(
$
this
-
>
search_string
&&
$
mailbox
==
$
this
-
>
mailbox
&&
$
mode
==
'
ALL
'
&&
!$
force
)
return
count
((
array
)
$
this
-
>
search_set
);
$
a_mailbox_cache
=
$
this
-
>
get_cache
(
'
messagecount
'
);
// return cached value
if
(
!$
force
&&
is_array
(
$
a_mailbox_cache
[
$
mailbox
])
&&
isset
(
$
a_mailbox_cache
[
$
mailbox
][
$
mode
]))
return
$
a_mailbox_cache
[
$
mailbox
][
$
mode
];
// RECENT count is fetched abit different
if
(
$
mode
==
'
RECENT
'
)
$
count
=
iil_C_CheckForRecent
(
$
this
-
>
conn
,
$
mailbox
);
// use SEARCH for message counting
else
if
(
$
this
-
>
skip_deleted
)
{
$
search_str
=
"ALL UNDELETED"
;
// get message count and store in cache
if
(
$
mode
==
'
UNSEEN
'
)
$
search_str
.
=
" UNSEEN"
;
// get message count using SEARCH
// not very performant but more precise (using UNDELETED)
$
count
=
0
;
$
index
=
$
this
-
>
_search_index
(
$
mailbox
,
$
search_str
);
if
(
is_array
(
$
index
))
{
$
str
=
implode
(
","
,
$
index
);
if
(
!
empty
(
$
str
))
$
count
=
count
(
$
index
);
}
}
else
{
if
(
$
mode
==
'
UNSEEN
'
)
$
count
=
iil_C_CountUnseen
(
$
this
-
>
conn
,
$
mailbox
);
else
$
count
=
iil_C_CountMessages
(
$
this
-
>
conn
,
$
mailbox
);
}
if
(
!
is_array
(
$
a_mailbox_cache
[
$
mailbox
]))
$
a_mailbox_cache
[
$
mailbox
]
=
array
();
$
a_mailbox_cache
[
$
mailbox
][
$
mode
]
=
(
int
)
$
count
;
// write back to cache
$
this
-
>
update_cache
(
'
messagecount
'
,
$
a_mailbox_cache
);
return
(
int
)
$
count
;
}
/**
* Public method for listing headers
* convert mailbox name with root dir first
*
* @param string Mailbox/folder name
* @param int Current page to list
* @param string Header field to sort by
* @param string Sort order [ASC|DESC]
* @return array Indexed array with message header objects
* @access public
*/
function
list_headers
(
$
mbox_name
=
''
,
$
page
=
NULL
,
$
sort_field
=
NULL
,
$
sort_order
=
NULL
)
{
$
mailbox
=
$
mbox_name
?
$
this
-
>
_mod_mailbox
(
$
mbox_name
)
:
$
this
-
>
mailbox
;
return
$
this
-
>
_list_headers
(
$
mailbox
,
$
page
,
$
sort_field
,
$
sort_order
);
}
/**
* Private method for listing message headers
*
* @access private
* @see rcube_imap::list_headers
*/
function
_list_headers
(
$
mailbox
=
''
,
$
page
=
NULL
,
$
sort_field
=
NULL
,
$
sort_order
=
NULL
,
$
recursive
=
FALSE
)
{
if
(
!
strlen
(
$
mailbox
))
return
array
();
// use saved message set
if
(
$
this
-
>
search_string
&&
$
mailbox
==
$
this
-
>
mailbox
)
return
$
this
-
>
_list_header_set
(
$
mailbox
,
$
this
-
>
search_set
,
$
page
,
$
sort_field
,
$
sort_order
);
$
this
-
>
_set_sort_order
(
$
sort_field
,
$
sort_order
);
$
max
=
$
this
-
>
_messagecount
(
$
mailbox
);
$
start_msg
=
(
$
this
-
>
list_page
-
1
)
*
$
this
-
>
page_size
;
list
(
$
begin
,
$
end
)
=
$
this
-
>
_get_message_range
(
$
max
,
$
page
);
// mailbox is empty
if
(
$
begin
>
=
$
end
)
return
array
();
$
headers_sorted
=
FALSE
;
$
cache_key
=
$
mailbox
.'.
msg
'
;
$
cache_status
=
$
this
-
>
check_cache_status
(
$
mailbox
,
$
cache_key
);
// cache is OK, we can get all messages from local cache
if
(
$
cache_status
>
0
)
{
$
a_msg_headers
=
$
this
-
>
get_message_cache
(
$
cache_key
,
$
start_msg
,
$
start_msg
+
$
this
-
>
page_size
,
$
this
-
>
sort_field
,
$
this
-
>
sort_order
);
$
headers_sorted
=
TRUE
;
}
// cache is dirty, sync it
else
if
(
$
this
-
>
caching_enabled
&&
$
cache_status
==-
1
&&
!$
recursive
)
{
$
this
-
>
sync_header_index
(
$
mailbox
);
return
$
this
-
>
_list_headers
(
$
mailbox
,
$
page
,
$
this
-
>
sort_field
,
$
this
-
>
sort_order
,
TRUE
);
}
else
{
// retrieve headers from IMAP
if
(
$
this
-
>
get_capability
(
'
sort
'
)
&&
(
$
msg_index
=
iil_C_Sort
(
$
this
-
>
conn
,
$
mailbox
,
$
this
-
>
sort_field
,
$
this
-
>
skip_deleted
?
'
UNDELETED
'
:
''
)))
{
$
msgs
=
$
msg_index
[
$
begin
];
for
(
$
i
=
$
begin
+
1
;
$
i
<
$
end
;
$
i
++
)
$
msgs
=
$
msgs
.'
,
'.$
msg_index
[
$
i
];
}
else
{
$
msgs
=
sprintf
(
"%d:%d"
,
$
begin
+
1
,
$
end
);
$
i
=
0
;
for
(
$
msg_seqnum
=
$
begin
;
$
msg_seqnum
<
=
$
end
;
$
msg_seqnum
++
)
$
msg_index
[
$
i
++
]
=
$
msg_seqnum
;
}
// use this class for message sorting
$
sorter
=
new
rcube_header_sorter
();
$
sorter
-
>
set_sequence_numbers
(
$
msg_index
);
// fetch reuested headers from server
$
a_msg_headers
=
array
();
$
deleted_count
=
$
this
-
>
_fetch_headers
(
$
mailbox
,
$
msgs
,
$
a_msg_headers
,
$
cache_key
);
// delete cached messages with a higher index than $max+1
// Changed $max to $max+1 to fix this bug : #1484295
$
this
-
>
clear_message_cache
(
$
cache_key
,
$
max
+
1
);
// kick child process to sync cache
// ...
}
// return empty array if no messages found
if
(
!
is_array
(
$
a_msg_headers
)
||
empty
(
$
a_msg_headers
))
return
array
();
// if not already sorted
if
(
!$
headers_sorted
)
{
$
sorter
-
>
sort_headers
(
$
a_msg_headers
);
if
(
$
this
-
>
sort_order
==
'
DESC
'
)
$
a_msg_headers
=
array_reverse
(
$
a_msg_headers
);
}
return
array_values
(
$
a_msg_headers
);
}
/**
* Public method for listing a specific set of headers
* convert mailbox name with root dir first
*
* @param string Mailbox/folder name
* @param array List of message ids to list
* @param int Current page to list
* @param string Header field to sort by
* @param string Sort order [ASC|DESC]
* @return array Indexed array with message header objects
* @access public
*/
function
list_header_set
(
$
mbox_name
=
''
,
$
msgs
,
$
page
=
NULL
,
$
sort_field
=
NULL
,
$
sort_order
=
NULL
)
{
$
mailbox
=
$
mbox_name
?
$
this
-
>
_mod_mailbox
(
$
mbox_name
)
:
$
this
-
>
mailbox
;
return
$
this
-
>
_list_header_set
(
$
mailbox
,
$
msgs
,
$
page
,
$
sort_field
,
$
sort_order
);
}
/**
* Private method for listing a set of message headers
*
* @access private
* @see rcube_imap::list_header_set()
*/
function
_list_header_set
(
$
mailbox
,
$
msgs
,
$
page
=
NULL
,
$
sort_field
=
NULL
,
$
sort_order
=
NULL
)
{
// also accept a comma-separated list of message ids
if
(
is_string
(
$
msgs
))
$
msgs
=
split
(
'
,
'
,
$
msgs
);
if
(
!
strlen
(
$
mailbox
)
||
empty
(
$
msgs
))
return
array
();
$
this
-
>
_set_sort_order
(
$
sort_field
,
$
sort_order
);
$
max
=
count
(
$
msgs
);
$
start_msg
=
(
$
this
-
>
list_page
-
1
)
*
$
this
-
>
page_size
;
// fetch reuested headers from server
$
a_msg_headers
=
array
();
$
this
-
>
_fetch_headers
(
$
mailbox
,
join
(
'
,
'
,
$
msgs
),
$
a_msg_headers
,
NULL
);
// return empty array if no messages found
if
(
!
is_array
(
$
a_msg_headers
)
||
empty
(
$
a_msg_headers
))
return
array
();
// if not already sorted
$
a_msg_headers
=
iil_SortHeaders
(
$
a_msg_headers
,
$
this
-
>
sort_field
,
$
this
-
>
sort_order
);
// only return the requested part of the set
return
array_slice
(
array_values
(
$
a_msg_headers
),
$
start_msg
,
min
(
$
max
-
$
start_msg
,
$
this
-
>
page_size
));
}
/**
* Helper function to get first and last index of the requested set
*
* @param int message count
* @param mixed page number to show, or string 'all'
* @return array array with two values: first index, last index
* @access private
*/
function
_get_message_range
(
$
max
,
$
page
)
{
$
start_msg
=
(
$
this
-
>
list_page
-
1
)
*
$
this
-
>
page_size
;
if
(
$
page
==
'
all
'
)
{
$
begin
=
0
;
$
end
=
$
max
;
}
else
if
(
$
this
-
>
sort_order
==
'
DESC
'
)
{
$
begin
=
$
max
-
$
this
-
>
page_size
-
$
start_msg
;
$
end
=
$
max
-
$
start_msg
;
}
else
{
$
begin
=
$
start_msg
;
$
end
=
$
start_msg
+
$
this
-
>
page_size
;
}
if
(
$
begin
<
0
)
$
begin
=
0
;
if
(
$
end
<
0
)
$
end
=
$
max
;
if
(
$
end
>
$
max
)
$
end
=
$
max
;
return
array
(
$
begin
,
$
end
);
}
/**
* Fetches message headers
* Used for loop
*
* @param string Mailbox name
* @param string Message index to fetch
* @param array Reference to message headers array
* @param array Array with cache index
* @return int Number of deleted messages
* @access private
*/
function
_fetch_headers
(
$
mailbox
,
$
msgs
,
&$
a_msg_headers
,
$
cache_key
)
{
// cache is incomplete
$
cache_index
=
$
this
-
>
get_message_cache_index
(
$
cache_key
);
// fetch reuested headers from server
$
a_header_index
=
iil_C_FetchHeaders
(
$
this
-
>
conn
,
$
mailbox
,
$
msgs
);
$
deleted_count
=
0
;
if
(
!
empty
(
$
a_header_index
))
{
foreach
(
$
a_header_index
as
$
i
=
>
$
headers
)
{
if
(
$
headers
-
>
deleted
&&
$
this
-
>
skip_deleted
)
{
// delete from cache
if
(
$
cache_index
[
$
headers
-
>
id
]
&&
$
cache_index
[
$
headers
-
>
id
]
==
$
headers
-
>
uid
)
$
this
-
>
remove_message_cache
(
$
cache_key
,
$
headers
-
>
id
);
$
deleted_count
++
;
continue
;
}
// add message to cache
if
(
$
this
-
>
caching_enabled
&&
$
cache_index
[
$
headers
-
>
id
]
!
=
$
headers
-
>
uid
)
$
this
-
>
add_message_cache
(
$
cache_key
,
$
headers
-
>
id
,
$
headers
);
$
a_msg_headers
[
$
headers
-
>
uid
]
=
$
headers
;
}
}
return
$
deleted_count
;
}
/**
* Return sorted array of message UIDs
*
* @param string Mailbox to get index from
* @param string Sort column
* @param string Sort order [ASC, DESC]
* @return array Indexed array with message ids
*/
function
message_index
(
$
mbox_name
=
''
,
$
sort_field
=
NULL
,
$
sort_order
=
NULL
)
{
$
this
-
>
_set_sort_order
(
$
sort_field
,
$
sort_order
);
$
mailbox
=
$
mbox_name
?
$
this
-
>
_mod_mailbox
(
$
mbox_name
)
:
$
this
-
>
mailbox
;
$
key
=
"{$mailbox}:{$this->sort_field}:{$this->sort_order}:{$this->search_string}.msgi"
;
// we have a saved search result. get index from there
if
(
!
isset
(
$
this
-
>
cache
[
$
key
])
&&
$
this
-
>
search_string
&&
$
mailbox
==
$
this
-
>
mailbox
)
{
$
this
-
>
cache
[
$
key
]
=
$
a_msg_headers
=
array
();
$
this
-
>
_fetch_headers
(
$
mailbox
,
join
(
'
,
'
,
$
this
-
>
search_set
),
$
a_msg_headers
,
NULL
);
foreach
(
iil_SortHeaders
(
$
a_msg_headers
,
$
this
-
>
sort_field
,
$
this
-
>
sort_order
)
as
$
i
=
>
$
msg
)
$
this
-
>
cache
[
$
key
][]
=
$
msg
-
>
uid
;
}
// have stored it in RAM
if
(
isset
(
$
this
-
>
cache
[
$
key
]))
return
$
this
-
>
cache
[
$
key
];
// check local cache
$
cache_key
=
$
mailbox
.'.
msg
'
;
$
cache_status
=
$
this
-
>
check_cache_status
(
$
mailbox
,
$
cache_key
);
// cache is OK
if
(
$
cache_status
>
0
)
{
$
a_index
=
$
this
-
>
get_message_cache_index
(
$
cache_key
,
TRUE
,
$
this
-
>
sort_field
,
$
this
-
>
sort_order
);
return
array_values
(
$
a_index
);
}
// fetch complete message index
$
msg_count
=
$
this
-
>
_messagecount
(
$
mailbox
);
if
(
$
this
-
>
get_capability
(
'
sort
'
)
&&
(
$
a_index
=
iil_C_Sort
(
$
this
-
>
conn
,
$
mailbox
,
$
this
-
>
sort_field
,
''
,
TRUE
)))
{
if
(
$
this
-
>
sort_order
==
'
DESC
'
)
$
a_index
=
array_reverse
(
$
a_index
);
$
this
-
>
cache
[
$
key
]
=
$
a_index
;
}
else
{
$
a_index
=
iil_C_FetchHeaderIndex
(
$
this
-
>
conn
,
$
mailbox
,
"1:$msg_count"
,
$
this
-
>
sort_field
);
$
a_uids
=
iil_C_FetchUIDs
(
$
this
-
>
conn
,
$
mailbox
);
if
(
$
this
-
>
sort_order
==
"ASC"
)
asort
(
$
a_index
);
else
if
(
$
this
-
>
sort_order
==
"DESC"
)
arsort
(
$
a_index
);
$
i
=
0
;
$
this
-
>
cache
[
$
key
]
=
array
();
foreach
(
$
a_index
as
$
index
=
>
$
value
)
$
this
-
>
cache
[
$
key
][
$
i
++
]
=
$
a_uids
[
$
index
];
}
return
$
this
-
>
cache
[
$
key
];
}
/**
* @access private
*/
function
sync_header_index
(
$
mailbox
)
{
$
cache_key
=
$
mailbox
.'.
msg
'
;
$
cache_index
=
$
this
-
>
get_message_cache_index
(
$
cache_key
);
$
msg_count
=
$
this
-
>
_messagecount
(
$
mailbox
);
// fetch complete message index
$
a_message_index
=
iil_C_FetchHeaderIndex
(
$
this
-
>
conn
,
$
mailbox
,
"1:$msg_count"
,
'
UID
'
);
foreach
(
$
a_message_index
as
$
id
=
>
$
uid
)
{
// message in cache at correct position
if
(
$
cache_index
[
$
id
]
==
$
uid
)
{
unset
(
$
cache_index
[
$
id
]);
continue
;
}
// message in cache but in wrong position
if
(
in_array
((
string
)
$
uid
,
$
cache_index
,
TRUE
))
{
unset
(
$
cache_index
[
$
id
]);
}
// other message at this position
if
(
isset
(
$
cache_index
[
$
id
]))
{
$
this
-
>
remove_message_cache
(
$
cache_key
,
$
id
);
unset
(
$
cache_index
[
$
id
]);
}
// fetch complete headers and add to cache
$
headers
=
iil_C_FetchHeader
(
$
this
-
>
conn
,
$
mailbox
,
$
id
);
$
this
-
>
add_message_cache
(
$
cache_key
,
$
headers
-
>
id
,
$
headers
);
}
// those ids that are still in cache_index have been deleted
if
(
!
empty
(
$
cache_index
))
{
foreach
(
$
cache_index
as
$
id
=
>
$
uid
)
$
this
-
>
remove_message_cache
(
$
cache_key
,
$
id
);
}
}
/**
* Invoke search request to IMAP server
*
* @param string mailbox name to search in
* @param string search criteria (ALL, TO, FROM, SUBJECT, etc)
* @param string search string
* @return array search results as list of message ids
* @access public
*/
function
search
(
$
mbox_name
=
''
,
$
criteria
=
'
ALL
'
,
$
str
=
NULL
,
$
charset
=
NULL
)
{
$
mailbox
=
$
mbox_name
?
$
this
-
>
_mod_mailbox
(
$
mbox_name
)
:
$
this
-
>
mailbox
;
// have an array of criterias => execute multiple searches
if
(
is_array
(
$
criteria
)
&&
$
str
)
{
$
results
=
array
();
foreach
(
$
criteria
as
$
crit
)
if
(
$
search_result
=
$
this
-
>
search
(
$
mbox_name
,
$
crit
,
$
str
,
$
charset
))
$
results
=
array_merge
(
$
results
,
$
search_result
);
$
results
=
array_unique
(
$
results
);
$
this
-
>
set_search_set
(
$
criteria
,
$
str
,
$
results
,
$
charset
);
return
$
results
;
}
else
if
(
$
str
&&
$
criteria
)
{
$
search
=
(
!
empty
(
$
charset
)
?
"CHARSET $charset "
:
''
)
.
sprintf
(
"%s {%d}\r\n%s"
,
$
criteria
,
strlen
(
$
str
),
$
str
);
$
results
=
$
this
-
>
_search_index
(
$
mailbox
,
$
search
);
// try search with ISO charset (should be supported by server)
if
(
empty
(
$
results
)
&&
!
empty
(
$
charset
)
&&
$
charset
!
=
'
ISO
-
8859
-
1
'
)
$
results
=
$
this
-
>
search
(
$
mbox_name
,
$
criteria
,
rcube_charset_convert
(
$
str
,
$
charset
,
'
ISO
-
8859
-
1
'
),
'
ISO
-
8859
-
1
'
);
$
this
-
>
set_search_set
(
$
criteria
,
$
str
,
$
results
,
$
charset
);
return
$
results
;
}
else
return
$
this
-
>
_search_index
(
$
mailbox
,
$
criteria
);
}
/**
* Private search method
*
* @return array search results as list of message ids
* @access private
* @see rcube_imap::search()
*/
function
_search_index
(
$
mailbox
,
$
criteria
=
'
ALL
'
)
{
$
a_messages
=
iil_C_Search
(
$
this
-
>
conn
,
$
mailbox
,
$
criteria
);
// clean message list (there might be some empty entries)
if
(
is_array
(
$
a_messages
))
{
foreach
(
$
a_messages
as
$
i
=
>
$
val
)
if
(
empty
(
$
val
))
unset
(
$
a_messages
[
$
i
]);
}
return
$
a_messages
;
}
/**
* Refresh saved search set
*
* @return array Current search set
*/
function
refresh_search
()
{
if
(
!
empty
(
$
this
-
>
search_subject
)
&&
!
empty
(
$
this
-
>
search_string
))
$
this
-
>
search_set
=
$
this
-
>
search
(
''
,
$
this
-
>
search_subject
,
$
this
-
>
search_string
,
$
this
-
>
search_charset
);
return
$
this
-
>
get_search_set
();
}
/**
* Check if the given message ID is part of the current search set
*
* @return boolean True on match or if no search request is stored
*/
function
in_searchset
(
$
msgid
)
{
if
(
!
empty
(
$
this
-
>
search_string
))
return
in_array
(
"$msgid"
,
(
array
)
$
this
-
>
search_set
,
true
);
else
return
true
;
}
/**
* Return message headers object of a specific message
*
* @param int Message ID
* @param string Mailbox to read from
* @param boolean True if $id is the message UID
* @return object Message headers representation
*/
function
get_headers
(
$
id
,
$
mbox_name
=
NULL
,
$
is_uid
=
TRUE
)
{
$
mailbox
=
$
mbox_name
?
$
this
-
>
_mod_mailbox
(
$
mbox_name
)
:
$
this
-
>
mailbox
;
$
uid
=
$
is_uid
?
$
id
:
$
this
-
>
_id2uid
(
$
id
);
// get cached headers
if
(
$
uid
&&
(
$
headers
=
&$
this
-
>
get_cached_message
(
$
mailbox
.'.
msg
'
,
$
uid
)))
return
$
headers
;
$
headers
=
iil_C_FetchHeader
(
$
this
-
>
conn
,
$
mailbox
,
$
id
,
$
is_uid
);
// write headers cache
if
(
$
headers
)
{
if
(
$
headers
-
>
uid
&&
$
headers
-
>
id
)
$
this
-
>
uid_id_map
[
$
mailbox
][
$
headers
-
>
uid
]
=
$
headers
-
>
id
;
$
this
-
>
add_message_cache
(
$
mailbox
.'.
msg
'
,
$
headers
-
>
id
,
$
headers
);
}
return
$
headers
;
}
/**
* Fetch body structure from the IMAP server and build
* an object structure similar to the one generated by PEAR::Mail_mimeDecode
*
* @param int Message UID to fetch
* @return object stdClass Message part tree or False on failure
*/
function
&
get_structure
(
$
uid
)
{
$
cache_key
=
$
this
-
>
mailbox
.'.
msg
'
;
$
headers
=
&$
this
-
>
get_cached_message
(
$
cache_key
,
$
uid
,
true
);
// return cached message structure
if
(
is_object
(
$
headers
)
&&
is_object
(
$
headers
-
>
structure
))
return
$
headers
-
>
structure
;
// resolve message sequence number
if
(
!
(
$
msg_id
=
$
this
-
>
_uid2id
(
$
uid
)))
return
FALSE
;
$
structure_str
=
iil_C_FetchStructureString
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
$
msg_id
);
$
structure
=
iml_GetRawStructureArray
(
$
structure_str
);
$
struct
=
false
;
// parse structure and add headers
if
(
!
empty
(
$
structure
))
{
$
this
-
>
_msg_id
=
$
msg_id
;
$
headers
=
$
this
-
>
get_headers
(
$
uid
);
$
struct
=
&$
this
-
>
_structure_part
(
$
structure
);
$
struct
-
>
headers
=
get_object_vars
(
$
headers
);
// don't trust given content-type
if
(
empty
(
$
struct
-
>
parts
)
&&
!
empty
(
$
struct
-
>
headers
[
'
ctype
'
]))
{
$
struct
-
>
mime_id
=
'
1
'
;
$
struct
-
>
mimetype
=
strtolower
(
$
struct
-
>
headers
[
'
ctype
'
]);
list
(
$
struct
-
>
ctype_primary
,
$
struct
-
>
ctype_secondary
)
=
explode
(
'
/
'
,
$
struct
-
>
mimetype
);
}
// write structure to cache
if
(
$
this
-
>
caching_enabled
)
$
this
-
>
add_message_cache
(
$
cache_key
,
$
msg_id
,
$
headers
,
$
struct
);
}
return
$
struct
;
}
/**
* Build message part object
*
* @access private
*/
function
&
_structure_part
(
$
part
,
$
count
=
0
,
$
parent
=
''
)
{
$
struct
=
new
rcube_message_part
;
$
struct
-
>
mime_id
=
empty
(
$
parent
)
?
(
string
)
$
count
:
"$parent.$count"
;
// multipart
if
(
is_array
(
$
part
[
0
]))
{
$
struct
-
>
ctype_primary
=
'
multipart
'
;
// find first non-array entry
for
(
$
i
=
1
;
count
(
$
part
);
$
i
++
)
if
(
!
is_array
(
$
part
[
$
i
]))
{
$
struct
-
>
ctype_secondary
=
strtolower
(
$
part
[
$
i
]);
break
;
}
$
struct
-
>
mimetype
=
'
multipart
/
'.$
struct
-
>
ctype_secondary
;
$
struct
-
>
parts
=
array
();
for
(
$
i
=
0
,
$
count
=
0
;
$
i
<
count
(
$
part
);
$
i
++
)
if
(
is_array
(
$
part
[
$
i
])
&&
count
(
$
part
[
$
i
])
>
5
)
$
struct
-
>
parts
[]
=
$
this
-
>
_structure_part
(
$
part
[
$
i
],
++
$
count
,
$
struct
-
>
mime_id
);
return
$
struct
;
}
// regular part
$
struct
-
>
ctype_primary
=
strtolower
(
$
part
[
0
]);
$
struct
-
>
ctype_secondary
=
strtolower
(
$
part
[
1
]);
$
struct
-
>
mimetype
=
$
struct
-
>
ctype_primary
.'
/
'.$
struct
-
>
ctype_secondary
;
// read content type parameters
if
(
is_array
(
$
part
[
2
]))
{
$
struct
-
>
ctype_parameters
=
array
();
for
(
$
i
=
0
;
$
i
<
count
(
$
part
[
2
]);
$
i
+=
2
)
$
struct
-
>
ctype_parameters
[
strtolower
(
$
part
[
2
][
$
i
])]
=
$
part
[
2
][
$
i
+
1
];
if
(
isset
(
$
struct
-
>
ctype_parameters
[
'
charset
'
]))
$
struct
-
>
charset
=
$
struct
-
>
ctype_parameters
[
'
charset
'
];
}
// read content encoding
if
(
!
empty
(
$
part
[
5
])
&&
$
part
[
5
]
!
=
'
NIL
'
)
{
$
struct
-
>
encoding
=
strtolower
(
$
part
[
5
]);
$
struct
-
>
headers
[
'
content
-
transfer
-
encoding
'
]
=
$
struct
-
>
encoding
;
}
// get part size
if
(
!
empty
(
$
part
[
6
])
&&
$
part
[
6
]
!
=
'
NIL
'
)
$
struct
-
>
size
=
intval
(
$
part
[
6
]);
// read part disposition
$
di
=
count
(
$
part
)
-
2
;
if
((
is_array
(
$
part
[
$
di
])
&&
count
(
$
part
[
$
di
])
==
2
&&
is_array
(
$
part
[
$
di
][
1
]))
||
(
is_array
(
$
part
[
--
$
di
])
&&
count
(
$
part
[
$
di
])
==
2
))
{
$
struct
-
>
disposition
=
strtolower
(
$
part
[
$
di
][
0
]);
if
(
is_array
(
$
part
[
$
di
][
1
]))
for
(
$
n
=
0
;
$
n
<
count
(
$
part
[
$
di
][
1
]);
$
n
+=
2
)
$
struct
-
>
d_parameters
[
strtolower
(
$
part
[
$
di
][
1
][
$
n
])]
=
$
part
[
$
di
][
1
][
$
n
+
1
];
}
// get child parts
if
(
is_array
(
$
part
[
8
])
&&
$
di
!
=
8
)
{
$
struct
-
>
parts
=
array
();
for
(
$
i
=
0
,
$
count
=
0
;
$
i
<
count
(
$
part
[
8
]);
$
i
++
)
if
(
is_array
(
$
part
[
8
][
$
i
])
&&
count
(
$
part
[
8
][
$
i
])
>
5
)
$
struct
-
>
parts
[]
=
$
this
-
>
_structure_part
(
$
part
[
8
][
$
i
],
++
$
count
,
$
struct
-
>
mime_id
);
}
// get part ID
if
(
!
empty
(
$
part
[
3
])
&&
$
part
[
3
]
!
=
'
NIL
'
)
{
$
struct
-
>
content_id
=
$
part
[
3
];
$
struct
-
>
headers
[
'
content
-
id
'
]
=
$
part
[
3
];
if
(
empty
(
$
struct
-
>
disposition
))
$
struct
-
>
disposition
=
'
inline
'
;
}
// fetch message headers if message/rfc822
if
(
$
struct
-
>
ctype_primary
==
'
message
'
)
{
$
headers
=
iil_C_FetchPartBody
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
$
this
-
>
_msg_id
,
$
struct
-
>
mime_id
.'.
HEADER
'
);
$
struct
-
>
headers
=
$
this
-
>
_parse_headers
(
$
headers
);
if
(
is_array
(
$
part
[
8
])
&&
empty
(
$
struct
-
>
parts
))
$
struct
-
>
parts
[]
=
$
this
-
>
_structure_part
(
$
part
[
8
],
++
$
count
,
$
struct
-
>
mime_id
);
}
// normalize filename property
if
(
$
filename_mime
=
$
struct
-
>
d_parameters
[
'
filename
'
]
?
$
struct
-
>
d_parameters
[
'
filename
'
]
:
$
struct
-
>
ctype_parameters
[
'
name
'
])
$
struct
-
>
filename
=
rcube_imap
::
decode_mime_string
(
$
filename_mime
,
$
this
-
>
default_charset
);
else
if
(
$
filename_encoded
=
$
struct
-
>
d_parameters
[
'
filename
*
'
]
?
$
struct
-
>
d_parameters
[
'
filename
*
'
]
:
$
struct
-
>
ctype_parameters
[
'
name
*
'
])
{
// decode filename according to RFC 2231, Section 4
list
(
$
filename_charset
,,
$
filename_urlencoded
)
=
split
(
'\''
,
$
filename_encoded
);
$
struct
-
>
filename
=
rcube_charset_convert
(
urldecode
(
$
filename_urlencoded
),
$
filename_charset
);
}
else
if
(
!
empty
(
$
struct
-
>
headers
[
'
content
-
description
'
]))
$
struct
-
>
filename
=
rcube_imap
::
decode_mime_string
(
$
struct
-
>
headers
[
'
content
-
description
'
],
$
this
-
>
default_charset
);
return
$
struct
;
}
/**
* Return a flat array with references to all parts, indexed by part numbers
*
* @param object rcube_message_part Message body structure
* @return Array with part number -> object pairs
*/
function
get_mime_numbers
(
&$
structure
)
{
$
a_parts
=
array
();
$
this
-
>
_get_part_numbers
(
$
structure
,
$
a_parts
);
return
$
a_parts
;
}
/**
* Helper method for recursive calls
*
* @access private
*/
function
_get_part_numbers
(
&$
part
,
&$
a_parts
)
{
if
(
$
part
-
>
mime_id
)
$
a_parts
[
$
part
-
>
mime_id
]
=
&$
part
;
if
(
is_array
(
$
part
-
>
parts
))
for
(
$
i
=
0
;
$
i
<
count
(
$
part
-
>
parts
);
$
i
++
)
$
this
-
>
_get_part_numbers
(
$
part
-
>
parts
[
$
i
],
$
a_parts
);
}
/**
* Fetch message body of a specific message from the server
*
* @param int Message UID
* @param string Part number
* @param object rcube_message_part Part object created by get_structure()
* @param mixed True to print part, ressource to write part contents in
* @return string Message/part body if not printed
*/
function
&
get_message_part
(
$
uid
,
$
part
=
1
,
$
o_part
=
NULL
,
$
print
=
NULL
)
{
if
(
!
(
$
msg_id
=
$
this
-
>
_uid2id
(
$
uid
)))
return
FALSE
;
// get part encoding if not provided
if
(
!
is_object
(
$
o_part
))
{
$
structure_str
=
iil_C_FetchStructureString
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
$
msg_id
);
$
structure
=
iml_GetRawStructureArray
(
$
structure_str
);
$
part_type
=
iml_GetPartTypeCode
(
$
structure
,
$
part
);
$
o_part
=
new
rcube_message_part
;
$
o_part
-
>
ctype_primary
=
$
part_type
==
0
?
'
text
'
:
(
$
part_type
==
2
?
'
message
'
:
'
other
'
);
$
o_part
-
>
encoding
=
strtolower
(
iml_GetPartEncodingString
(
$
structure
,
$
part
));
$
o_part
-
>
charset
=
iml_GetPartCharset
(
$
structure
,
$
part
);
}
// TODO: Add caching for message parts
if
(
$
print
)
{
$
mode
=
$
o_part
-
>
encoding
==
'
base64
'
?
3
:
(
$
o_part
-
>
encoding
==
'
quoted
-
printable
'
?
1
:
2
);
$
body
=
iil_C_HandlePartBody
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
$
msg_id
,
$
part
,
$
mode
);
// we have to decode the part manually before printing
if
(
$
mode
==
1
)
{
echo
$
this
-
>
mime_decode
(
$
body
,
$
o_part
-
>
encoding
);
$
body
=
true
;
}
}
else
{
$
body
=
iil_C_HandlePartBody
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
$
msg_id
,
$
part
,
1
);
// decode part body
if
(
$
o_part
-
>
encoding
)
$
body
=
$
this
-
>
mime_decode
(
$
body
,
$
o_part
-
>
encoding
);
// convert charset (if text or message part)
if
(
$
o_part
-
>
ctype_primary
==
'
text
'
||
$
o_part
-
>
ctype_primary
==
'
message
'
)
{
// assume default if no charset specified
if
(
empty
(
$
o_part
-
>
charset
))
$
o_part
-
>
charset
=
$
this
-
>
default_charset
;
$
body
=
rcube_charset_convert
(
$
body
,
$
o_part
-
>
charset
);
}
}
return
$
body
;
}
/**
* Fetch message body of a specific message from the server
*
* @param int Message UID
* @return string Message/part body
* @see rcube_imap::get_message_part()
*/
function
&
get_body
(
$
uid
,
$
part
=
1
)
{
return
$
this
-
>
get_message_part
(
$
uid
,
$
part
);
}
/**
* Returns the whole message source as string
*
* @param int Message UID
* @return string Message source string
*/
function
&
get_raw_body
(
$
uid
)
{
if
(
!
(
$
msg_id
=
$
this
-
>
_uid2id
(
$
uid
)))
return
FALSE
;
$
body
=
iil_C_FetchPartHeader
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
$
msg_id
,
NULL
);
$
body
.
=
iil_C_HandlePartBody
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
$
msg_id
,
NULL
,
1
);
return
$
body
;
}
/**
* Sends the whole message source to stdout
*
* @param int Message UID
*/
function
print_raw_body
(
$
uid
)
{
if
(
!
(
$
msg_id
=
$
this
-
>
_uid2id
(
$
uid
)))
return
FALSE
;
print
iil_C_FetchPartHeader
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
$
msg_id
,
NULL
);
flush
();
iil_C_HandlePartBody
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
$
msg_id
,
NULL
,
2
);
}
/**
* Set message flag to one or several messages
*
* @param mixed Message UIDs as array or as comma-separated string
* @param string Flag to set: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT, MDNSENT
* @return boolean True on success, False on failure
*/
function
set_flag
(
$
uids
,
$
flag
)
{
$
flag
=
strtoupper
(
$
flag
);
$
msg_ids
=
array
();
if
(
!
is_array
(
$
uids
))
$
uids
=
explode
(
'
,
'
,
$
uids
);
foreach
(
$
uids
as
$
uid
)
{
$
msg_ids
[
$
uid
]
=
$
this
-
>
_uid2id
(
$
uid
);
}
if
(
$
flag
==
'
UNDELETED
'
)
$
result
=
iil_C_Undelete
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
join
(
'
,
'
,
array_values
(
$
msg_ids
)));
else
if
(
$
flag
==
'
UNSEEN
'
)
$
result
=
iil_C_Unseen
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
join
(
'
,
'
,
array_values
(
$
msg_ids
)));
else
$
result
=
iil_C_Flag
(
$
this
-
>
conn
,
$
this
-
>
mailbox
,
join
(
'
,
'
,
array_values
(
$
msg_ids
)),
$
flag
);
// reload message headers if cached
$
cache_key
=
$
this
-
>
mailbox
.'.
msg
'
;
if
(
$
this
-
>
caching_enabled
)
{
foreach
(
$
msg_ids
as
$
uid
=
>
$
id
)
{
if
(
$
cached_headers
=
$
this
-
>
get_cached_message
(
$
cache_key
,
$
uid
))
{
$
this
-
>
remove_message_cache
(
$
cache_key
,
$
id
);
//$this->get_headers($uid);
}
}
// close and re-open connection
// this prevents connection problems with Courier
$
this
-
>
reconnect
();
}
// set nr of messages that were flaged
$
count
=
count
(
$
msg_ids
);
// clear message count cache
if
(
$
result
&&
$
flag
==
'
SEEN
'
)
$
this
-
>
_set_messagecount
(
$
this
-
>
mailbox
,
'
UNSEEN
'
,
$
count
*
(
-
1
));
else
if
(
$
result
&&
$
flag
==
'
UNSEEN
'
)
$
this
-
>
_set_messagecount
(
$
this
-
>
mailbox
,
'
UNSEEN
'
,
$
count
);
else
if
(
$
result
&&
$
flag
==
'
DELETED
'
)
$
this
-
>
_set_messagecount
(
$
this
-
>
mailbox
,
'
ALL
'
,
$
count
*
(
-
1
));
return
$
result
;
}
/**
* Append a mail message (source) to a specific mailbox
*
* @param string Target mailbox
* @param string Message source
* @return boolean True on success, False on error
*/
function
save_message
(
$
mbox_name
,
&$
message
)
{
$
mbox_name
=
stripslashes
(
$
mbox_name
);
$
mailbox
=
$
this
-
>
_mod_mailbox
(
$
mbox_name
);
// make sure mailbox exists
if
(
in_array
(
$
mailbox
,
$
this
-
>
_list_mailboxes
()))
$
saved
=
iil_C_Append
(
$
this
-
>
conn
,
$
mailbox
,
$
message
);
if
(
$
saved
)
{
// increase messagecount of the target mailbox
$
this
-
>
_set_messagecount
(
$
mailbox
,
'
ALL
'
,
1
);
}
return
$
saved
;
}
/**
* Move a message from one mailbox to another
*
* @param string List of UIDs to move, separated by comma
* @param string Target mailbox
* @param string Source mailbox
* @return boolean True on success, False on error
*/
function
move_message
(
$
uids
,
$
to_mbox
,
$
from_mbox
=
''
)
{
$
to_mbox
=
stripslashes
(
$
to_mbox
);
$
from_mbox
=
stripslashes
(
$
from_mbox
);
$
to_mbox
=
$
this
-
>
_mod_mailbox
(
$
to_mbox
);
$
from_mbox
=
$
from_mbox
?
$
this
-
>
_mod_mailbox
(
$
from_mbox
)
:
$
this
-
>
mailbox
;
// make sure mailbox exists
if
(
!
in_array
(
$
to_mbox
,
$
this
-
>
_list_mailboxes
()))
{
if
(
in_array
(
$
to_mbox
,
$
this
-
>
default_folders
))
$
this
-
>
create_mailbox
(
$
to_mbox
,
TRUE
);
else
return
FALSE
;
}
// convert the list of uids to array
$
a_uids
=
is_string
(
$
uids
)
?
explode
(
'
,
'
,
$
uids
)
:
(
is_array
(
$
uids
)
?
$
uids
:
NULL
);
// exit if no message uids are specified
if
(
!
is_array
(
$
a_uids
))
return
false
;
// convert uids to message ids
$
a_mids
=
array
();
foreach
(
$
a_uids
as
$
uid
)
$
a_mids
[]
=
$
this
-
>
_uid2id
(
$
uid
,
$
from_mbox
);
$
iil_move
=
iil_C_Move
(
$
this
-
>
conn
,
join
(
'
,
'
,
$
a_mids
),
$
from_mbox
,
$
to_mbox
);
$
moved
=
!
(
$
iil_move
===
false
||
$
iil_move
<
0
);
// send expunge command in order to have the moved message
// really deleted from the source mailbox
if
(
$
moved
)
{
$
this
-
>
_expunge
(
$
from_mbox
,
FALSE
);
$
this
-
>
_clear_messagecount
(
$
from_mbox
);
$
this
-
>
_clear_messagecount
(
$
to_mbox
);
}
// remove message ids from search set
if
(
$
moved
&&
$
this
-
>
search_set
&&
$
from_mbox
==
$
this
-
>
mailbox
)
$
this
-
>
search_set
=
array_diff
(
$
this
-
>
search_set
,
$
a_mids
);
// update cached message headers
$
cache_key
=
$
from_mbox
.'.
msg
'
;
if
(
$
moved
&&
(
$
a_cache_index
=
$
this
-
>
get_message_cache_index
(
$
cache_key
)))
{
$
start_index
=
100000
;
foreach
(
$
a_uids
as
$
uid
)
{
if
((
$
index
=
array_search
(
$
uid
,
$
a_cache_index
))
!
==
FALSE
)
$
start_index
=
min
(
$
index
,
$
start_index
);
}
// clear cache from the lowest index on
$
this
-
>
clear_message_cache
(
$
cache_key
,
$
start_index
);
}
return
$
moved
;
}
/**
* Mark messages as deleted and expunge mailbox
*
* @param string List of UIDs to move, separated by comma
* @param string Source mailbox
* @return boolean True on success, False on error
*/
function
delete_message
(
$
uids
,
$
mbox_name
=
''
)
{
$
mbox_name
=
stripslashes
(
$
mbox_name
);
$
mailbox
=
$
mbox_name
?
$
this
-
>
_mod_mailbox
(
$
mbox_name
)
:
$
this
-
>
mailbox
;
// convert the list of uids to array
$
a_uids
=
is_string
(
$
uids
)
?
explode
(
'
,
'
,
$
uids
)
:
(
is_array
(
$
uids
)
?
$
uids
:
NULL
);
// exit if no message uids are specified
if
(
!
is_array
(
$
a_uids
))
return
false
;
// convert uids to message ids
$
a_mids
=
array
();
foreach
(
$
a_uids
as
$
uid
)
$
a_mids
[]
=
$
this
-
>
_uid2id
(
$
uid
,
$
mailbox
);
$
deleted
=
iil_C_Delete
(
$
this
-
>
conn
,
$
mailbox
,
join
(
'
,
'
,
$
a_mids
));
// send expunge command in order to have the deleted message
// really deleted from the mailbox
if
(
$
deleted
)
{
$
this
-
>
_expunge
(
$
mailbox
,
FALSE
);
$
this
-
>
_clear_messagecount
(
$
mailbox
);
}
// remove message ids from search set
if
(
$
moved
&&
$
this
-
>
search_set
&&
$
mailbox
==
$
this
-
>
mailbox
)
$
this
-
>
search_set
=
array_diff
(
$
this
-
>
search_set
,
$
a_mids
);
// remove deleted messages from cache
$
cache_key
=
$
mailbox
.'.
msg
'
;
if
(
$
deleted
&&
(
$
a_cache_index
=
$
this
-
>
get_message_cache_index
(
$
cache_key
)))
{
$
start_index
=
100000
;
foreach
(
$
a_uids
as
$
uid
)
{
if
((
$
index
=
array_search
(
$
uid
,
$
a_cache_index
))
!
==
FALSE
)
$
start_index
=
min
(
$
index
,
$
start_index
);
}
// clear cache from the lowest index on
$
this
-
>
clear_message_cache
(
$
cache_key
,
$
start_index
);
}
return
$
deleted
;
}
/**
* Clear all messages in a specific mailbox
*
* @param string Mailbox name
* @return int Above 0 on success
*/
function
clear_mailbox
(
$
mbox_name
=
NULL
)
{
$
mbox_name
=
stripslashes
(
$
mbox_name
);
$
mailbox
=
!
empty
(
$
mbox_name
)
?
$
this
-
>
_mod_mailbox
(
$
mbox_name
)
:
$
this
-
>
mailbox
;
$
msg_count
=
$
this
-
>
_messagecount
(
$
mailbox
,
'
ALL
'
);
if
(
$
msg_count
>
0
)
{
$
cleared
=
iil_C_ClearFolder
(
$
this
-
>
conn
,
$
mailbox
);
// make sure the message count cache is cleared as well
if
(
$
cleared
)
{
$
this
-
>
clear_message_cache
(
$
mailbox
.'.
msg
'
);
$
a_mailbox_cache
=
$
this
-
>
get_cache
(
'
messagecount
'
);
unset
(
$
a_mailbox_cache
[
$
mailbox
]);
$
this
-
>
update_cache
(
'
messagecount
'
,
$
a_mailbox_cache
);
}
return
$
cleared
;
}
else
return
0
;
}
/**
* Send IMAP expunge command and clear cache
*
* @param string Mailbox name
* @param boolean False if cache should not be cleared
* @return boolean True on success
*/
function
expunge
(
$
mbox_name
=
''
,
$
clear_cache
=
TRUE
)
{
$
mbox_name
=
stripslashes
(
$
mbox_name
);
$
mailbox
=
$
mbox_name
?
$
this
-
>
_mod_mailbox
(
$
mbox_name
)
:
$
this
-
>
mailbox
;
return
$
this
-
>
_expunge
(
$
mailbox
,
$
clear_cache
);
}
/**
* Send IMAP expunge command and clear cache
*
* @see rcube_imap::expunge()
* @access private
*/
function
_expunge
(
$
mailbox
,
$
clear_cache
=
TRUE
)
{
$
result
=
iil_C_Expunge
(
$
this
-
>
conn
,
$
mailbox
);
if
(
$
result
>
=
0
&&
$
clear_cache
)
{
$
this
-
>
clear_message_cache
(
$
mailbox
.'.
msg
'
);
$
this
-
>
_clear_messagecount
(
$
mailbox
);
}
return
$
result
;
}
/* --------------------------------
* folder managment
* --------------------------------*/
/**
* Get a list of all folders available on the IMAP server
*
* @param string IMAP root dir
* @return array Indexed array with folder names
*/
function
list_unsubscribed
(
$
root
=
''
)
{
static
$
sa_unsubscribed
;
if
(
is_array
(
$
sa_unsubscribed
))
return
$
sa_unsubscribed
;
// retrieve list of folders from IMAP server
$
a_mboxes
=
iil_C_ListMailboxes
(
$
this
-
>
conn
,
$
this
-
>
_mod_mailbox
(
$
root
),
'
*
'
);
// modify names with root dir
foreach
(
$
a_mboxes
as
$
mbox_name
)
{
$
name
=
$
this
-
>
_mod_mailbox
(
$
mbox_name
,
'
out
'
);
if
(
strlen
(
$
name
))
$
a_folders
[]
=
$
name
;
}
// filter folders and sort them
$
sa_unsubscribed
=
$
this
-
>
_sort_mailbox_list
(
$
a_folders
);
return
$
sa_unsubscribed
;
}
/**
* Get mailbox quota information
* added by Nuny
*
* @return mixed Quota info or False if not supported
*/
function
get_quota
()
{
if
(
$
this
-
>
get_capability
(
'
QUOTA
'
))
return
iil_C_GetQuota
(
$
this
-
>
conn
);
return
FALSE
;
}
/**
* Subscribe to a specific mailbox(es)
*
* @param string Mailbox name(s)
* @return boolean True on success
*/
function
subscribe
(
$
mbox_name
)
{
if
(
is_array
(
$
mbox_name
))
$
a_mboxes
=
$
mbox_name
;
else
if
(
is_string
(
$
mbox_name
)
&&
strlen
(
$
mbox_name
))
$
a_mboxes
=
explode
(
'
,
'
,
$
mbox_name
);
// let this common function do the main work
return
$
this
-
>
_change_subscription
(
$
a_mboxes
,
'
subscribe
'
);
}
/**
* Unsubscribe mailboxes
*
* @param string Mailbox name(s)
* @return boolean True on success
*/
function
unsubscribe
(
$
mbox_name
)
{
if
(
is_array
(
$
mbox_name
))
$
a_mboxes
=
$
mbox_name
;
else
if
(
is_string
(
$
mbox_name
)
&&
strlen
(
$
mbox_name
))
$
a_mboxes
=
(
array
)
$
mbox_name
;
// let this common function do the main work
return
$
this
-
>
_change_subscription
(
$
a_mboxes
,
'
unsubscribe
'
);
}
/**
* Create a new mailbox on the server and register it in local cache
*
* @param string New mailbox name (as utf-7 string)
* @param boolean True if the new mailbox should be subscribed
* @param string Name of the created mailbox, false on error
*/
function
create_mailbox
(
$
name
,
$
subscribe
=
FALSE
)
{
$
result
=
FALSE
;
// replace backslashes
$
name
=
preg_replace
(
'
/
[
\\\
]
+/
'
,
'
-
'
,
$
name
);
// reduce mailbox name to 100 chars
$
name
=
substr
(
$
name
,
0
,
100
);
$
abs_name
=
$
this
-
>
_mod_mailbox
(
$
name
);
$
a_mailbox_cache
=
$
this
-
>
get_cache
(
'
mailboxes
'
);
if
(
strlen
(
$
abs_name
)
&&
(
!
is_array
(
$
a_mailbox_cache
)
||
!
in_array
(
$
abs_name
,
$
a_mailbox_cache
)))
$
result
=
iil_C_CreateFolder
(
$
this
-
>
conn
,
$
abs_name
);
// try to subscribe it
if
(
$
result
&&
$
subscribe
)
$
this
-
>
subscribe
(
$
name
);
return
$
result
?
$
name
:
FALSE
;
}
/**
* Set a new name to an existing mailbox
*
* @param string Mailbox to rename (as utf-7 string)
* @param string New mailbox name (as utf-7 string)
* @return string Name of the renames mailbox, False on error
*/
function
rename_mailbox
(
$
mbox_name
,
$
new_name
)
{
$
result
=
FALSE
;
// replace backslashes
$
name
=
preg_replace
(
'
/
[
\\\
]
+/
'
,
'
-
'
,
$
new_name
);
// encode mailbox name and reduce it to 100 chars
$
name
=
substr
(
$
new_name
,
0
,
100
);
// make absolute path
$
mailbox
=
$
this
-
>
_mod_mailbox
(
$
mbox_name
);
$
abs_name
=
$
this
-
>
_mod_mailbox
(
$
name
);
// check if mailbox is subscribed
$
a_subscribed
=
$
this
-
>
_list_mailboxes
();
$
subscribed
=
in_array
(
$
mailbox
,
$
a_subscribed
);
// unsubscribe folder
if
(
$
subscribed
)
iil_C_UnSubscribe
(
$
this
-
>
conn
,
$
mailbox
);
if
(
strlen
(
$
abs_name
))
$
result
=
iil_C_RenameFolder
(
$
this
-
>
conn
,
$
mailbox
,
$
abs_name
);
if
(
$
result
)
{
$
delm
=
$
this
-
>
get_hierarchy_delimiter
();
// check if mailbox children are subscribed
foreach
(
$
a_subscribed
as
$
c_subscribed
)
if
(
preg_match
(
'
/
^'.
preg_quote
(
$
mailbox
.$
delm
,
'
/
'
)
.'
/
'
,
$
c_subscribed
))
{
iil_C_UnSubscribe
(
$
this
-
>
conn
,
$
c_subscribed
);
iil_C_Subscribe
(
$
this
-
>
conn
,
preg_replace
(
'
/
^'.
preg_quote
(
$
mailbox
,
'
/
'
)
.'
/
'
,
$
abs_name
,
$
c_subscribed
));
}
// clear cache
$
this
-
>
clear_message_cache
(
$
mailbox
.'.
msg
'
);
$
this
-
>
clear_cache
(
'
mailboxes
'
);
}
// try to subscribe it
if
(
$
result
&&
$
subscribed
)
iil_C_Subscribe
(
$
this
-
>
conn
,
$
abs_name
);
return
$
result
?
$
name
:
FALSE
;
}
/**
* Remove mailboxes from server
*
* @param string Mailbox name
* @return boolean True on success
*/
function
delete_mailbox
(
$
mbox_name
)
{
$
deleted
=
FALSE
;
if
(
is_array
(
$
mbox_name
))
$
a_mboxes
=
$
mbox_name
;
else
if
(
is_string
(
$
mbox_name
)
&&
strlen
(
$
mbox_name
))
$
a_mboxes
=
explode
(
'
,
'
,
$
mbox_name
);
$
all_mboxes
=
iil_C_ListMailboxes
(
$
this
-
>
conn
,
$
this
-
>
_mod_mailbox
(
$
root
),
'
*
'
);
if
(
is_array
(
$
a_mboxes
))
foreach
(
$
a_mboxes
as
$
mbox_name
)
{
$
mailbox
=
$
this
-
>
_mod_mailbox
(
$
mbox_name
);
// unsubscribe mailbox before deleting
iil_C_UnSubscribe
(
$
this
-
>
conn
,
$
mailbox
);
// send delete command to server
$
result
=
iil_C_DeleteFolder
(
$
this
-
>
conn
,
$
mailbox
);
if
(
$
result
>
=
0
)
$
deleted
=
TRUE
;
foreach
(
$
all_mboxes
as
$
c_mbox
)
{
$
regex
=
preg_quote
(
$
mailbox
.
$
this
-
>
delimiter
,
'
/
'
);
$
regex
=
'
/
^'
.
$
regex
.
'
/
'
;
if
(
preg_match
(
$
regex
,
$
c_mbox
))
{
iil_C_UnSubscribe
(
$
this
-
>
conn
,
$
c_mbox
);
$
result
=
iil_C_DeleteFolder
(
$
this
-
>
conn
,
$
c_mbox
);
if
(
$
result
>
=
0
)
$
deleted
=
TRUE
;
}
}
}
// clear mailboxlist cache
if
(
$
deleted
)
{
$
this
-
>
clear_message_cache
(
$
mailbox
.'.
msg
'
);
$
this
-
>
clear_cache
(
'
mailboxes
'
);
}
return
$
deleted
;
}
/**
* Create all folders specified as default
*/
function
create_default_folders
()
{
$
a_folders
=
iil_C_ListMailboxes
(
$
this
-
>
conn
,
$
this
-
>
_mod_mailbox
(
''
),
'
*
'
);
$
a_subscribed
=
iil_C_ListSubscribed
(
$
this
-
>
conn
,
$
this
-
>
_mod_mailbox
(
''
),
'
*
'
);
// create default folders if they do not exist
foreach
(
$
this
-
>
default_folders
as
$
folder
)
{
$
abs_name
=
$
this
-
>
_mod_mailbox
(
$
folder
);
if
(
!
in_array_nocase
(
$
abs_name
,
$
a_folders
))
$
this
-
>
create_mailbox
(
$
folder
,
TRUE
);
else
if
(
!
in_array_nocase
(
$
abs_name
,
$
a_subscribed
))
$
this
-
>
subscribe
(
$
folder
);
}
}
/* --------------------------------
* internal caching methods
* --------------------------------*/
/**
* @access private
*/
function
set_caching
(
$
set
)
{
if
(
$
set
&&
is_object
(
$
this
-
>
db
))
$
this
-
>
caching_enabled
=
TRUE
;
else
$
this
-
>
caching_enabled
=
FALSE
;
}
/**
* @access private
*/
function
get_cache
(
$
key
)
{
// read cache
if
(
!
isset
(
$
this
-
>
cache
[
$
key
])
&&
$
this
-
>
caching_enabled
)
{
$
cache_data
=
$
this
-
>
_read_cache_record
(
'
IMAP
.'.$
key
);
$
this
-
>
cache
[
$
key
]
=
strlen
(
$
cache_data
)
?
unserialize
(
$
cache_data
)
:
FALSE
;
}
return
$
this
-
>
cache
[
$
key
];
}
/**
* @access private
*/
function
update_cache
(
$
key
,
$
data
)
{
$
this
-
>
cache
[
$
key
]
=
$
data
;
$
this
-
>
cache_changed
=
TRUE
;
$
this
-
>
cache_changes
[
$
key
]
=
TRUE
;
}
/**
* @access private
*/
function
write_cache
()
{
if
(
$
this
-
>
caching_enabled
&&
$
this
-
>
cache_changed
)
{
foreach
(
$
this
-
>
cache
as
$
key
=
>
$
data
)
{
if
(
$
this
-
>
cache_changes
[
$
key
])
$
this
-
>
_write_cache_record
(
'
IMAP
.'.$
key
,
serialize
(
$
data
));
}
}
}
/**
* @access private
*/
function
clear_cache
(
$
key
=
NULL
)
{
if
(
$
key
===
NULL
)
{
foreach
(
$
this
-
>
cache
as
$
key
=
>
$
data
)
$
this
-
>
_clear_cache_record
(
'
IMAP
.'.$
key
);
$
this
-
>
cache
=
array
();
$
this
-
>
cache_changed
=
FALSE
;
$
this
-
>
cache_changes
=
array
();
}
else
{
$
this
-
>
_clear_cache_record
(
'
IMAP
.'.$
key
);
$
this
-
>
cache_changes
[
$
key
]
=
FALSE
;
unset
(
$
this
-
>
cache
[
$
key
]);
}
}
/**
* @access private
*/
function
_read_cache_record
(
$
key
)
{
$
cache_data
=
FALSE
;
if
(
$
this
-
>
db
)
{
// get cached data from DB
$
sql_result
=
$
this
-
>
db
-
>
query
(
"SELECT cache_id, data
FROM "
.
get_table_name
(
'
cache
'
)
.
"
WHERE user_id=?
AND cache_key=?"
,
$
_SESSION
[
'
user_id
'
],
$
key
);
if
(
$
sql_arr
=
$
this
-
>
db
-
>
fetch_assoc
(
$
sql_result
))
{
$
cache_data
=
$
sql_arr
[
'
data
'
];
$
this
-
>
cache_keys
[
$
key
]
=
$
sql_arr
[
'
cache_id
'
];
}
}
return
$
cache_data
;
}
/**
* @access private
*/
function
_write_cache_record
(
$
key
,
$
data
)
{
if
(
!$
this
-
>
db
)
return
FALSE
;
// check if we already have a cache entry for this key
if
(
!
isset
(
$
this
-
>
cache_keys
[
$
key
]))
{
$
sql_result
=
$
this
-
>
db
-
>
query
(
"SELECT cache_id
FROM "
.
get_table_name
(
'
cache
'
)
.
"
WHERE user_id=?
AND cache_key=?"
,
$
_SESSION
[
'
user_id
'
],
$
key
);
if
(
$
sql_arr
=
$
this
-
>
db
-
>
fetch_assoc
(
$
sql_result
))
$
this
-
>
cache_keys
[
$
key
]
=
$
sql_arr
[
'
cache_id
'
];
else
$
this
-
>
cache_keys
[
$
key
]
=
FALSE
;
}
// update existing cache record
if
(
$
this
-
>
cache_keys
[
$
key
])
{
$
this
-
>
db
-
>
query
(
"UPDATE "
.
get_table_name
(
'
cache
'
)
.
"
SET created="
.$
this
-
>
db
-
>
now
()
.
",
data=?
WHERE user_id=?
AND cache_key=?"
,
$
data
,
$
_SESSION
[
'
user_id
'
],
$
key
);
}
// add new cache record
else
{
$
this
-
>
db
-
>
query
(
"INSERT INTO "
.
get_table_name
(
'
cache
'
)
.
"
(created, user_id, cache_key, data)
VALUES ("
.$
this
-
>
db
-
>
now
()
.
", ?, ?, ?)"
,
$
_SESSION
[
'
user_id
'
],
$
key
,
$
data
);
}
}
/**
* @access private
*/
function
_clear_cache_record
(
$
key
)
{
$
this
-
>
db
-
>
query
(
"DELETE FROM "
.
get_table_name
(
'
cache
'
)
.
"
WHERE user_id=?
AND cache_key=?"
,
$
_SESSION
[
'
user_id
'
],
$
key
);
}
/* --------------------------------
* message caching methods
* --------------------------------*/
/**
* Checks if the cache is up-to-date
*
* @param string Mailbox name
* @param string Internal cache key
* @return int -3 = off, -2 = incomplete, -1 = dirty
*/
function
check_cache_status
(
$
mailbox
,
$
cache_key
)
{
if
(
!$
this
-
>
caching_enabled
)
return
-
3
;
$
cache_index
=
$
this
-
>
get_message_cache_index
(
$
cache_key
,
TRUE
);
$
msg_count
=
$
this
-
>
_messagecount
(
$
mailbox
);
$
cache_count
=
count
(
$
cache_index
);
// console("Cache check: $msg_count !== ".count($cache_index));
if
(
$
cache_count
==
$
msg_count
)
{
// get highest index
$
header
=
iil_C_FetchHeader
(
$
this
-
>
conn
,
$
mailbox
,
"$msg_count"
);
$
cache_uid
=
array_pop
(
$
cache_index
);
// uids of highest message matches -> cache seems OK
if
(
$
cache_uid
==
$
header
-
>
uid
)
return
1
;
// cache is dirty
return
-
1
;
}
// if cache count differs less than 10% report as dirty
else
if
(
abs
(
$
msg_count
-
$
cache_count
)
<
$
msg_count
/
10
)
return
-
1
;
else
return
-
2
;
}
/**
* @access private
*/
function
get_message_cache
(
$
key
,
$
from
,
$
to
,
$
sort_field
,
$
sort_order
)
{
$
cache_key
=
"$key:$from:$to:$sort_field:$sort_order"
;
$
db_header_fields
=
array
(
'
idx
'
,
'
uid
'
,
'
subject
'
,
'
from
'
,
'
to
'
,
'
cc
'
,
'
date
'
,
'
size
'
);
if
(
!
in_array
(
$
sort_field
,
$
db_header_fields
))
$
sort_field
=
'
idx
'
;
if
(
$
this
-
>
caching_enabled
&&
!
isset
(
$
this
-
>
cache
[
$
cache_key
]))
{
$
this
-
>
cache
[
$
cache_key
]
=
array
();
$
sql_result
=
$
this
-
>
db
-
>
limitquery
(
"SELECT idx, uid, headers
FROM "
.
get_table_name
(
'
messages
'
)
.
"
WHERE user_id=?
AND cache_key=?
ORDER BY "
.$
this
-
>
db
-
>
quoteIdentifier
(
$
sort_field
)
.
" "
.
strtoupper
(
$
sort_order
),
$
from
,
$
to
-
$
from
,
$
_SESSION
[
'
user_id
'
],
$
key
);
while
(
$
sql_arr
=
$
this
-
>
db
-
>
fetch_assoc
(
$
sql_result
))
{
$
uid
=
$
sql_arr
[
'
uid
'
];
$
this
-
>
cache
[
$
cache_key
][
$
uid
]
=
unserialize
(
$
sql_arr
[
'
headers
'
]);
// featch headers if unserialize failed
if
(
empty
(
$
this
-
>
cache
[
$
cache_key
][
$
uid
]))
$
this
-
>
cache
[
$
cache_key
][
$
uid
]
=
iil_C_FetchHeader
(
$
this
-
>
conn
,
preg_replace
(
'
/
.
msg
$
/
'
,
''
,
$
key
),
$
uid
,
true
);
}
}
return
$
this
-
>
cache
[
$
cache_key
];
}
/**
* @access private
*/
function
&
get_cached_message
(
$
key
,
$
uid
,
$
struct
=
false
)
{
$
internal_key
=
'
__single_msg
'
;
if
(
$
this
-
>
caching_enabled
&&
(
!
isset
(
$
this
-
>
cache
[
$
internal_key
][
$
uid
])
||
(
$
struct
&&
empty
(
$
this
-
>
cache
[
$
internal_key
][
$
uid
]
-
>
structure
))))
{
$
sql_select
=
"idx, uid, headers"
.
(
$
struct
?
", structure"
:
''
);
$
sql_result
=
$
this
-
>
db
-
>
query
(
"SELECT $sql_select
FROM "
.
get_table_name
(
'
messages
'
)
.
"
WHERE user_id=?
AND cache_key=?
AND uid=?"
,
$
_SESSION
[
'
user_id
'
],
$
key
,
$
uid
);
if
(
$
sql_arr
=
$
this
-
>
db
-
>
fetch_assoc
(
$
sql_result
))
{
$
this
-
>
cache
[
$
internal_key
][
$
uid
]
=
unserialize
(
$
sql_arr
[
'
headers
'
]);
if
(
is_object
(
$
this
-
>
cache
[
$
internal_key
][
$
uid
])
&&
!
empty
(
$
sql_arr
[
'
structure
'
]))
$
this
-
>
cache
[
$
internal_key
][
$
uid
]
-
>
structure
=
unserialize
(
$
sql_arr
[
'
structure
'
]);
}
}
return
$
this
-
>
cache
[
$
internal_key
][
$
uid
];
}
/**
* @access private
*/
function
get_message_cache_index
(
$
key
,
$
force
=
FALSE
,
$
sort_col
=
'
idx
'
,
$
sort_order
=
'
ASC
'
)
{
static
$
sa_message_index
=
array
();
// empty key -> empty array
if
(
!$
this
-
>
caching_enabled
||
empty
(
$
key
))
return
array
();
if
(
!
empty
(
$
sa_message_index
[
$
key
])
&&
!$
force
)
return
$
sa_message_index
[
$
key
];
$
sa_message_index
[
$
key
]
=
array
();
$
sql_result
=
$
this
-
>
db
-
>
query
(
"SELECT idx, uid
FROM "
.
get_table_name
(
'
messages
'
)
.
"
WHERE user_id=?
AND cache_key=?
ORDER BY "
.$
this
-
>
db
-
>
quote_identifier
(
$
sort_col
)
.
" "
.$
sort_order
,
$
_SESSION
[
'
user_id
'
],
$
key
);
while
(
$
sql_arr
=
$
this
-
>
db
-
>
fetch_assoc
(
$
sql_result
))
$
sa_message_index
[
$
key
][
$
sql_arr
[
'
idx
'
]]
=
$
sql_arr
[
'
uid
'
];
return
$
sa_message_index
[
$
key
];
}
/**
* @access private
*/
function
add_message_cache
(
$
key
,
$
index
,
$
headers
,
$
struct
=
null
)
{
if
(
empty
(
$
key
)
||
!
is_object
(
$
headers
)
||
empty
(
$
headers
-
>
uid
))
return
;
// add to internal (fast) cache
$
this
-
>
cache
[
'
__single_msg
'
][
$
headers
-
>
uid
]
=
$
headers
;
$
this
-
>
cache
[
'
__single_msg
'
][
$
headers
-
>
uid
]
-
>
structure
=
$
struct
;
// no further caching
if
(
!$
this
-
>
caching_enabled
)
return
;
// check for an existing record (probly headers are cached but structure not)
$
sql_result
=
$
this
-
>
db
-
>
query
(
"SELECT message_id
FROM "
.
get_table_name
(
'
messages
'
)
.
"
WHERE user_id=?
AND cache_key=?
AND uid=?
AND del<>1"
,
$
_SESSION
[
'
user_id
'
],
$
key
,
$
headers
-
>
uid
);
// update cache record
if
(
$
sql_arr
=
$
this
-
>
db
-
>
fetch_assoc
(
$
sql_result
))
{
$
this
-
>
db
-
>
query
(
"UPDATE "
.
get_table_name
(
'
messages
'
)
.
"
SET idx=?, headers=?, structure=?
WHERE message_id=?"
,
$
index
,
serialize
(
$
headers
),
is_object
(
$
struct
)
?
serialize
(
$
struct
)
:
NULL
,
$
sql_arr
[
'
message_id
'
]
);
}
else
// insert new record
{
$
this
-
>
db
-
>
query
(
"INSERT INTO "
.
get_table_name
(
'
messages
'
)
.
"
(user_id, del, cache_key, created, idx, uid, subject, "
.$
this
-
>
db
-
>
quoteIdentifier
(
'
from
'
)
.
", "
.$
this
-
>
db
-
>
quoteIdentifier
(
'
to
'
)
.
", cc, date, size, headers, structure)
VALUES (?, 0, ?, "
.$
this
-
>
db
-
>
now
()
.
", ?, ?, ?, ?, ?, ?, "
.$
this
-
>
db
-
>
fromunixtime
(
$
headers
-
>
timestamp
)
.
", ?, ?, ?)"
,
$
_SESSION
[
'
user_id
'
],
$
key
,
$
index
,
$
headers
-
>
uid
,
(
string
)
substr
(
$
this
-
>
decode_header
(
$
headers
-
>
subject
,
TRUE
),
0
,
128
),
(
string
)
substr
(
$
this
-
>
decode_header
(
$
headers
-
>
from
,
TRUE
),
0
,
128
),
(
string
)
substr
(
$
this
-
>
decode_header
(
$
headers
-
>
to
,
TRUE
),
0
,
128
),
(
string
)
substr
(
$
this
-
>
decode_header
(
$
headers
-
>
cc
,
TRUE
),
0
,
128
),
(
int
)
$
headers
-
>
size
,
serialize
(
$
headers
),
is_object
(
$
struct
)
?
serialize
(
$
struct
)
:
NULL
);
}
}
/**
* @access private
*/
function
remove_message_cache
(
$
key
,
$
index
)
{
$
this
-
>
db
-
>
query
(
"DELETE FROM "
.
get_table_name
(
'
messages
'
)
.
"
WHERE user_id=?
AND cache_key=?
AND idx=?"
,
$
_SESSION
[
'
user_id
'
],
$
key
,
$
index
);
}
/**
* @access private
*/
function
clear_message_cache
(
$
key
,
$
start_index
=
1
)
{
$
this
-
>
db
-
>
query
(
"DELETE FROM "
.
get_table_name
(
'
messages
'
)
.
"
WHERE user_id=?
AND cache_key=?
AND idx>=?"
,
$
_SESSION
[
'
user_id
'
],
$
key
,
$
start_index
);
}
/* --------------------------------
* encoding/decoding methods
* --------------------------------*/
/**
* Split an address list into a structured array list
*
* @param string Input string
* @param int List only this number of addresses
* @param boolean Decode address strings
* @return array Indexed list of addresses
*/
function
decode_address_list
(
$
input
,
$
max
=
null
,
$
decode
=
true
)
{
$
a
=
$
this
-
>
_parse_address_list
(
$
input
,
$
decode
);
$
out
=
array
();
if
(
!
is_array
(
$
a
))
return
$
out
;
$
c
=
count
(
$
a
);
$
j
=
0
;
foreach
(
$
a
as
$
val
)
{
$
j
++
;
$
address
=
$
val
[
'
address
'
];
$
name
=
preg_replace
(
array
(
'
/
^
[
\'
"]/', '/[\'"
]
$
/
'
),
''
,
trim
(
$
val
[
'
name
'
]));
if
(
$
name
&&
$
address
&&
$
name
!
=
$
address
)
$
string
=
sprintf
(
'%
s
<
%
s
>
'
,
preg_match
(
'
/
[,;<>]
/
'
,
$
name
)
?
'
"'.addcslashes($name, '"
'
)
.'
"' : $name, $address);
else if ($address)
$string = $address;
else if ($name)
$string = $name;
$out[$j] = array('name' => $name,
'mailto' => $address,
'string' => $string);
if ($max && $j==$max)
break;
}
return $out;
}
/**
* Decode a message header value
*
* @param string Header value
* @param boolean Remove quotes if necessary
* @return string Decoded string
*/
function decode_header($input, $remove_quotes=FALSE)
{
$str = rcube_imap::decode_mime_string((string)$input, $this->default_charset);
if ($str{0}=='"
'
&&
$
remove_quotes
)
$
str
=
str_replace
(
'
"', '', $str);
return $str;
}
/**
* Decode a mime-encoded string to internal charset
*
* @param string Header value
* @param string Fallback charset if none specified
* @return string Decoded string
* @static
*/
function decode_mime_string($input, $fallback=null)
{
$out = '';
$pos = strpos($input, '=?');
if ($pos !== false)
{
// rfc: all line breaks or other characters not found in the Base64 Alphabet must be ignored by decoding software
// delete all blanks between MIME-lines, differently we can receive unnecessary blanks and broken utf-8 symbols
$input = preg_replace("
/
\?
=
\
s
+=
\?
/
", '?==?', $input);
$out = substr($input, 0, $pos);
$end_cs_pos = strpos($input, "
?
", $pos+2);
$end_en_pos = strpos($input, "
?
", $end_cs_pos+1);
$end_pos = strpos($input, "
?
=
", $end_en_pos+1);
$encstr = substr($input, $pos+2, ($end_pos-$pos-2));
$rest = substr($input, $end_pos+2);
$out .= rcube_imap::_decode_mime_string_part($encstr);
$out .= rcube_imap::decode_mime_string($rest, $fallback);
return $out;
}
// no encoding information, use fallback
return rcube_charset_convert($input, !empty($fallback) ? $fallback : 'ISO-8859-1');
}
/**
* Decode a part of a mime-encoded string
*
* @access private
*/
function _decode_mime_string_part($str)
{
$a = explode('?', $str);
$count = count($a);
// should be in format "
charset
?
encoding
?
base64_string
"
if ($count >= 3)
{
for ($i=2; $i<$count; $i++)
$rest.=$a[$i];
if (($a[1]=="
B
")||($a[1]=="
b
"))
$rest = base64_decode($rest);
else if (($a[1]=="
Q
")||($a[1]=="
q
"))
{
$rest = str_replace("
_
", "
", $rest);
$rest = quoted_printable_decode($rest);
}
return rcube_charset_convert($rest, $a[0]);
}
else
return $str; // we dont' know what to do with this
}
/**
* Decode a mime part
*
* @param string Input string
* @param string Part encoding
* @return string Decoded string
* @access private
*/
function mime_decode($input, $encoding='7bit')
{
switch (strtolower($encoding))
{
case '7bit':
return $input;
break;
case 'quoted-printable':
return quoted_printable_decode($input);
break;
case 'base64':
return base64_decode($input);
break;
default:
return $input;
}
}
/**
* Convert body charset to UTF-8 according to the ctype_parameters
*
* @param string Part body to decode
* @param string Charset to convert from
* @return string Content converted to internal charset
*/
function charset_decode($body, $ctype_param)
{
if (is_array($ctype_param) && !empty($ctype_param['charset']))
return rcube_charset_convert($body, $ctype_param['charset']);
// defaults to what is specified in the class header
return rcube_charset_convert($body, $this->default_charset);
}
/**
* Translate UID to message ID
*
* @param int Message UID
* @param string Mailbox name
* @return int Message ID
*/
function get_id($uid, $mbox_name=NULL)
{
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
return $this->_uid2id($uid, $mailbox);
}
/**
* Translate message number to UID
*
* @param int Message ID
* @param string Mailbox name
* @return int Message UID
*/
function get_uid($id,$mbox_name=NULL)
{
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
return $this->_id2uid($id, $mailbox);
}
/* --------------------------------
* private methods
* --------------------------------*/
/**
* @access private
*/
function _mod_mailbox($mbox_name, $mode='in')
{
if ((!empty($this->root_ns) && $this->root_ns == $mbox_name) || $mbox_name == 'INBOX')
return $mbox_name;
if (!empty($this->root_dir) && $mode=='in')
$mbox_name = $this->root_dir.$this->delimiter.$mbox_name;
else if (strlen($this->root_dir) && $mode=='out')
$mbox_name = substr($mbox_name, strlen($this->root_dir)+1);
return $mbox_name;
}
/**
* Validate the given input and save to local properties
* @access private
*/
function _set_sort_order($sort_field, $sort_order)
{
if ($sort_field != null)
$this->sort_field = asciiwords($sort_field);
if ($sort_order != null)
$this->sort_order = strtoupper($sort_order) == 'DESC' ? 'DESC' : 'ASC';
}
/**
* Sort mailboxes first by default folders and then in alphabethical order
* @access private
*/
function _sort_mailbox_list($a_folders)
{
$a_out = $a_defaults = array();
// find default folders and skip folders starting with '.'
foreach($a_folders as $i => $folder)
{
if ($folder{0}=='.')
continue;
if (($p = array_search(strtolower($folder), $this->default_folders_lc)) !== false && !$a_defaults[$p])
$a_defaults[$p] = $folder;
else
$a_out[] = $folder;
}
natcasesort($a_out);
ksort($a_defaults);
return array_merge($a_defaults, $a_out);
}
/**
* @access private
*/
function _uid2id($uid, $mbox_name=NULL)
{
if (!$mbox_name)
$mbox_name = $this->mailbox;
if (!isset($this->uid_id_map[$mbox_name][$uid]))
$this->uid_id_map[$mbox_name][$uid] = iil_C_UID2ID($this->conn, $mbox_name, $uid);
return $this->uid_id_map[$mbox_name][$uid];
}
/**
* @access private
*/
function _id2uid($id, $mbox_name=NULL)
{
if (!$mbox_name)
$mbox_name = $this->mailbox;
$index = array_flip((array)$this->uid_id_map[$mbox_name]);
if (isset($index[$id]))
$uid = $index[$id];
else
{
$uid = iil_C_ID2UID($this->conn, $mbox_name, $id);
$this->uid_id_map[$mbox_name][$uid] = $id;
}
return $uid;
}
/**
* Parse string or array of server capabilities and put them in internal array
* @access private
*/
function _parse_capability($caps)
{
if (!is_array($caps))
$cap_arr = explode(' ', $caps);
else
$cap_arr = $caps;
foreach ($cap_arr as $cap)
{
if ($cap=='CAPABILITY')
continue;
if (strpos($cap, '=')>0)
{
list($key, $value) = explode('=', $cap);
if (!is_array($this->capabilities[$key]))
$this->capabilities[$key] = array();
$this->capabilities[$key][] = $value;
}
else
$this->capabilities[$cap] = TRUE;
}
}
/**
* Subscribe/unsubscribe a list of mailboxes and update local cache
* @access private
*/
function _change_subscription($a_mboxes, $mode)
{
$updated = FALSE;
if (is_array($a_mboxes))
foreach ($a_mboxes as $i => $mbox_name)
{
$mailbox = $this->_mod_mailbox($mbox_name);
$a_mboxes[$i] = $mailbox;
if ($mode=='subscribe')
$result = iil_C_Subscribe($this->conn, $mailbox);
else if ($mode=='unsubscribe')
$result = iil_C_UnSubscribe($this->conn, $mailbox);
if ($result>=0)
$updated = TRUE;
}
// get cached mailbox list
if ($updated)
{
$a_mailbox_cache = $this->get_cache('mailboxes');
if (!is_array($a_mailbox_cache))
return $updated;
// modify cached list
if ($mode=='subscribe')
$a_mailbox_cache = array_merge($a_mailbox_cache, $a_mboxes);
else if ($mode=='unsubscribe')
$a_mailbox_cache = array_diff($a_mailbox_cache, $a_mboxes);
// write mailboxlist to cache
$this->update_cache('mailboxes', $this->_sort_mailbox_list($a_mailbox_cache));
}
return $updated;
}
/**
* Increde/decrese messagecount for a specific mailbox
* @access private
*/
function _set_messagecount($mbox_name, $mode, $increment)
{
$a_mailbox_cache = FALSE;
$mailbox = $mbox_name ? $mbox_name : $this->mailbox;
$mode = strtoupper($mode);
$a_mailbox_cache = $this->get_cache('messagecount');
if (!is_array($a_mailbox_cache[$mailbox]) || !isset($a_mailbox_cache[$mailbox][$mode]) || !is_numeric($increment))
return FALSE;
// add incremental value to messagecount
$a_mailbox_cache[$mailbox][$mode] += $increment;
// there's something wrong, delete from cache
if ($a_mailbox_cache[$mailbox][$mode] < 0)
unset($a_mailbox_cache[$mailbox][$mode]);
// write back to cache
$this->update_cache('messagecount', $a_mailbox_cache);
return TRUE;
}
/**
* Remove messagecount of a specific mailbox from cache
* @access private
*/
function _clear_messagecount($mbox_name='')
{
$a_mailbox_cache = FALSE;
$mailbox = $mbox_name ? $mbox_name : $this->mailbox;
$a_mailbox_cache = $this->get_cache('messagecount');
if (is_array($a_mailbox_cache[$mailbox]))
{
unset($a_mailbox_cache[$mailbox]);
$this->update_cache('messagecount', $a_mailbox_cache);
}
}
/**
* Split RFC822 header string into an associative array
* @access private
*/
function _parse_headers($headers)
{
$a_headers = array();
$lines = explode("
\
n
", $headers);
$c = count($lines);
for ($i=0; $i<$c; $i++)
{
if ($p = strpos($lines[$i], ': '))
{
$field = strtolower(substr($lines[$i], 0, $p));
$value = trim(substr($lines[$i], $p+1));
if (!empty($value))
$a_headers[$field] = $value;
}
}
return $a_headers;
}
/**
* @access private
*/
function _parse_address_list($str, $decode=true)
{
// remove any newlines and carriage returns before
$a = $this->_explode_quoted_string('[,;]', preg_replace( "
/
[
\
r
\
n
]
/
", "
", $str));
$result = array();
foreach ($a as $key => $val)
{
$val = preg_replace("
/
([
\
"\w])</"
,
"$1 <"
,
$
val
);
$
sub_a
=
$
this
-
>
_explode_quoted_string
(
'
'
,
$
decode
?
$
this
-
>
decode_header
(
$
val
)
:
$
val
);
$
result
[
$
key
][
'
name
'
]
=
''
;
foreach
(
$
sub_a
as
$
k
=
>
$
v
)
{
if
(
strpos
(
$
v
,
'@'
)
>
0
)
$
result
[
$
key
][
'
address
'
]
=
str_replace
(
'
<
'
,
''
,
str_replace
(
'
>
'
,
''
,
$
v
));
else
$
result
[
$
key
][
'
name
'
]
.
=
(
empty
(
$
result
[
$
key
][
'
name
'
])
?'':'
'
)
.
str_replace
(
"\""
,
''
,
stripslashes
(
$
v
));
}
if
(
empty
(
$
result
[
$
key
][
'
name
'
]))
$
result
[
$
key
][
'
name
'
]
=
$
result
[
$
key
][
'
address
'
];
}
return
$
result
;
}
/**
* @access private
*/
function
_explode_quoted_string
(
$
delimiter
,
$
string
)
{
$
result
=
array
();
$
strlen
=
strlen
(
$
string
);
for
(
$
q
=
$
p
=
$
i
=
0
;
$
i
<
$
strlen
;
$
i
++
)
{
if
(
$
string
{
$
i
}
==
"\""
&&
$
string
{
$
i
-
1
}
!
=
"\\"
)
$
q
=
$
q
?
false
:
true
;
else
if
(
!$
q
&&
preg_match
(
"/$delimiter/"
,
$
string
{
$
i
}))
{
$
result
[]
=
substr
(
$
string
,
$
p
,
$
i
-
$
p
);
$
p
=
$
i
+
1
;
}
}
$
result
[]
=
substr
(
$
string
,
$
p
);
return
$
result
;
}
}
// end class rcube_imap
/**
* Class representing a message part
*
* @package Mail
*/
class
rcube_message_part
{
var
$
mime_id
=
''
;
var
$
ctype_primary
=
'
text
'
;
var
$
ctype_secondary
=
'
plain
'
;
var
$
mimetype
=
'
text
/
plain
'
;
var
$
disposition
=
''
;
var
$
filename
=
''
;
var
$
encoding
=
'
8
bit
'
;
var
$
charset
=
''
;
var
$
size
=
0
;
var
$
headers
=
array
();
var
$
d_parameters
=
array
();
var
$
ctype_parameters
=
array
();
}
/**
* Class for sorting an array of iilBasicHeader objects in a predetermined order.
*
* @package Mail
* @author Eric Stadtherr
*/
class
rcube_header_sorter
{
var
$
sequence_numbers
=
array
();
/**
* Set the predetermined sort order.
*
* @param array Numerically indexed array of IMAP message sequence numbers
*/
function
set_sequence_numbers
(
$
seqnums
)
{
$
this
-
>
sequence_numbers
=
$
seqnums
;
}
/**
* Sort the array of header objects
*
* @param array Array of iilBasicHeader objects indexed by UID
*/
function
sort_headers
(
&$
headers
)
{
/*
* uksort would work if the keys were the sequence number, but unfortunately
* the keys are the UIDs. We'll use uasort instead and dereference the value
* to get the sequence number (in the "id" field).
*
* uksort($headers, array($this, "compare_seqnums"));
*/
uasort
(
$
headers
,
array
(
$
this
,
"compare_seqnums"
));
}
/**
* Get the position of a message sequence number in my sequence_numbers array
*
* @param int Message sequence number contained in sequence_numbers
* @return int Position, -1 if not found
*/
function
position_of
(
$
seqnum
)
{
$
c
=
count
(
$
this
-
>
sequence_numbers
);
for
(
$
pos
=
0
;
$
pos
<
=
$
c
;
$
pos
++
)
{
if
(
$
this
-
>
sequence_numbers
[
$
pos
]
==
$
seqnum
)
return
$
pos
;
}
return
-
1
;
}
/**
* Sort method called by uasort()
*/
function
compare_seqnums
(
$
a
,
$
b
)
{
// First get the sequence number from the header object (the 'id' field).
$
seqa
=
$
a
-
>
id
;
$
seqb
=
$
b
-
>
id
;
// then find each sequence number in my ordered list
$
posa
=
$
this
-
>
position_of
(
$
seqa
);
$
posb
=
$
this
-
>
position_of
(
$
seqb
);
// return the relative position as the comparison value
$
ret
=
$
posa
-
$
posb
;
return
$
ret
;
}
}
/**
* Add quoted-printable encoding to a given string
*
* @param string String to encode
* @param int Add new line after this number of characters
* @param boolean True if spaces should be converted into =20
* @return string Encoded string
*/
function
quoted_printable_encode
(
$
input
,
$
line_max
=
76
,
$
space_conv
=
false
)
{
$
hex
=
array
(
'
0
'
,
'
1
'
,
'
2
'
,
'
3
'
,
'
4
'
,
'
5
'
,
'
6
'
,
'
7
'
,
'
8
'
,
'
9
'
,
'
A
'
,
'
B
'
,
'
C
'
,
'
D
'
,
'
E
'
,
'
F
'
);
$
lines
=
preg_split
(
"/(?:\r\n|\r|\n)/"
,
$
input
);
$
eol
=
"\r\n"
;
$
escape
=
"="
;
$
output
=
""
;
while
(
list
(,
$
line
)
=
each
(
$
lines
))
{
//$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary
$
linlen
=
strlen
(
$
line
);
$
newline
=
""
;
for
(
$
i
=
0
;
$
i
<
$
linlen
;
$
i
++
)
{
$
c
=
substr
(
$
line
,
$
i
,
1
);
$
dec
=
ord
(
$
c
);
if
(
(
$
i
==
0
)
&&
(
$
dec
==
46
)
)
// convert first point in the line into =2E
{
$
c
=
"=2E"
;
}
if
(
$
dec
==
32
)
{
if
(
$
i
==
(
$
linlen
-
1
)
)
// convert space at eol only
{
$
c
=
"=20"
;
}
else
if
(
$
space_conv
)
{
$
c
=
"=20"
;
}
}
else
if
(
(
$
dec
==
61
)
||
(
$
dec
<
32
)
||
(
$
dec
>
126
)
)
// always encode "\t", which is *not* required
{
$
h2
=
floor
(
$
dec
/
16
);
$
h1
=
floor
(
$
dec
%
16
);
$
c
=
$
escape
.$
hex
[
"$h2"
]
.$
hex
[
"$h1"
];
}
if
(
(
strlen
(
$
newline
)
+
strlen
(
$
c
))
>
=
$
line_max
)
// CRLF is not counted
{
$
output
.
=
$
newline
.$
escape
.$
eol
;
// soft line break; " =\r\n" is okay
$
newline
=
""
;
// check if newline first character will be point or not
if
(
$
dec
==
46
)
{
$
c
=
"=2E"
;
}
}
$
newline
.
=
$
c
;
}
// end of for
$
output
.
=
$
newline
.$
eol
;
}
// end of while
return
trim
(
$
output
);
}
?
>
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Fri, Apr 24, 10:24 AM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18844031
Default Alt Text
rcube_imap.inc (78 KB)
Attached To
Mode
R113 roundcubemail
Attached
Detach File
Event Timeline