summaryrefslogtreecommitdiff
path: root/lib/kolab_sync_data_contacts.php
blob: e348474d346e411873808c9bb82fdae9a0ddd9cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
<?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>                      |
 +--------------------------------------------------------------------------+
*/

/**
 * COntacts data class for Syncroton
 */
class kolab_sync_data_contacts extends kolab_sync_data
{
    /**
     * Mapping from ActiveSync Contacts namespace fields
     */
    protected $mapping = array(
        'anniversary'           => 'anniversary',
        'assistantName'         => 'assistant:0',
        //'assistantPhoneNumber' => 'assistantphonenumber',
        'birthday'              => 'birthday',
        'body'                  => 'notes',
        'businessAddressCity'          => 'address.work.locality',
        'businessAddressCountry'       => 'address.work.country',
        'businessAddressPostalCode'    => 'address.work.code',
        'businessAddressState'         => 'address.work.region',
        'businessAddressStreet'        => 'address.work.street',
        'businessFaxNumber'     => 'phone.workfax.number',
        'businessPhoneNumber'   => 'phone.work.number',
        'carPhoneNumber'        => 'phone.car.number',
        //'categories'            => 'categories',
        'children'              => 'children',
        'companyName'           => 'organization',
        'department'            => 'department',
        //'email1Address'         => 'email:0',
        //'email2Address'         => 'email:1',
        //'email3Address'         => 'email:2',
        //'fileAs'                => 'fileas', //@TODO: ?
        'firstName'             => 'firstname',
        //'home2PhoneNumber'      => 'home2phonenumber',
        'homeAddressCity'       => 'address.home.locality',
        'homeAddressCountry'    => 'address.home.country',
        'homeAddressPostalCode' => 'address.home.code',
        'homeAddressState'      => 'address.home.region',
        'homeAddressStreet'     => 'address.home.street',
        'homeFaxNumber'         => 'phone.homefax.number',
        'homePhoneNumber'       => 'phone.home.number',
        'jobTitle'              => 'jobtitle',
        'lastName'              => 'surname',
        'middleName'            => 'middlename',
        'mobilePhoneNumber'     => 'phone.mobile.number',
        //'officeLocation'        => 'officelocation',
        'otherAddressCity'      => 'address.office.locality',
        'otherAddressCountry'   => 'address.office.country',
        'otherAddressPostalCode' => 'address.office.code',
        'otherAddressState'     => 'address.office.region',
        'otherAddressStreet'    => 'address.office.street',
        'pagerNumber'           => 'phone.pager.number',
        'picture'               => 'photo',
        //'radioPhoneNumber'      => 'radiophonenumber',
        //'rtf'                   => 'rtf',
        'spouse'                => 'spouse',
        'suffix'                => 'suffix',
        'title'                 => 'prefix',
        'webPage'               => 'website.homepage.url',
        //'yomiCompanyName'       => 'yomicompanyname',
        //'yomiFirstName'         => 'yomifirstname',
        //'yomiLastName'          => 'yomilastname',
        // Mapping from ActiveSync Contacts2 namespace fields
        //'accountName'           => 'accountname',
        //'companyMainPhone'      => 'companymainphone',
        //'customerId'            => 'customerid',
        //'governmentId'          => 'governmentid',
        'iMAddress'             => 'im:0',
        'iMAddress2'            => 'im:1',
        'iMAddress3'            => 'im:2',
        'managerName'           => 'manager:0',
        //'mMS'                   => 'mms',
        'nickName'              => 'nickname',
    );

    /**
     * Kolab object type
     *
     * @var string
     */
    protected $modelName = 'contact';

    /**
     * Type of the default folder
     *
     * @var int
     */
    protected $defaultFolderType = Syncroton_Command_FolderSync::FOLDERTYPE_CONTACT;

    /**
     * Default container for new entries
     *
     * @var string
     */
    protected $defaultFolder = 'Contacts';

    /**
     * Type of user created folders
     *
     * @var int
     */
    protected $folderType = Syncroton_Command_FolderSync::FOLDERTYPE_CONTACT_USER_CREATED;


