Changes of Revision 67

kolab-syncroton.spec Changed
x
 
1
@@ -43,7 +43,7 @@
2
 %global upstream_version 2.4.2
3
 
4
 Name:           kolab-syncroton
5
-Version: 2.4.2.39
6
+Version: 2.4.2.41
7
 Release:        1%{?dist}
8
 Summary:        ActiveSync for Kolab Groupware
9
 
10
debian.changelog Changed
7
 
1
@@ -1,4 +1,4 @@
2
-kolab-syncroton (2.4.2.39-0~kolab1) unstable; urgency=low
3
+kolab-syncroton (2.4.2.41-0~kolab1) unstable; urgency=low
4
 
5
   * Release version 2.4.2
6
 
7
kolab-syncroton-2.4.2.tar.gz/bin/delete-device.php Changed
18
 
1
@@ -74,11 +74,15 @@
2
 }
3
 $owner = $opts'owner';
4
 
5
+$host = $rcube->config->get('imap_host', $rcube->config->get('default_host'));
6
+$host = parse_url($host, PHP_URL_HOST) ?? $host;
7
 $select = $db->query(
8
     "SELECT `user_id` FROM `users`"
9
     . " WHERE `username` = ?"
10
+    . " AND `mail_host` = ?"
11
     . " ORDER BY `user_id` DESC",
12
-    \strtolower($owner)
13
+    \strtolower($owner),
14
+    \strtolower($host)
15
 );
16
 
17
 if ($data = $db->fetch_assoc($select)) {
18
kolab-syncroton-2.4.2.tar.gz/bin/inspect.php Changed
134
 
1
@@ -78,6 +78,7 @@
2
     'e' => 'email',
3
     'p' => 'adminpassword',
4
     'd' => 'debug',
5
+    'k' => 'dump'
6
 );
7
 
