Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F1841267
kolab_sync_backend.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
29 KB
Referenced Files
None
Subscribers
None
kolab_sync_backend.php
View Options
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com> |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
class
kolab_sync_backend
{
/**
* Singleton instace of kolab_sync_backend
*
* @var kolab_sync_backend
*/
static
protected
$instance
;
protected
$storage
;
protected
$folder_meta
;
protected
$folder_uids
;
protected
$root_meta
;
static
protected
$types
=
array
(
1
=>
''
,
2
=>
'mail.inbox'
,
3
=>
'mail.drafts'
,
4
=>
'mail.wastebasket'
,
5
=>
'mail.sentitems'
,
6
=>
'mail.outbox'
,
7
=>
'task.default'
,
8
=>
'event.default'
,
9
=>
'contact.default'
,
10
=>
'note.default'
,
11
=>
'journal.default'
,
12
=>
'mail'
,
13
=>
'event'
,
14
=>
'contact'
,
15
=>
'task'
,
16
=>
'journal'
,
17
=>
'note'
,
);
static
protected
$classes
=
array
(
Syncroton_Data_Factory
::
CLASS_CALENDAR
=>
'event'
,
Syncroton_Data_Factory
::
CLASS_CONTACTS
=>
'contact'
,
Syncroton_Data_Factory
::
CLASS_EMAIL
=>
'mail'
,
Syncroton_Data_Factory
::
CLASS_TASKS
=>
'task'
,
);
const
ROOT_MAILBOX
=
'INBOX'
;
// const ROOT_MAILBOX = '';
const
ASYNC_KEY
=
'/private/vendor/kolab/activesync'
;
const
UID_KEY
=
'/shared/vendor/cmu/cyrus-imapd/uniqueid'
;
/**
* This implements the 'singleton' design pattern
*
* @return kolab_sync_backend The one and only instance
*/
static
function
get_instance
()
{
if
(!
self
::
$instance
)
{
self
::
$instance
=
new
kolab_sync_backend
;
self
::
$instance
->
startup
();
// init AFTER object was linked with self::$instance
}
return
self
::
$instance
;
}
/**
* Class initialization
*/
public
function
startup
()
{
$this
->
storage
=
rcube
::
get_instance
()->
get_storage
();
// @TODO: reset cache? if we do this for every request the cache would be useless
// There's no session here
//$this->storage->clear_cache('mailboxes.', true);
// set additional header used by libkolab
$this
->
storage
->
set_options
(
array
(
// @TODO: there can be Roundcube plugins defining additional headers,
// we maybe would need to add them here
'fetch_headers'
=>
'X-KOLAB-TYPE X-KOLAB-MIME-VERSION'
,
'skip_deleted'
=>
true
,
'threading'
=>
false
,
));
// Disable paging
$this
->
storage
->
set_pagesize
(
999999
);
}
/**
* List known devices
*
* @return array Device list as hash array
*/
public
function
devices_list
()
{
if
(
$this
->
root_meta
===
null
)
{
// @TODO: consider server annotation instead of INBOX
if
(
$meta
=
$this
->
storage
->
get_metadata
(
self
::
ROOT_MAILBOX
,
self
::
ASYNC_KEY
))
{
$this
->
root_meta
=
$this
->
unserialize_metadata
(
$meta
[
self
::
ROOT_MAILBOX
][
self
::
ASYNC_KEY
]);
}
else
{
$this
->
root_meta
=
array
();
}
}
if
(!
empty
(
$this
->
root_meta
[
'DEVICE'
])
&&
is_array
(
$this
->
root_meta
[
'DEVICE'
]))
{
return
$this
->
root_meta
[
'DEVICE'
];
}
return
array
();
}
/**
* Get list of folders available for sync
*
* @param string $deviceid Device identifier
* @param string $type Folder type
*
* @return array List of mailbox folders
*/
public
function
folders_list
(
$deviceid
,
$type
)
{
$folders_list
=
array
();
// get all folders of specified type
$folders
=
(
array
)
kolab_storage
::
list_folders
(
''
,
'*'
,
$type
,
false
,
$typedata
);
// get folders activesync config
$folderdata
=
$this
->
folder_meta
();
// check if folders are "subscribed" for activesync
foreach
(
$folderdata
as
$folder
=>
$meta
)
{
if
(
empty
(
$meta
[
'FOLDER'
])
||
empty
(
$meta
[
'FOLDER'
][
$deviceid
])
||
empty
(
$meta
[
'FOLDER'
][
$deviceid
][
'S'
])
)
{
continue
;
}
if
(!
empty
(
$type
)
&&
!
in_array
(
$folder
,
$folders
))
{
continue
;
}
// Activesync folder identifier (serverId)
$folder_id
=
self
::
folder_id
(
$folder
,
$typedata
[
$folder
]);
$folders_list
[
$folder_id
]
=
$this
->
folder_data
(
$folder
,
$typedata
[
$folder
]);
}
return
$folders_list
;
}
/**
* Getter for folder metadata
*
* @return array Hash array with meta data for each folder
*/
public
function
folder_meta
()
{
if
(!
isset
(
$this
->
folder_meta
))
{
$this
->
folder_meta
=
array
();
// get folders activesync config
$folderdata
=
$this
->
storage
->
get_metadata
(
"*"
,
self
::
ASYNC_KEY
);
foreach
(
$folderdata
as
$folder
=>
$meta
)
{
if
(
$asyncdata
=
$meta
[
self
::
ASYNC_KEY
])
{
if
(
$metadata
=
$this
->
unserialize_metadata
(
$asyncdata
))
{
$this
->
folder_meta
[
$folder
]
=
$metadata
;
}
}
}
}
return
$this
->
folder_meta
;
}
/**
* Creates folder and subscribes to the device
*
* @param string $name Folder name (UTF7-IMAP)
* @param int $type Folder (ActiveSync) type
* @param string $deviceid Device identifier
*
* @return bool True on success, False on failure
*/
public
function
folder_create
(
$name
,
$type
,
$deviceid
)
{
if
(
$this
->
storage
->
folder_exists
(
$name
))
{
$created
=
true
;
}
else
{
$type
=
self
::
type_activesync2kolab
(
$type
);
$created
=
kolab_storage
::
folder_create
(
$name
,
$kolab_type
,
true
);
}
if
(
$created
)
{
// Set ActiveSync subscription flag
$this
->
folder_set
(
$name
,
$deviceid
,
1
);
return
true
;
}
return
false
;
}
/**
* Renames a folder
*
* @param string $old_name Old folder name (UTF7-IMAP)
* @param string $new_name New folder name (UTF7-IMAP)
* @param int $type Folder (ActiveSync) type
* @param string $deviceid Device identifier
*
* @return bool True on success, False on failure
*/
public
function
folder_rename
(
$old_name
,
$new_name
,
$type
,
$deviceid
)
{
$type
=
self
::
type_activesync2kolab
(
$type
);
$moved
=
kolab_storage
::
folder_rename
(
$old_name
,
$new_name
);
if
(
$moved
)
{
// UnSet ActiveSync subscription flag
$this
->
folder_set
(
$old_name
,
$deviceid
,
0
);
// Set ActiveSync subscription flag
$this
->
folder_set
(
$new_name
,
$deviceid
,
1
);
return
true
;
}
return
false
;
}
/**
* Deletes folder
*
* @param string $name Folder name (UTF7-IMAP)
* @param string $deviceid Device identifier
*
*/
public
function
folder_delete
(
$name
,
$deviceid
)
{
unset
(
$this
->
folder_meta
[
$name
]);
return
kolab_storage
::
folder_delete
(
$name
);
}
/**
* Sets ActiveSync subscription flag on a folder
*
* @param string $name Folder name (UTF7-IMAP)
* @param string $deviceid Device identifier
* @param int $flag Flag value (0|1|2)
*/
public
function
folder_set
(
$name
,
$deviceid
,
$flag
)
{
if
(
empty
(
$deviceid
))
{
return
false
;
}
// get folders activesync config
$metadata
=
$this
->
folder_meta
();
$metadata
=
$metadata
[
$name
];
if
(
$flag
)
{
if
(
empty
(
$metadata
))
{
$metadata
=
array
();
}
if
(
empty
(
$metadata
[
'FOLDER'
]))
{
$metadata
[
'FOLDER'
]
=
array
();
}
if
(
empty
(
$metadata
[
'FOLDER'
][
$deviceid
]))
{
$metadata
[
'FOLDER'
][
$deviceid
]
=
array
();
}
// Z-Push uses:
// 1 - synchronize, no alarms
// 2 - synchronize with alarms
$metadata
[
'FOLDER'
][
$deviceid
][
'S'
]
=
$flag
;
}
if
(!
$flag
)
{
unset
(
$metadata
[
'FOLDER'
][
$deviceid
][
'S'
]);
if
(
empty
(
$metadata
[
'FOLDER'
][
$deviceid
]))
{
unset
(
$metadata
[
'FOLDER'
][
$deviceid
]);
}
if
(
empty
(
$metadata
[
'FOLDER'
]))
{
unset
(
$metadata
[
'FOLDER'
]);
}
if
(
empty
(
$metadata
))
{
$metadata
=
null
;
}
}
// Return if nothing's been changed
if
(!
self
::
data_array_diff
(
$this
->
folder_meta
[
$name
],
$metadata
))
{
return
true
;
}
$this
->
folder_meta
[
$name
]
=
$metadata
;
return
$this
->
storage
->
set_metadata
(
$name
,
array
(
self
::
ASYNC_KEY
=>
$this
->
serialize_metadata
(
$metadata
)));
}
public
function
device_get
(
$id
)
{
$devices_list
=
$this
->
devices_list
();
$result
=
$devices_list
[
$id
];
return
$result
;
}
/**
* Registers new device on server
*
* @param array $device Device data
* @param string $id Device ID
*
* @return bool True on success, False on failure
*/
public
function
device_create
(
$device
,
$id
)
{
// Fill local cache
$this
->
devices_list
();
// Old Kolab_ZPush device parameters
// MODE: -1 | 0 | 1 (not set | flatmode | foldermode)
// TYPE: device type string
// ALIAS: user-friendly device name
// Syncroton (kolab_sync_backend_device) uses
// ID: internal identifier in syncroton database
// TYPE: device type string
// ALIAS: user-friendly device name
$metadata
=
$this
->
root_meta
;
$metadata
[
'DEVICE'
][
$id
]
=
$device
;
$metadata
=
array
(
self
::
ASYNC_KEY
=>
$this
->
serialize_metadata
(
$metadata
));
$result
=
$this
->
storage
->
set_metadata
(
self
::
ROOT_MAILBOX
,
$metadata
);
if
(
$result
)
{
// Update local cache
$this
->
root_meta
[
'DEVICE'
][
$id
]
=
$device
;
// Subscribe to default folders
$foldertypes
=
$this
->
storage
->
get_metadata
(
'*'
,
kolab_storage
::
CTYPE_KEY
);
$types
=
array
(
'mail.drafts'
,
'mail.wastebasket'
,
'mail.sentitems'
,
'mail.outbox'
,
'event.default'
,
'contact.default'
,
'task.default'
,
'event'
,
'contact'
,
'task'
);
$foldertypes
=
array_map
(
'implode'
,
$foldertypes
);
$foldertypes
=
array_intersect
(
$foldertypes
,
$types
);
// get default folders
foreach
(
$foldertypes
as
$folder
=>
$type
)
{
// only personal folders
if
(
$this
->
storage
->
folder_namespace
(
$folder
)
==
'personal'
)
{
$this
->
folder_set
(
$folder
,
$id
,
1
);
}
}
// INBOX always exists
$this
->
folder_set
(
'INBOX'
,
$id
,
1
);
}
return
$result
;
}
public
function
device_update
(
$device
,
$id
)
{
$devices_list
=
$this
->
devices_list
();
$old_device
=
$devices_list
[
$id
];
if
(!
$old_device
)
{
return
false
;
}
// Do nothing if nothing is changed
if
(!
self
::
data_array_diff
(
$old_device
,
$device
))
{
return
true
;
}
$device
=
array_merge
(
$old_device
,
$device
);
$metadata
=
$this
->
root_meta
;
$metadata
[
'DEVICE'
][
$id
]
=
$device
;
$metadata
=
array
(
self
::
ASYNC_KEY
=>
$this
->
serialize_metadata
(
$metadata
));
$result
=
$this
->
storage
->
set_metadata
(
self
::
ROOT_MAILBOX
,
$metadata
);
if
(
$result
)
{
// Update local cache
$this
->
root_meta
[
'DEVICE'
][
$id
]
=
$device
;
}
return
$result
;
}
/**
* Device delete.
*
* @param string $id Device ID
*
* @return bool True on success, False on failure
*/
public
function
device_delete
(
$id
)
{
$device
=
$this
->
device_get
(
$id
);
if
(!
$device
)
{
return
false
;
}
unset
(
$this
->
root_meta
[
'DEVICE'
][
$id
],
$this
->
root_meta
[
'FOLDER'
][
$id
]);
if
(
empty
(
$this
->
root_meta
[
'DEVICE'
]))
{
unset
(
$this
->
root_meta
[
'DEVICE'
]);
}
if
(
empty
(
$this
->
root_meta
[
'FOLDER'
]))
{
unset
(
$this
->
root_meta
[
'FOLDER'
]);
}
$metadata
=
$this
->
serialize_metadata
(
$this
->
root_meta
);
$metadata
=
array
(
self
::
ASYNC_KEY
=>
$metadata
);
// update meta data
$result
=
$this
->
storage
->
set_metadata
(
self
::
ROOT_MAILBOX
,
$metadata
);
if
(
$result
)
{
// remove device annotation for every folder
foreach
(
$this
->
folder_meta
()
as
$folder
=>
$meta
)
{
// skip root folder (already handled above)
if
(
$folder
==
self
::
ROOT_MAILBOX
)
continue
;
if
(!
empty
(
$meta
[
'FOLDER'
])
&&
isset
(
$meta
[
'FOLDER'
][
$id
]))
{
unset
(
$meta
[
'FOLDER'
][
$id
]);
if
(
empty
(
$meta
[
'FOLDER'
]))
{
unset
(
$this
->
folder_meta
[
$folder
][
'FOLDER'
]);
unset
(
$meta
[
'FOLDER'
]);
}
if
(
empty
(
$meta
))
{
unset
(
$this
->
folder_meta
[
$folder
]);
$meta
=
null
;
}
$metadata
=
array
(
self
::
ASYNC_KEY
=>
$this
->
serialize_metadata
(
$meta
));
$res
=
$this
->
storage
->
set_metadata
(
$folder
,
$metadata
);
if
(
$res
&&
$meta
)
{
$this
->
folder_meta
[
$folder
]
=
$meta
;
}
}
}
}
return
$result
;
}
/**
* Helper method to decode saved IMAP metadata
*/
private
function
unserialize_metadata
(
$str
)
{
if
(!
empty
(
$str
))
{
// Support old Z-Push annotation format
if
(
$str
[
0
]
!=
'{'
)
{
$str
=
base64_decode
(
$str
);
}
$data
=
json_decode
(
$str
,
true
);
return
$data
;
}
return
null
;
}
/**
* Helper method to encode IMAP metadata for saving
*/
private
function
serialize_metadata
(
$data
)
{
if
(!
empty
(
$data
)
&&
is_array
(
$data
))
{
$data
=
json_encode
(
$data
);
// $data = base64_encode($data);
return
$data
;
}
return
null
;
}
/**
* Returns Kolab folder type for specified ActiveSync type ID
*/
public
static
function
type_activesync2kolab
(
$type
)
{
if
(!
empty
(
self
::
$types
[
$type
]))
{
return
self
::
$types
[
$type
];
}
return
''
;
}
/**
* Returns ActiveSync folder type for specified Kolab type
*/
public
static
function
type_kolab2activesync
(
$type
)
{
if
(
$key
=
array_search
(
$type
,
self
::
$types
))
{
return
$key
;
}
return
key
(
self
::
$types
);
}
/**
* Returns Kolab folder type for specified ActiveSync class name
*/
public
static
function
class_activesync2kolab
(
$class
)
{
if
(!
empty
(
self
::
$classes
[
$class
]))
{
return
self
::
$classes
[
$class
];
}
return
''
;
}
private
function
folder_data
(
$folder
,
$type
)
{
// Folder name parameters
$delim
=
$this
->
storage
->
get_hierarchy_delimiter
();
$items
=
explode
(
$delim
,
$folder
);
$name
=
array_pop
(
$items
);
// Folder UID
$folder_id
=
$this
->
folder_id
(
$folder
);
// Syncroton folder data array
return
array
(
'serverId'
=>
$folder_id
,
'parentId'
=>
count
(
$items
)
?
self
::
folder_id
(
implode
(
$delim
,
$items
))
:
0
,
'displayName'
=>
rcube_charset
::
convert
(
$name
,
'UTF7-IMAP'
,
kolab_sync
::
CHARSET
),
'type'
=>
self
::
type_kolab2activesync
(
$type
),
);
}
/**
* Builds folder ID based on folder name
*/
public
function
folder_id
(
$name
,
$type
=
null
)
{
// ActiveSync expects folder identifiers to be max.64 characters
// So we can't use just folder name
if
(
$name
===
''
||
!
is_string
(
$name
))
{
return
null
;
}
if
(
isset
(
$this
->
folder_uids
[
$name
]))
{
return
$this
->
folder_uids
[
$name
];
}
/*
@TODO: For now uniqueid annotation doesn't work, we will create UIDs by ourselves.
There's one inconvenience of this solution: folder name/type change
would be handled in ActiveSync as delete + create.
// get folders unique identifier
$folderdata = $this->storage->get_metadata($name, self::UID_KEY);
if ($folderdata && !empty($folderdata[$name])) {
$uid = $folderdata[$name][self::UID_KEY];
$this->folder_uids[$name] = $uid;
return $uid;
}
*/
// Add type to folder UID hash, so type change can be detected by Syncroton
if
(!
$type
)
{
$metadata
=
$this
->
storage
->
get_metadata
(
$name
,
kolab_storage
::
CTYPE_KEY
);
$type
=
$metadata
[
$name
][
kolab_storage
::
CTYPE_KEY
];
}
$uid
=
md5
(
$name
.
'!!'
.
$type
);
return
$this
->
folder_uids
[
$name
]
=
$uid
;
return
$uid
;
}
/**
* Returns IMAP folder name
*
* @param string $id Folder identifier
* @param string $deviceid Device dentifier
*
* @return string Folder name (UTF7-IMAP)
*/
public
function
folder_id2name
(
$id
,
$deviceid
)
{
// check in cache first
if
(!
empty
(
$this
->
folder_uids
))
{
if
((
$name
=
array_search
(
$id
,
$this
->
folder_uids
))
!==
false
)
{
return
$name
;
}
}
/*
@TODO: see folder_id()
// get folders unique identifier
$folderdata = $this->storage->get_metadata('*', self::UID_KEY);
foreach ((array)$folderdata as $folder => $data) {
if (!empty($data[self::UID_KEY])) {
$uid = $data[self::UID_KEY];
$this->folder_uids[$folder] = $uid;
if ($uid == $id) {
$name = $folder;
}
}
}
*/
// get all folders of specified type
$folderdata
=
$this
->
folder_meta
();
// check if folders are "subscribed" for activesync
foreach
(
$folderdata
as
$folder
=>
$meta
)
{
if
(
empty
(
$meta
[
'FOLDER'
])
||
empty
(
$meta
[
'FOLDER'
][
$deviceid
])
||
empty
(
$meta
[
'FOLDER'
][
$deviceid
][
'S'
])
)
{
continue
;
}
$uid
=
self
::
folder_id
(
$folder
);
$this
->
folder_uids
[
$folder
]
=
$uid
;
if
(
$uid
==
$id
)
{
$name
=
$folder
;
}
}
return
$name
;
}
/**
* Compares two arrays
*
* @param array $array1
* @param array $array2
*
* @return bool True if arrays differs, False otherwise
*/
private
static
function
data_array_diff
(
$array1
,
$array2
)
{
if
(!
is_array
(
$array1
)
||
!
is_array
(
$array2
))
{
return
$array1
!=
$array2
;
}
if
(
count
(
$array1
)
!=
count
(
$array2
))
{
return
true
;
}
foreach
(
$array1
as
$key
=>
$val
)
{
if
(!
array_key_exists
(
$key
,
$array2
))
{
return
true
;
}
if
(
$val
!==
$array2
[
$key
])
{
return
true
;
}
}
return
false
;
}
/**
* Send the given message using the configured method.
*
* @param string $message Complete message source
* @param array $smtp_error SMTP error array (reference)
* @param array $smtp_opts SMTP options (e.g. DSN request)
*
* @return boolean Send status.
*/
public
function
send_message
(&
$message
,
&
$smtp_error
,
$smtp_opts
=
null
)
{
$rcube
=
rcube
::
get_instance
();
list
(
$headers
,
$message
)
=
$this
->
parse_mime
(
$message
);
$mailto
=
$headers
[
'To'
];
$headers
[
'User-Agent'
]
.=
sprintf
(
'%s v.%.1f'
,
$rcube
->
app_name
,
kolab_sync
::
VERSION
);
if
(
$agent
=
$rcube
->
config
->
get
(
'useragent'
))
{
$headers
[
'User-Agent'
]
.=
'/'
.
$agent
;
}
if
(
empty
(
$headers
[
'From'
]))
{
$headers
[
'From'
]
=
$this
->
get_identity
();
}
if
(
empty
(
$headers
[
'Message-ID'
]))
{
$headers
[
'Message-ID'
]
=
$this
->
gen_message_id
();
}
// remove empty headers
$headers
=
array_filter
(
$headers
);
// send thru SMTP server using custom SMTP library
if
(
$rcube
->
config
->
get
(
'smtp_server'
))
{
$smtp_headers
=
$headers
;
// generate list of recipients
$recipients
=
array
();
if
(!
empty
(
$headers
[
'To'
]))
$recipients
[]
=
$headers
[
'To'
];
if
(!
empty
(
$headers
[
'Cc'
]))
$recipients
[]
=
$headers
[
'Cc'
];
if
(!
empty
(
$headers
[
'Bcc'
]))
$recipients
[]
=
$headers
[
'Bcc'
];
// remove Bcc header
unset
(
$smtp_headers
[
'Bcc'
]);
// send message
if
(!
is_object
(
$rcube
->
smtp
))
{
$rcube
->
smtp_init
(
true
);
}
$sent
=
$rcube
->
smtp
->
send_mail
(
$headers
[
'From'
],
$recipients
,
$smtp_headers
,
$message
,
$smtp_opts
);
$smtp_response
=
$rcube
->
smtp
->
get_response
();
$smtp_error
=
$rcube
->
smtp
->
get_error
();
// log error
if
(!
$sent
)
{
rcube
::
raise_error
(
array
(
'code'
=>
800
,
'type'
=>
'smtp'
,
'line'
=>
__LINE__
,
'file'
=>
__FILE__
,
'message'
=>
"SMTP error: "
.
join
(
"
\n
"
,
$smtp_response
)),
true
,
false
);
}
}
// send mail using PHP's mail() function
else
{
$mail_headers
=
$headers
;
$delim
=
$rcube
->
config
->
header_delimiter
();
$subject
=
$headers
[
'Subject'
];
$to
=
$headers
[
'To'
];
// unset some headers because they will be added by the mail() function
unset
(
$mail_headers
[
'To'
],
$mail_headers
[
'Subject'
]);
// #1485779
if
(
strtoupper
(
substr
(
PHP_OS
,
0
,
3
))
===
'WIN'
)
{
if
(
preg_match_all
(
'/<([^@]+@[^>]+)>/'
,
$to
,
$m
))
{
$to
=
implode
(
', '
,
$m
[
1
]);
}
}
foreach
(
$mail_headers
as
$header
=>
$header_value
)
{
$mail_headers
[
$header
]
=
$header
.
': '
.
$header_value
;
}
$header_str
=
rtrim
(
implode
(
"
\r\n
"
,
$mail_headers
));
if
(
$delim
!=
"
\r\n
"
)
{
$header_str
=
str_replace
(
"
\r\n
"
,
$delim
,
$header_str
);
$msg_body
=
str_replace
(
"
\r\n
"
,
$delim
,
$message
);
$to
=
str_replace
(
"
\r\n
"
,
$delim
,
$to
);
$subject
=
str_replace
(
"
\r\n
"
,
$delim
,
$subject
);
}
if
(
ini_get
(
'safe_mode'
))
$sent
=
mail
(
$to
,
$subject
,
$message
,
$header_str
);
else
$sent
=
mail
(
$to
,
$subject
,
$message
,
$header_str
,
"-f$from"
);
}
if
(
$sent
)
{
$rcube
->
plugins
->
exec_hook
(
'message_sent'
,
array
(
'headers'
=>
$headers
,
'body'
=>
$message
));
// remove MDN headers after sending
unset
(
$headers
[
'Return-Receipt-To'
],
$headers
[
'Disposition-Notification-To'
]);
// get all recipients
if
(
$headers
[
'Cc'
])
$mailto
.=
' '
.
$headers
[
'Cc'
];
if
(
$headers
[
'Bcc'
])
$mailto
.=
' '
.
$headers
[
'Bcc'
];
if
(
preg_match_all
(
'/<([^@]+@[^>]+)>/'
,
$mailto
,
$m
))
$mailto
=
implode
(
', '
,
array_unique
(
$m
[
1
]));
if
(
$rcube
->
config
->
get
(
'smtp_log'
))
{
rcube
::
write_log
(
'sendmail'
,
sprintf
(
"User %s [%s]; Message for %s; %s"
,
$rcube
->
get_user_name
(),
$_SERVER
[
'REMOTE_ADDR'
],
$mailto
,
!
empty
(
$smtp_response
)
?
join
(
'; '
,
$smtp_response
)
:
''
));
}
}
unset
(
$headers
[
'Bcc'
]);
// Build the message back
foreach
(
$headers
as
$header
=>
$header_value
)
{
$headers
[
$header
]
=
$header
.
': '
.
$header_value
;
}
$message
=
trim
(
implode
(
"
\r\n
"
,
$headers
))
.
"
\r\n\r\n
"
.
ltrim
(
$message
);
return
$sent
;
}
/**
* MIME message parser
*
* @param string|resource $message MIME message source
* @param bool $decode_body Enables body decoding
*
* @return array Message headers array and message body
*/
public
function
parse_mime
(
$message
,
$decode_body
=
false
)
{
if
(
is_resource
(
$message
))
{
$message
=
stream_get_contents
(
$message
);
}
list
(
$headers
,
$message
)
=
preg_split
(
'/
\r
?
\n\r
?
\n
/'
,
$message
,
2
,
PREG_SPLIT_NO_EMPTY
);
// Parse headers to get sender and recipients
$headers
=
str_replace
(
"
\r\n
"
,
"
\n
"
,
$headers
);
$headers
=
explode
(
"
\n
"
,
trim
(
$headers
));
$ln
=
0
;
$lines
=
array
();
foreach
(
$headers
as
$line
)
{
if
(
ord
(
$line
[
0
])
<=
32
)
{
$lines
[
$ln
]
.=
(
empty
(
$lines
[
$ln
])
?
''
:
"
\n
"
)
.
$line
;
}
else
{
$lines
[++
$ln
]
=
trim
(
$line
);
}
}
$headers
=
array
();
$headers_map
=
array
(
'subject'
=>
'Subject'
,
'from'
=>
'From'
,
'to'
=>
'To'
,
'cc'
=>
'Cc'
,
'bcc'
=>
'Bcc'
,
'message-id'
=>
'Message-ID'
,
'references'
=>
'References'
,
'content-type'
=>
'Content-Type'
,
'content-transfer-encoding'
=>
'Content-Transfer-Encoding'
,
);
foreach
(
$lines
as
$line
)
{
list
(
$field
,
$string
)
=
explode
(
':'
,
$line
,
2
);
$_field
=
strtolower
(
$field
);
if
(
isset
(
$headers_map
[
$_field
]))
{
$field
=
$headers_map
[
$_field
];
}
$headers
[
$field
]
=
trim
(
$string
);
}
// Decode body
if
(
$decode_body
)
{
$message
=
str_replace
(
"
\r\n
"
,
"
\n
"
,
$message
);
$encoding
=
strtolower
(
$headers
[
'Content-Transfer-Encoding'
]);
switch
(
$encoding
)
{
case
'base64'
:
$message
=
base64_decode
(
$message
);
break
;
case
'quoted-printable'
:
$message
=
quoted_printable_decode
(
$message
);
break
;
}
}
return
array
(
$headers
,
$message
);
}
/**
* Creates complete MIME message body
*
* @param array $headers Message headers
* @param string $body Message body
*
* @return string Message source
*/
public
function
build_mime
(
$headers
,
$body
)
{
// Encode the body
$encoding
=
strtolower
(
$headers
[
'Content-Transfer-Encoding'
]);
switch
(
$encoding
)
{
case
'base64'
:
$body
=
base64_encode
(
$body
);
$body
=
chunk_split
(
$body
,
76
,
"
\r\n
"
);
break
;
case
'quoted-printable'
:
$body
=
quoted_printable_encode
(
$body
);
break
;
}
foreach
(
$headers
as
$header
=>
$header_value
)
{
$headers
[
$header
]
=
$header
.
': '
.
$header_value
;
}
// Build the complete message
return
trim
(
implode
(
"
\r\n
"
,
$headers
))
.
"
\r\n\r\n
"
.
ltrim
(
$body
);
}
/**
* Returns email address string from default identity of the current user
*/
protected
function
get_identity
()
{
$user
=
kolab_sync
::
get_instance
()->
user
;
if
(
$identity
=
$user
->
get_identity
())
{
return
format_email_recipient
(
format_email
(
$identity
[
'email'
]),
$identity
[
'name'
]);
}
}
/**
* Unique Message-ID generator.
*
* @return string Message-ID
*/
protected
function
gen_message_id
()
{
$user
=
kolab_sync
::
get_instance
()->
user
;
$local_part
=
md5
(
uniqid
(
'rcmail'
.
mt_rand
(),
true
));
$domain_part
=
$user
->
get_username
(
'domain'
);
// Try to find FQDN, some spamfilters doesn't like 'localhost' (#1486924)
if
(!
preg_match
(
'/
\.
[a-z]+$/i'
,
$domain_part
))
{
foreach
(
array
(
$_SERVER
[
'HTTP_HOST'
],
$_SERVER
[
'SERVER_NAME'
])
as
$host
)
{
$host
=
preg_replace
(
'/:[0-9]+$/'
,
''
,
$host
);
if
(
$host
&&
preg_match
(
'/
\.
[a-z]+$/i'
,
$host
))
{
$domain_part
=
$host
;
}
}
}
return
sprintf
(
'<%s@%s>'
,
$local_part
,
$domain_part
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Mon, Aug 25, 2:40 PM (22 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
257666
Default Alt Text
kolab_sync_backend.php (29 KB)
Attached To
Mode
R4 syncroton
Attached
Detach File
Event Timeline
Log In to Comment