    /**
     * Creates model object
     *
     * @param Syncroton_Model_SyncCollection $collection Collection data
     * @param string                         $serverId   Local entry identifier
     */
    public function getEntry(Syncroton_Model_SyncCollection $collection, $serverId)
    {
        $data   = is_array($serverId) ? $serverId : $this->getObject($collection->collectionId, $serverId);
        $result = array();

        // Contacts namespace fields
        foreach ($this->mapping as $key => $name) {
            $value = $this->getKolabDataItem($data, $name);

            switch ($name) {
            case 'photo':
                if ($value) {
                    // ActiveSync limits photo size to 48KB (of base64 encoded string)
                    if (strlen($value) * 1.33 > 48 * 1024) {
                        continue;
                    }
                }
                break;

            case 'birthday':
            case 'anniversary':
                $value = self::date_from_kolab($value);
                break;

            case 'notes':
                $value = $this->body_from_kolab($value, $collection);
                break;
            }

            if (empty($value) || is_array($value)) {
                continue;
            }

            $result[$key] = $value;
        }

        // email address(es): email1Address, email2Address, email3Address
        for ($x=0; $x<3; $x++) {
            if (!empty($data['email'][$x]) && !empty($data['email'][$x]['address'])) {
                $result['email' . ($x+1) . 'Address'] = $data['email'][$x]['address'];
            }
        }

        return new Syncroton_Model_Contact($result);
    }

    /**
     * convert contact from xml to libkolab array
     *
     * @param Syncroton_Model_IEntry $data     Contact to convert
     * @param string                 $folderId Folder identifier
     * @param array                  $entry    Existing entry
     *
     * @return array Kolab object array
     */
    public function toKolab(Syncroton_Model_IEntry $data, $folderId, $entry = null)
    {
        $contact = !empty($entry) ? $entry : array();

        // Contacts namespace fields
        foreach ($this->mapping as $key => $name) {
            $value = $data->$key;

            switch ($name) {
            case 'address.work.street':
                if (strtolower($this->device->devicetype) == 'palm') {
                    // palm pre sends the whole address in the <Contacts:BusinessStreet> tag
                    $value = null;
                }
                break;

            case 'website.homepage.url':
                // remove facebook urls
                if (preg_match('/^fb:\/\//', $value)) {
                    $value = null;
                }
                break;

            case 'notes':
                $value = $this->getBody($value, Syncroton_Model_EmailBody::TYPE_PLAINTEXT);
                // If note isn't specified keep old note
                if ($value === null) {
                    continue 2;
                }
                break;

            case 'photo':
                // If photo isn't specified keep old photo
                if ($value === null) {
                    continue 2;
                }
                break;

            case 'birthday':
            case 'anniversary':
                if ($value) {
                    // convert date to string format, so libkolab will store
                    // it with no time and timezone what could be incorrectly re-calculated (#2555)
                    $value = $value->format('Y-m-d');
                }
                break;
            }

            $this->setKolabDataItem($contact, $name, $value);
        }

        // email address(es): email1Address, email2Address, email3Address
        $emails = array();
        for ($x=0; $x<3; $x++) {
            $key = 'email' . ($x+1) . 'Address';
            if ($value = $data->$key) {
                // Android sends email address as: Lars Kneschke <l.kneschke@metaways.de>
                if (preg_match('/(.*)<(.+@[^@]+)>/', $value, $matches)) {
                    $value = trim($matches[2]);
                }

                // sanitize email address, it can contain broken (non-unicode) characters (#3287)
                $value = rcube_charset::clean($value);

                // try to find address type, at least we can do this if
                // address wasn't changed
                $type = '';
                foreach ((array)$contact['email'] as $email) {
                    if ($email['address'] == $value) {
                        $type = $email['type'];
                    }
                }
                $emails[] = array('address' => $value, 'type' => $type);
            }
        }
        $contact['email'] = $emails;

        return $contact;
    }

    /**
     * Returns filter query array according to specified ActiveSync FilterType
     *
     * @param int $filter_type Filter type
     *
     * @param array Filter query
     */
    protected function filter($filter_type = 0)
    {
        // specify object type, contact folders in Kolab might
        // contain also ditribution-list objects, we'll skip them
        return array(array('type', '=', $this->modelName));
    }

}