8
 if (empty($opts'email')) {
9
@@ -99,14 +100,35 @@
10
 
11
 $rcube = rcube::get_instance();
12
 $default_port = $rcube->config->get('default_port', 143);
13
-$default_host = $rcube->config->get('default_host');
14
+
15
+$host = $rcube->config->get('imap_host', $rcube->config->get('default_host'));
16
+
17
+// parse $host
18
+$a_host = parse_url($host);
19
+$port = null;
20
+$ssl = null;
21
+if (!empty($a_host'host')) {
22
+    $host = $a_host'host';
23
+    $ssl = (isset($a_host'scheme') && in_array($a_host'scheme', 'ssl','imaps','tls')) ? $a_host'scheme' : null;
24
+    if (!empty($a_host'port')) {
25
+        $port = $a_host'port';
26
+    } elseif ($ssl && $ssl != 'tls' && (!$default_port || $default_port == 143)) {
27
+        $port = 993;
28
+    }
29
+}
30
+
31
+if (!$port) {
32
+    $port = $default_port;
33
+}
34
+
35
 $imap = new \rcube_imap_generic();
36
 if ($proxyAuth) {
37
     $options'auth_cid' = $user;
38
     $options'auth_pw' = $password;
39
 }
40
 $options'auth_type' = 'PLAIN';
41
-$options'port' = $default_port;
42
+$options'port' = $port;
43
+
44
 $options'socket_options' = 
45
     'ssl' => 
46
         'verify_peer_name' => false,
47
@@ -121,7 +143,7 @@
48
 $debug = !empty($opts'debug');
49
 
50
 $imap->setDebug($debug);
51
-if (!$imap->connect($default_host, $email, $password, $options)) {
52
+if (!$imap->connect($host, $email, $password, $options)) {
53
     rcube::raise_error("Failed to connect to imap.", false, true);
54
 }
55
 
56
@@ -131,8 +153,10 @@
57
 $select = $db->query(
58
     "SELECT `user_id` FROM `users`"
59
     . " WHERE `username` = ?"
60
+    . " AND `mail_host` = ?"
61
     . " ORDER BY `user_id` DESC",
62
-    \strtolower($email)
63
+    \strtolower($email),
64
+    \strtolower($host)
65
 );
66
 
67
 if ($data = $db->fetch_assoc($select)) {
68
@@ -260,6 +284,23 @@
69
     return "Unknown value: $value";
70
 }
71
 
72
+
73
+function getContentIds($db, $device_id, $folder_id)
74
+{
75
+    $contentSelect = $db->query(
76
+        "SELECT contentid FROM `syncroton_content`"
77
+        . " WHERE `device_id` = ? AND `folder_id` = ? AND `is_deleted` = 0",
78
+        $device_id,
79
+        $folder_id
80
+    );
81
+
82
+    $contentUids = ;
83
+    while ($content = $db->fetch_assoc($contentSelect)) {
84
+        $contentUids = $content'contentid';
85
+    }
86
+    return $contentUids;
87
+}
88
+
89
 function getContentUids($db, $device_id, $folder_id)
90
 {
91
     $contentSelect = $db->query(
92
@@ -311,6 +352,41 @@
93
         println("    Number of syncs: " . ($folder'counter' ?? "None"));
94
         println("    Filter type: " . filterType($folder'lastfiltertype' ?? null));
95
 
96
+
97
+        if (!empty($opts'dump') && $opts'dump' == $folder'name') {
98
+            $contentUids = getContentUids($db, $deviceId, $folderId);
99
+            $imapUids = getImapUids($imap, $folder'name', $folder'lastfiltertype' ?? null);
100
+
101
+            $entries = array_diff($imapUids, $contentUids);
102
+            if (!empty($entries)) {
103
+                println("       The following messages are on the server, but not the device:");
104
+                foreach ($entries as $uid) {
105
+                    println("           $uid");
106
+                    //TODO get details from imap?
107
+                }
108
+            }
109
+
110
+            $entries = array_diff($contentUids, $imapUids);
111
+            if (!empty($entries)) {
112
+                println("       The following messages are on the device, but not the server:");
113
+                foreach ($entries as $uid) {
114
+                    println("           $uid");
115
+                    //TODO get details from the content part?
116
+                    //TODO display creation_synckey?
117
+                }
118
+            }
119
+
120
+            $contentUids = getContentUids($db, $deviceId, $folderId);
121
+            foreach (array_slice($contentUids, -10, 10) as $uid) {
122
+                println($uid);
123
+            }
124
+
125
+            $contentIds = getContentIds($db, $deviceId, $folderId);
126
+            foreach (array_slice($contentIds, -10, 10) as $uid) {
127
+                println($uid);
128
+            }
129
+        }
130
+
131
         if (($folder'class' == "Email") && ($folder'counter' ?? false) && $messageCount != $totalCount && ($modseq == "none" || $modseq == $imapModseq)) {
132
             if (($folder'lastfiltertype' ?? false) && $messageCount > $totalCount) {
133
                 // This doesn't have to indicate an issue, since the timewindow of the filter wanders, so some messages that have been synchronized may no longer match the window.
134
kolab-syncroton-2.4.2.tar.gz/config/config.inc.php.dist Changed
14
 
1
@@ -10,11 +10,9 @@
2
 $config'activesync_log_file' = null;
3
 
4
 // Type of ActiveSync cache. Supported values: 'db', 'apc' and 'memcache'.
5
-// Note: This is only for some additional data like timezones mapping.
6
 $config'activesync_cache' = 'db';
7
 
8
-// lifetime of ActiveSync cache
9
-// possible units: s, m, h, d, w
10
+// Lifetime of the ActiveSync cache. Supported units: s, m, h, d, w
11
 $config'activesync_cache_ttl' = '1d';
12
 
13
 // Type of ActiveSync Auth cache. Supported values: 'db', 'apc' and 'memcache'.
14
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/SendMail.php Changed
25
 
1
@@ -23,6 +23,7 @@
2
     protected $_defaultNameSpace    = 'uri:ComposeMail';
3
     protected $_documentElement     = 'SendMail';
4
 
5
+    protected $_clientId;
6
     protected $_mime;
7
     protected $_saveInSent;
8
     protected $_source;
9
@@ -48,6 +49,7 @@
10
         } elseif ($this->_requestBody) {
11
             $xml = simplexml_import_dom($this->_requestBody);
12
 
13
+            $this->_clientId      = (string) $xml->ClientId;
14
             $this->_mime          = (string) $xml->Mime;
15
             $this->_saveInSent    = isset($xml->SaveInSentItems);
16
             $this->_replaceMime   = isset($xml->ReplaceMime);
17
@@ -125,6 +127,6 @@
18
      */
19
     protected function sendMail($dataController)
20
     {
21
-        $dataController->sendEmail($this->_mime, $this->_saveInSent);
22
+        $dataController->sendEmail($this->_mime, $this->_saveInSent, $this->_clientId);
23
     }
24
 }
25
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/SmartForward.php Changed
15
 
1
@@ -26,6 +26,12 @@
2
      */
3
     protected function sendMail($dataController)
4
     {
5
-        $dataController->forwardEmail($this->_source, $this->_mime, $this->_saveInSent, $this->_replaceMime);
6
+        $dataController->forwardEmail(
7
+            $this->_source,
8
+            $this->_mime,
9
+            $this->_saveInSent,
10
+            $this->_replaceMime,
11
+            $this->_clientId
12
+        );
13
     }
14
 }
15
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/SmartReply.php Changed
9
 
1
@@ -26,6 +26,6 @@
2
      */
3
     protected function sendMail($dataController)
4
     {
5
-        $dataController->replyEmail($this->_source, $this->_mime, $this->_saveInSent, $this->_replaceMime);
6
+        $dataController->replyEmail($this->_source, $this->_mime, $this->_saveInSent, $this->_replaceMime, $this->_clientId);
7
     }
8
 }
9
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Data/Email.php Changed
28
 
1
@@ -31,7 +31,7 @@
2
      * (non-PHPdoc)
3
      * @see Syncroton_Data_IDataEmail::forwardEmail()
4
      */
5
-    public function forwardEmail($source, $inputStream, $saveInSent, $replaceMime)
6
+    public function forwardEmail($source, $inputStream, $saveInSent, $replaceMime, $clientId)
7
     {
8
         if ($inputStream == 'triggerException') {
9
             throw new Syncroton_Exception_Status(Syncroton_Exception_Status::MAILBOX_SERVER_OFFLINE);
10
@@ -59,7 +59,7 @@
11
      * (non-PHPdoc)
12
      * @see Syncroton_Data_IDataEmail::replyEmail()
13
      */
14
-    public function replyEmail($source, $inputStream, $saveInSent, $replaceMime)
15
+    public function replyEmail($source, $inputStream, $saveInSent, $replaceMime, $clientId)
16
     {
17
         // forward email
18
     }
19
@@ -78,7 +78,7 @@
20
      * (non-PHPdoc)
21
      * @see Syncroton_Data_IDataEmail::sendEmail()
22
      */
23
-    public function sendEmail($inputStream, $saveInSent)
24
+    public function sendEmail($inputStream, $saveInSent, $clientId)
25
     {
26
         if ($inputStream == 'triggerException') {
27
             throw new Syncroton_Exception_Status(Syncroton_Exception_Status::MAILBOX_SERVER_OFFLINE);
28
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Data/IDataEmail.php Changed
44
 
1
@@ -21,26 +21,31 @@
2
     /**
3
      * send an email
4
      *
5
-     * @param  resource  $inputStream
6
-     * @param  boolean   $saveInSent
7
+     * @param resource $inputStream
8
+     * @param bool     $saveInSent
9
+     * @param ?string  $clientId
10
      */
11
-    public function sendEmail($inputStream, $saveInSent);
12
+    public function sendEmail($inputStream, $saveInSent, $clientId);
13
 
14
     /**
15
      * forward an email
16
      *
17
-     * @param  string|array  $source       is either a string(LongId) or an array with following properties collectionId, itemId and instanceId
18
-     * @param  string        $inputStream
19
-     * @param  string        $saveInSent
20
+     * @param string|array  $source       is either a string(LongId) or an array with following properties collectionId, itemId and instanceId
21
+     * @param string        $inputStream
22
+     * @param string        $saveInSent
23
+     * @param bool          $replaceMime
24
+     * @param ?string       $clientId
25
      */
26
-    public function forwardEmail($source, $inputStream, $saveInSent, $replaceMime);
27
+    public function forwardEmail($source, $inputStream, $saveInSent, $replaceMime, $clientId);
28
 
29
     /**
30
      * reply to an email
31
      *
32
-     * @param  string|array  $source       is either a string(LongId) or an array with following properties collectionId, itemId and instanceId
33
-     * @param  string        $inputStream
34
-     * @param  string        $saveInSent
35
+     * @param string|array  $source       is either a string(LongId) or an array with following properties collectionId, itemId and instanceId
36
+     * @param string        $inputStream
37
+     * @param string        $saveInSent
38
+     * @param bool          $replaceMime
39
+     * @param ?string       $clientId
40
      */
41
-    public function replyEmail($source, $inputStream, $saveInSent, $replaceMime);
42
+    public function replyEmail($source, $inputStream, $saveInSent, $replaceMime, $clientId);
43
 }
44
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Server.php Changed
13
 
1
@@ -165,6 +165,11 @@
2
             if (!$response) {
3
                 $response = $command->getResponse();
4
             }
5
+
6
+            if ($response === null) {
7
+                // FIXME: Is this really needed? It is for PHP cli-server, but not really for a real http server
8
+                header('Content-Length: 0');
9
+            }
10
         } catch (Syncroton_Exception_ProvisioningNeeded $sepn) {
11
             if ($this->_logger instanceof Zend_Log) {
12
                 $this->_logger->info(__METHOD__ . '::' . __LINE__ . " provisioning needed");
13
kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_data_email.php Changed
211
 
1
@@ -30,6 +30,9 @@
2
 {
3
     public const MAX_SEARCH_RESULT = 200;
4
 
5
+    protected const MAIL_SUBMITTED = 1;
6
+    protected const MAIL_DONE = 2;
7
+
8
     /**
9
      * Mapping from ActiveSync Email namespace fields
10
      */
11
@@ -510,9 +513,13 @@
12
         $is_ios = preg_match('/(iphone|ipad)/i', $this->device->devicetype);
13
 
14
         // attachments
15
-        $attachments = array_merge($message->attachments, $message->inline_parts);
16
+        $attachments = $message->attachments;
17
+        if (isset($message->inline_parts)) {
18
+            $attachments = array_merge($attachments, $message->inline_parts);
19
+        }
20
         if (!empty($attachments)) {
21
             $result'attachments' = ;
22
+            $attachment_ids = ;
23
 
24
             foreach ($attachments as $attachment) {
25
                 $att = ;
26
@@ -521,28 +528,37 @@
27
                     continue;
28
                 }
29
 
30
+                // Eliminate possible duplicates in $attachments
31
+                if (in_array($attachment->mime_id, $attachment_ids)) {
32
+                    continue;
33
+                }
34
+
35
                 $filename = rcube_charset::clean($attachment->filename);
36
                 if (empty($filename) && $attachment->mimetype == 'text/html') {
37
                     $filename = 'HTML Part';
38
                 }
39
 
40
-                $att'displayName'   = $filename;
41
+                $att'displayName' = $filename;
42
                 $att'fileReference' = $serverId . '::' . $attachment->mime_id;
43
-                $att'method'        = 1;
44
+                // Message/rfc822 parts have Method=5, anything else Method=1
45
+                // FIXME: Method=6 is for OLE objects, but is it inline images or sth else?
46
+                $att'method' = strcasecmp($attachment->mimetype, 'message/rfc822') === 0 ? 5 : 1;
47
                 $att'estimatedDataSize' = $attachment->size;
48
 
49
                 if (!empty($attachment->content_id)) {
50
                     $att'contentId' = rcube_charset::clean($attachment->content_id);
51
                 }
52
+
53
                 if (!empty($attachment->content_location)) {
54
                     $att'contentLocation' = rcube_charset::clean($attachment->content_location);
55
                 }
56
 
57
-                if (in_array($attachment, $message->inline_parts)) {
58
+                if (strcasecmp($attachment->disposition, 'inline') === 0) {
59
                     $att'isInline' = 1;
60
                 }
61
 
62
                 $result'attachments' = new Syncroton_Model_EmailAttachment($att);
63
+                $attachment_ids = $attachment->mime_id;
64
             }
65
         }
66
 
67
@@ -813,30 +829,44 @@
68
      * Send an email
69
      *
70
      * @param mixed   $message    MIME message
71
-     * @param boolean $saveInSent Enables saving the sent message in Sent folder
72
+     * @param bool    $saveInSent Enables saving the sent message in Sent folder
73
+     * @param ?string $clientId   Message client-id
74
      *
75
      * @throws Syncroton_Exception_Status
76
      */
77
-    public function sendEmail($message, $saveInSent)
78
+    public function sendEmail($message, $saveInSent, $clientId)
79
     {
80
+        if (($status = $this->sentMailStatus($clientId, $cache, $cache_key)) === self::MAIL_DONE) {
81
+            throw new Syncroton_Exception_Status(Syncroton_Exception_Status::MESSAGE_PREVIOUSLY_SENT);
82
+        }
83
+
84
         if (!($message instanceof kolab_sync_message)) {
85
             $message = new kolab_sync_message($message);
86
         }
87
 
88
-        $sent = $message->send($smtp_error);
89
+        // Snet the message (if not sent previously)
90
+        if (!$status) {
91
+            $sent = $message->send($smtp_error);
92
 
93
-        if (!$sent) {
94
-            throw new Syncroton_Exception_Status(Syncroton_Exception_Status::MAIL_SUBMISSION_FAILED);
95
+            if (!$sent) {
96
+                throw new Syncroton_Exception_Status(Syncroton_Exception_Status::MAIL_SUBMISSION_FAILED);
97
+            }
98
+
99
+            if (!empty($cache)) {
100
+                $cache->set($cache_key, self::MAIL_SUBMITTED);
101
+            }
102
         }
103
 
104
         // Save sent message in Sent folder
105
         if ($saveInSent) {
106
-            $sent_folder = kolab_sync::get_instance()->config->get('sent_mbox');
107
-
108
-            if (strlen($sent_folder) && $this->storage->folder_exists($sent_folder)) {
109
-                return $this->storage->save_message($sent_folder, $message->source(), '', false, 'SEEN');
110
+            if (!$message->saveInSent()) {
111
+                throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR);
112
             }
113
         }
114
+
115
+        if (!empty($cache)) {
116
+            $cache->set($cache_key, self::MAIL_DONE);
117
+        }
118
     }
119
 
120
     /**
121
@@ -845,13 +875,18 @@
122
      * @param array|string    $itemId      A string LongId or an array with following properties:
123
      *                                     collectionId, itemId and instanceId
124
      * @param resource|string $body        MIME message
125
-     * @param boolean         $saveInSent  Enables saving the sent message in Sent folder
126
-     * @param boolean         $replaceMime If enabled, original message would be appended
127
+     * @param bool            $saveInSent  Enables saving the sent message in Sent folder
128
+     * @param bool            $replaceMime If enabled, original message would be appended
129
+     * @param ?string         $clientId    Message client-id
130
      *
131
      * @throws Syncroton_Exception_Status
132
      */
133
-    public function forwardEmail($itemId, $body, $saveInSent, $replaceMime)
134
+    public function forwardEmail($itemId, $body, $saveInSent, $replaceMime, $clientId)
135
     {
136
+        if ($this->sentMailStatus($clientId) === self::MAIL_DONE) {
137
+            throw new Syncroton_Exception_Status(Syncroton_Exception_Status::MESSAGE_PREVIOUSLY_SENT);
138
+        }
139
+
140
         /*
141
         @TODO:
142
         The SmartForward command can be applied to a meeting. When SmartForward is applied to a recurring meeting,
143
@@ -893,7 +928,7 @@
144
         }
145
 
146
         // Send message
147
-        $this->sendEmail($sync_msg, $saveInSent);
148
+        $this->sendEmail($sync_msg, $saveInSent, $clientId);
149
 
150
         // Set FORWARDED flag on the replied message
151
         if (empty($message->headers->flags'FORWARDED')) {
152
@@ -908,13 +943,18 @@
153
      * @param array|string    $itemId      A string LongId or an array with following properties:
154
      *                                     collectionId, itemId and instanceId
155
      * @param resource|string $body        MIME message
156
-     * @param boolean         $saveInSent  Enables saving the sent message in Sent folder
157
-     * @param boolean         $replaceMime If enabled, original message would be appended
158
+     * @param bool            $saveInSent  Enables saving the sent message in Sent folder
159
+     * @param bool            $replaceMime If enabled, original message would be appended
160
+     * @param ?string         $clientId    Message client-id
161
      *
162
      * @throws Syncroton_Exception_Status
163
      */
164
-    public function replyEmail($itemId, $body, $saveInSent, $replaceMime)
165
+    public function replyEmail($itemId, $body, $saveInSent, $replaceMime, $clientId)
166
     {
167
+        if ($this->sentMailStatus($clientId) === self::MAIL_DONE) {
168
+            throw new Syncroton_Exception_Status(Syncroton_Exception_Status::MESSAGE_PREVIOUSLY_SENT);
169
+        }
170
+
171
         $msg     = $this->parseMessageId($itemId);
172
         $message = $this->getObject($itemId);
173
 
174
@@ -944,7 +984,7 @@
175
         }
176
 
177
         // Send message
178
-        $this->sendEmail($sync_msg, $saveInSent);
179
+        $this->sendEmail($sync_msg, $saveInSent, $clientId);
180
 
181
         // Set ANSWERED flag on the replied message
182
         if (empty($message->headers->flags'ANSWERED')) {
183
@@ -1568,4 +1608,27 @@
184
             return kolab_sync_message::fake_message($headers, $msg);
185
         }
186
     }
187
+
188
+    /**
189
+     * Check in the cache if specified message (client-id) has been previously processed
190
+     * and with what result. It's used to prevent a duplicate submission.
191
+     */
192
+    protected function sentMailStatus($clientId, &$cache = null, &$cache_key = null)
193
+    {
194
+        // Note: ClientId is set with ActiveSync version >= 14.0
195
+        if ($clientId === null || $clientId === '') {
196
+            return 0;
197
+        }
198
+
199
+        $engine = kolab_sync::get_instance();
200
+        $status = null;
201
+        $cache_key = "ClientId:{$clientId}";
202
+
203
+        if ($cache_type = $engine->config->get('activesync_cache', 'db')) {
204
+            $cache = $engine->get_cache('activesync_cache', $cache_type, '1d', false);
205
+            $status = $cache->get($cache_key);
206
+        }
207
+
208
+        return (int) $status;
209
+    }
210
 }
211
kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_message.php Changed
68
 
1
@@ -216,13 +216,21 @@
2
         unset($smtp_headers'Bcc');
3
 
4
         // send message
5
-        if (!is_object($rcube->smtp)) {
6
-            $rcube->smtp_init(true);
7
-        }
8
+        if (isset($headers'X-Syncroton-Test')
9
+            && preg_match('/smtp=(true|false)/i', $headers'X-Syncroton-Test', $m)
10
+        ) {
11
+            $sent = $m1 == 'true';
12
+            $smtp_response = ;
13
+            $smtp_error = 999;
14
+        } else {
15
+            if (!is_object($rcube->smtp)) {
16
+                $rcube->smtp_init(true);
17
+            }
18
 
19
-        $sent = $rcube->smtp->send_mail($headers'From', $recipients, $smtp_headers, $this->body, $smtp_opts);
20
-        $smtp_response = $rcube->smtp->get_response();
21
-        $smtp_error    = $rcube->smtp->get_error();
22
+            $sent = $rcube->smtp->send_mail($headers'From', $recipients, $smtp_headers, $this->body, $smtp_opts);
23
+            $smtp_response = $rcube->smtp->get_response();
24
+            $smtp_error    = $rcube->smtp->get_error();
25
+        }
26
 
27
         // log error
28
         if (!$sent) {
29
@@ -261,6 +269,38 @@
30
     }
31
 
32
     /**
33
+     * Save message in Sent folder
34
+     *
35
+     * @return bool True on success (or when the folder does not exist), False otherwise
36
+     */
37
+    public function saveInSent()
38
+    {
39
+        $engine = kolab_sync::get_instance();
40
+        $storage = $engine->get_storage();
41
+        $sent_folder = $engine->config->get('sent_mbox');
42
+
43
+        if (isset($this->headers'X-Syncroton-Test')
44
+            && preg_match('/imap=(true|false)/i', $this->headers'X-Syncroton-Test', $m)
45
+        ) {
46
+            return $m1 == 'true';
47
+        }
48
+
49
+        if (strlen($sent_folder) && $storage->folder_exists($sent_folder)) {
50
+            $source = $this->source();
51
+            $uid = $storage->save_message($sent_folder, $source, '', false, 'SEEN');
52
+
53
+            if (empty($uid)) {
54
+                rcube::raise_error('code' => 500, 'type' => 'imap',
55
+                    'line' => __LINE__, 'file' => __FILE__,
56
+                    'message' => "Failed to save message in {$sent_folder}", true, false);
57
+                return false;
58
+            }
59
+        }
60
+
61
+        return true;
62
+    }
63
+
64
+    /**
65
      * Parses the message source and fixes 8bit data for ActiveSync.
66
      * This way any not UTF8 characters will be encoded before
67
      * sending to the device.
68
kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_timezone_converter.php Changed
18
 
1
@@ -40,15 +40,8 @@
2
     protected $_startDate = ;
3
 
4
     /**
5
-     * If set then the timezone guessing results will be cached.
6
-     * This is strongly recommended for performance reasons.
7
-     *
8
-     * @var rcube_cache
9
-     */
10
-    protected $cache = null;
11
-
12
-    /**
13
      * array of offsets known by ActiceSync clients, but unknown by php
14
+     *
15
      * @var array
16
      */
17
     protected $_knownTimezones = 
18
kolab-syncroton-2.4.2.tar.gz/tests/Sync/SendMailTest.php Added
114
 
1
@@ -0,0 +1,112 @@
2
+<?php
3
+
4
+class SendMailTest extends Tests\SyncTestCase
5
+{
6
+    /**
7
+     * Test SendMail command
8
+     */
9
+    public function testSendMail()
10
+    {
11
+        $this->emptyTestFolder('Sent', 'mail');
12
+
13
+        $clientId = microtime();
14
+
15
+        $request = <<<EOF
16
+            <?xml version="1.0" encoding="utf-8"?>
17
+            <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
18
+            <SendMail xmlns="uri:ComposeMail">
19
+                <ClientId>{$clientId}</ClientId>
20
+                <SaveInSentItems />
21
+                <Mime>From: testuser1@kolab.org
22
+            To: testuser2@kolab.org
23
+            Subject: Test
24
+            MIME-Version: 1.0
25
+            Content-Type: text/plain; charset="iso-8859-1"
26
+            Message-ID: &lt;msg1@kolab.org&gt;
27
+            X-Syncroton-Test: smtp=true
28
+
29
+            This is the email body content.</Mime>
30
+            </SendMail>
31
+            EOF;
32
+
33
+        $response = $this->request($request, 'SendMail');
34
+
35
+        $this->assertEquals(200, $response->getStatusCode());
36
+        $this->assertSame('', (string) $response->getBody());
37
+        $emails = $this->listEmails('Sent', '*');
38
+        $this->assertCount(1, $emails);
39
+        // TODO: Assert mail content
40
+    }
41
+
42
+    /**
43
+     * Test SendMail command
44
+     */
45
+    public function testSendMailErrorHandling()
46
+    {
47
+        $this->emptyTestFolder('Sent', 'mail');
48
+
49
+        $clientId = microtime();
50
+
51
+        $request = <<<EOF
52
+            <?xml version="1.0" encoding="utf-8"?>
53
+            <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
54
+            <SendMail xmlns="uri:ComposeMail">
55
+                <ClientId>{$clientId}</ClientId>
56
+                <SaveInSentItems />
57
+                <Mime>From: testuser1@kolab.org
58
+            To: testuser2@kolab.org
59
+            Subject: Test
60
+            MIME-Version: 1.0
61
+            Content-Type: text/plain; charset="iso-8859-1"
62
+            Message-ID: &lt;msg1@kolab.org&gt;
63
+            X-Syncroton-Test: smtp=false imap=true
64
+
65
+            This is the email body content.</Mime>
66
+            </SendMail>
67
+            EOF;
68
+
69
+        // Expect a SMTP error
70
+        $response = $this->request($request, 'SendMail');
71
+
72
+        $dom = $this->fromWbxml($response->getBody());
73
+        $xpath = $this->xpath($dom);
74
+
75
+        $this->assertEquals(200, $response->getStatusCode());
76
+        $this->assertSame('120', $xpath->query("//ns:SendMail/ns:Status")->item(0)->nodeValue);
77
+        $this->assertCount(0, $this->listEmails('Sent', '*'));
78
+
79
+        // Test IMAP error handling
80
+        $request = str_replace('smtp=false', 'smtp=true', $request);
81
+        $request = str_replace('imap=true', 'imap=false', $request);
82
+
83
+        $response = $this->request($request, 'SendMail');
84
+
85
+        $dom = $this->fromWbxml($response->getBody());
86
+        $xpath = $this->xpath($dom);
87
+
88
+        $this->assertEquals(200, $response->getStatusCode());
89
+        $this->assertSame('110', $xpath->query("//ns:SendMail/ns:Status")->item(0)->nodeValue);
90
+        $this->assertCount(0, $this->listEmails('Sent', '*'));
91
+
92
+        // Test no error
93
+        // smtp=false would cause error, but the submission should get skipped now
94
+        $request = str_replace('smtp=true', 'smtp=false', $request);
95
+        $request = str_replace('imap=false', '', $request);
96
+
97
+        $response = $this->request($request, 'SendMail');
98
+
99
+        $this->assertEquals(200, $response->getStatusCode());
100
+        $this->assertSame('', (string) $response->getBody());
101
+        $this->assertCount(1, $this->listEmails('Sent', '*'));
102
+
103
+        // Send the same mail again, expect an error
104
+        $response = $this->request($request, 'SendMail');
105
+
106
+        $dom = $this->fromWbxml($response->getBody());
107
+        $xpath = $this->xpath($dom);
108
+
109
+        $this->assertEquals(200, $response->getStatusCode());
110
+        $this->assertSame('118', $xpath->query("//ns:SendMail/ns:Status")->item(0)->nodeValue);
111
+        $this->assertCount(1, $this->listEmails('Sent', '*'));
112
+    }
113
+}
114
kolab-syncroton-2.4.2.tar.gz/tests/Sync/Sync/EmailTest.php Changed
74
 
1
@@ -112,29 +112,7 @@
2
         $this->assertSame('test sync', $xpath->query("{$root}/ns:ApplicationData/Email:Subject")->item(0)->nodeValue);
3
 
4
         // List the rest of the mail
5
-        $request = <<<EOF
6
-            <?xml version="1.0" encoding="utf-8"?>
7
-            <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
8
-            <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase">
9
-                <Collections>
10
-                    <Collection>
11
-                        <SyncKey>{$syncKey}</SyncKey>
12
-                        <CollectionId>{$folderId}</CollectionId>
13
-                        <DeletesAsMoves>1</DeletesAsMoves>
14
-                        <GetChanges>1</GetChanges>
15
-                        <Options>
16
-                            <FilterType>0</FilterType>
17
-                            <Conflict>1</Conflict>
18
-                            <BodyPreference xmlns="uri:AirSyncBase">
19
-                                <Type>2</Type>
20
-                                <TruncationSize>51200</TruncationSize>
21
-                                <AllOrNone>0</AllOrNone>
22
-                            </BodyPreference>
23
-                        </Options>
24
-                    </Collection>
25
-                </Collections>
26
-            </Sync>
27
-            EOF;
28
+        $request = preg_replace('|<SyncKey>\d</SyncKey>|', "<SyncKey>{$syncKey}</SyncKey>", $request);
29
 
30
         $response = $this->request($request, 'Sync');
31
 
32
@@ -155,6 +133,41 @@
33
         $this->assertStringMatchesFormat("{$folderId}::%d", $xpath->query("{$root}/ns:ServerId")->item(0)->nodeValue);
34
         $this->assertSame('sync test with attachment', $xpath->query("{$root}/ns:ApplicationData/Email:Subject")->item(0)->nodeValue);
35
         $this->assertSame(1, $xpath->query("{$root}/ns:ApplicationData/AirSyncBase:Body")->count());
36
+        $attachments = $xpath->query("{$root}/ns:ApplicationData/AirSyncBase:Attachments/AirSyncBase:Attachment");
37
+        $this->assertSame(3, $attachments->count());
38
+        $att = $attachments->item(0);
39
+        $this->assertSame('message.eml', $xpath->query('AirSyncBase:DisplayName', $att)->item(0)->nodeValue);
40
+        $this->assertSame('5', $xpath->query('AirSyncBase:Method', $att)->item(0)->nodeValue);
41
+        $this->assertSame('63', $xpath->query('AirSyncBase:EstimatedDataSize', $att)->item(0)->nodeValue);
42
+        $this->assertSame(0, $xpath->query('AirSyncBase:IsInline', $att)->count());
43
+        $att = $attachments->item(1);
44
+        $this->assertSame('logo1.gif', $xpath->query('AirSyncBase:DisplayName', $att)->item(0)->nodeValue);
45
+        $this->assertSame('1', $xpath->query('AirSyncBase:Method', $att)->item(0)->nodeValue);
46
+        $this->assertSame('76', $xpath->query('AirSyncBase:EstimatedDataSize', $att)->item(0)->nodeValue);
47
+        $this->assertSame(0, $xpath->query('AirSyncBase:IsInline', $att)->count());
48
+        $att = $attachments->item(2);
49
+        $this->assertSame('logo2.gif', $xpath->query('AirSyncBase:DisplayName', $att)->item(0)->nodeValue);
50
+        $this->assertSame('1', $xpath->query('AirSyncBase:Method', $att)->item(0)->nodeValue);
51
+        $this->assertSame('76', $xpath->query('AirSyncBase:EstimatedDataSize', $att)->item(0)->nodeValue);
52
+        $this->assertSame('1', $xpath->query('AirSyncBase:IsInline', $att)->item(0)->nodeValue);
53
+        $this->assertSame('foo4foo1@bar.net', $xpath->query('AirSyncBase:ContentId', $att)->item(0)->nodeValue);
54
+        $this->assertSame('fiction1/fiction2', $xpath->query('AirSyncBase:ContentLocation', $att)->item(0)->nodeValue);
55
+
56
+        // Test the empty Sync response
57
+        $request = preg_replace('|<SyncKey>\d</SyncKey>|', "<SyncKey>{$syncKey}</SyncKey>", $request);
58
+
59
+        $response = $this->request($request, 'Sync');
60
+
61
+        // According to https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-ascmd/b4b366a5-7dfb-45a9-a256-af8fa7c53400
62
+        // the empty response should look like this
63
+        //
64
+        // HTTP/1.1 200 OK
65
+        // Date: Fri, 10 Apr 2009 20:32:39 GMT
66
+        // Content-Length: 0
67
+
68
+        $this->assertSame(200, $response->getStatusCode());
69
+        $this->assertSame('', (string) $response->getBody());
70
+        $this->assertSame('0', $response->getHeader('Content-Length'));
71
 
72
         return $syncKey;
73
     }
74
kolab-syncroton-2.4.2.tar.gz/tests/SyncTestCase.php Changed
27
 
1
@@ -294,7 +294,7 @@
2
 
3
         // Make sure user is authenticated
4
         $this->getImapStorage();
5
-        if (!empty($sync->user)) {
6
+        if ($sync->user) {
7
             // required e.g. for DAV client cache use
8
             \rcube::get_instance()->user = $sync->user;
9
         }
10
@@ -418,6 +418,7 @@
11
         $xpath->registerNamespace("AirSyncBase", "uri:AirSyncBase");
12
         $xpath->registerNamespace("Calendar", "uri:Calendar");
13
         $xpath->registerNamespace("Contacts", "uri:Contacts");
14
+        $xpath->registerNamespace("ComposeMail", "uri:ComposeMail");
15
         $xpath->registerNamespace("Email", "uri:Email");
16
         $xpath->registerNamespace("Email2", "uri:Email2");
17
         $xpath->registerNamespace("Settings", "uri:Settings");
18
@@ -443,7 +444,7 @@
19
         if (method_exists("PHPUnit\Framework\TestCase", "assertMatchesRegularExpression")) {
20
             parent::assertMatchesRegularExpression($arg1, $arg2, $message);
21
         } else {
22
-            parent::assertRegExp($arg1, $arg2);
23
+            parent::assertRegExp($arg1, $arg2, $message);
24
         }
25
     }
26
 }
27
kolab-syncroton-2.4.2.tar.gz/tests/src/mail.sync2 Changed
29
 
1
@@ -12,9 +12,25 @@
2
 
3
 ZWVl
4
 --BOUNDARY
5
+Content-Type: message/rfc822
6
+Content-Disposition: attachment; filename=message.eml
7
+
8
+Subject: Forwarded
9
+From: kolab@domain.tld
10
+
11
+Forwarded message
12
+--BOUNDARY
13
+Content-Transfer-Encoding: base64
14
+Content-Type: image/gif; name=logo1.gif
15
+Content-Disposition: attachment; filename=logo1.gif; size=2574
16
+
17
+/9j/4AAQSkZJRgABAgEASABIAAD/4QqARXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUA
18
+--BOUNDARY
19
 Content-Transfer-Encoding: base64
20
-Content-Type: image/jpeg; name=logo.gif
21
-Content-Disposition: inline; filename=logo.gif; size=2574
22
+Content-Type: image/gif; name=logo2.gif
23
+Content-Disposition: inline; filename=logo2.gif; size=2574
24
+Content-ID: <foo4foo1@bar.net>
25
+Content-Location: fiction1/fiction2
26
 
27
 /9j/4AAQSkZJRgABAgEASABIAAD/4QqARXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUA
28
 --BOUNDARY--
29
kolab-syncroton.dsc Changed
10
 
1
@@ -2,7 +2,7 @@
2
 Source: kolab-syncroton
3
 Binary: kolab-syncroton
4
 Architecture: all
5
-Version: 1:2.4.2.39-1~kolab1
6
+Version: 1:2.4.2.41-1~kolab1
7
 Maintainer: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com>
8
 Uploaders: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com>
9
 Homepage: http://www.kolab.org/
10