Passed
Push — master ( 8dcc68...34e8da )
by
unknown
23:44 queued 20:40
created

MAPIProvider::setContact()   F

Complexity

Conditions 28
Paths > 20000

Size

Total Lines 124
Code Lines 76

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 28
eloc 76
nc 121856
nop 2
dl 0
loc 124
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * SPDX-License-Identifier: AGPL-3.0-only
4
 * SPDX-FileCopyrightText: Copyright 2007-2016 Zarafa Deutschland GmbH
5
 * SPDX-FileCopyrightText: Copyright 2020-2022 grommunio GmbH
6
 */
7
8
class MAPIProvider {
9
    private $session;
10
    private $store;
11
    private $zRFC822;
12
    private $addressbook;
13
    private $storeProps;
14
    private $inboxProps;
15
    private $rootProps;
16
    private $specialFoldersData;
17
18
    /**
19
     * Constructor of the MAPI Provider
20
     * Almost all methods of this class require a MAPI session and/or store
21
     *
22
     * @param resource         $session
23
     * @param resource         $store
24
     *
25
     * @access public
26
     */
27
    function __construct($session, $store) {
28
        $this->session = $session;
29
        $this->store = $store;
30
    }
31
32
33
    /**----------------------------------------------------------------------------------------------------------
34
     * GETTER
35
     */
36
37
    /**
38
     * Reads a message from MAPI
39
     * Depending on the message class, a contact, appointment, task or email is read
40
     *
41
     * @param mixed             $mapimessage
42
     * @param ContentParameters $contentparameters
43
     *
44
     * @access public
45
     * @return SyncObject
46
     */
47
    public function GetMessage($mapimessage, $contentparameters) {
48
        // Gets the Sync object from a MAPI object according to its message class
49
50
        $props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

50
        $props = /** @scrutinizer ignore-call */ mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS));
Loading history...
51
        if(isset($props[PR_MESSAGE_CLASS]))
52
            $messageclass = $props[PR_MESSAGE_CLASS];
53
        else
54
            $messageclass = "IPM";
55
56
        if(strpos($messageclass,"IPM.Contact") === 0)
57
            return $this->getContact($mapimessage, $contentparameters);
58
        else if(strpos($messageclass,"IPM.Appointment") === 0)
59
            return $this->getAppointment($mapimessage, $contentparameters);
60
        else if(strpos($messageclass,"IPM.Task") === 0 && strpos($messageclass, "IPM.TaskRequest") === false)
61
            return $this->getTask($mapimessage, $contentparameters);
62
        else if(strpos($messageclass,"IPM.StickyNote") === 0)
63
            return $this->getNote($mapimessage, $contentparameters);
64
        else
65
            return $this->getEmail($mapimessage, $contentparameters);
66
    }
67
68
    /**
69
     * Reads a contact object from MAPI
70
     *
71
     * @param mixed             $mapimessage
72
     * @param ContentParameters $contentparameters
73
     *
74
     * @access private
75
     * @return SyncContact
76
     */
77
    private function getContact($mapimessage, $contentparameters) {
78
        $message = new SyncContact();
79
80
        // Standard one-to-one mappings first
81
        $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetContactMapping());
82
83
        // Contact specific props
84
        $contactproperties = MAPIMapping::GetContactProperties();
85
        $messageprops = $this->getProps($mapimessage, $contactproperties);
86
87
        //set the body according to contentparameters and supported AS version
88
        $this->setMessageBody($mapimessage, $contentparameters, $message);
89
90
        //check the picture
91
        if (isset($messageprops[$contactproperties["haspic"]]) && $messageprops[$contactproperties["haspic"]]) {
92
            // Add attachments
93
            $attachtable = mapi_message_getattachmenttable($mapimessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_getattachmenttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

93
            $attachtable = /** @scrutinizer ignore-call */ mapi_message_getattachmenttable($mapimessage);
Loading history...
94
            mapi_table_restrict($attachtable, MAPIUtils::GetContactPicRestriction());
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

94
            /** @scrutinizer ignore-call */ 
95
            mapi_table_restrict($attachtable, MAPIUtils::GetContactPicRestriction());
Loading history...
95
            $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

95
            $rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE));
Loading history...
96
97
            foreach($rows as $row) {
98
                if(isset($row[PR_ATTACH_NUM])) {
99
                    $mapiattach = mapi_message_openattach($mapimessage, $row[PR_ATTACH_NUM]);
1 ignored issue
show
Bug introduced by
The function mapi_message_openattach was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

99
                    $mapiattach = /** @scrutinizer ignore-call */ mapi_message_openattach($mapimessage, $row[PR_ATTACH_NUM]);
Loading history...
100
                    $message->picture = base64_encode(mapi_attach_openbin($mapiattach, PR_ATTACH_DATA_BIN));
1 ignored issue
show
Bug introduced by
The function mapi_attach_openbin was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

100
                    $message->picture = base64_encode(/** @scrutinizer ignore-call */ mapi_attach_openbin($mapiattach, PR_ATTACH_DATA_BIN));
Loading history...
101
                }
102
            }
103
        }
104
105
        return $message;
106
    }
107
108
    /**
109
     * Reads a task object from MAPI
110
     *
111
     * @param mixed             $mapimessage
112
     * @param ContentParameters $contentparameters
113
     *
114
     * @access private
115
     * @return SyncTask
116
     */
117
    private function getTask($mapimessage, $contentparameters) {
118
        $message = new SyncTask();
119
120
        // Standard one-to-one mappings first
121
        $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetTaskMapping());
122
123
        // Task specific props
124
        $taskproperties = MAPIMapping::GetTaskProperties();
125
        $messageprops = $this->getProps($mapimessage, $taskproperties);
126
127
        //set the body according to contentparameters and supported AS version
128
        $this->setMessageBody($mapimessage, $contentparameters, $message);
129
130
        //task with deadoccur is an occurrence of a recurring task and does not need to be handled as recurring
131
        //webaccess does not set deadoccur for the initial recurring task
132
        if(isset($messageprops[$taskproperties["isrecurringtag"]]) &&
133
            $messageprops[$taskproperties["isrecurringtag"]] &&
134
            (!isset($messageprops[$taskproperties["deadoccur"]]) ||
135
            (isset($messageprops[$taskproperties["deadoccur"]]) &&
136
            !$messageprops[$taskproperties["deadoccur"]]))) {
137
            // Process recurrence
138
            $message->recurrence = new SyncTaskRecurrence();
139
            $this->getRecurrence($mapimessage, $messageprops, $message, $message->recurrence, false);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type array expected by parameter $tz of MAPIProvider::getRecurrence(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

139
            $this->getRecurrence($mapimessage, $messageprops, $message, $message->recurrence, /** @scrutinizer ignore-type */ false);
Loading history...
140
        }
141
142
        // when set the task to complete using the WebAccess, the dateComplete property is not set correctly
143
        if ($message->complete == 1 && !isset($message->datecompleted))
144
            $message->datecompleted = time();
145
146
        // if no reminder is set, announce that to the mobile
147
        if (!isset($message->reminderset))
148
            $message->reminderset = 0;
149
150
        return $message;
151
    }
152
153
    /**
154
     * Reads an appointment object from MAPI
155
     *
156
     * @param mixed             $mapimessage
157
     * @param ContentParameters $contentparameters
158
     *
159
     * @access private
160
     * @return SyncAppointment
161
     */
162
    private function getAppointment($mapimessage, $contentparameters) {
163
        $message = new SyncAppointment();
164
165
        // Standard one-to-one mappings first
166
        $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetAppointmentMapping());
167
168
        // Appointment specific props
169
        $appointmentprops = MAPIMapping::GetAppointmentProperties();
170
        $messageprops = $this->getProps($mapimessage, $appointmentprops);
171
172
        //set the body according to contentparameters and supported AS version
173
        $this->setMessageBody($mapimessage, $contentparameters, $message);
174
175
        // Set reminder time if reminderset is true
176
        if(isset($messageprops[$appointmentprops["reminderset"]]) && $messageprops[$appointmentprops["reminderset"]] == true) {
177
            if ($messageprops[$appointmentprops["remindertime"]] == 0x5AE980E1)
178
                $message->reminder = 15;
179
            else
180
                $message->reminder = $messageprops[$appointmentprops["remindertime"]];
181
        }
182
183
        if(!isset($message->uid))
184
            $message->uid = bin2hex($messageprops[$appointmentprops["sourcekey"]]);
185
        else
186
            $message->uid = Utils::GetICalUidFromOLUid($message->uid);
187
188
        // Always set organizer information because some devices do not work properly without it
189
        if( isset($messageprops[$appointmentprops["representingentryid"]]) &&
190
            isset($messageprops[$appointmentprops["representingname"]])) {
191
192
            $message->organizeremail = w2u($this->getSMTPAddressFromEntryID($messageprops[$appointmentprops["representingentryid"]]));
193
            // if the email address can't be resolved, fall back to PR_SENT_REPRESENTING_SEARCH_KEY
194
            if ($message->organizeremail == "" && isset($messageprops[$appointmentprops["sentrepresentinsrchk"]])) {
195
                $message->organizeremail = $this->getEmailAddressFromSearchKey($messageprops[$appointmentprops["sentrepresentinsrchk"]]);
196
            }
197
            $message->organizername = w2u($messageprops[$appointmentprops["representingname"]]);
198
        }
199
200
        $appTz = false; // if the appointment has some timezone information saved on the server
201
        if (!empty($messageprops[$appointmentprops["timezonetag"]])) {
202
            $tz = $this->getTZFromMAPIBlob($messageprops[$appointmentprops["timezonetag"]]);
203
            $appTz = true;
204
        }
205
        elseif (!empty($messageprops[$appointmentprops["timezonedesc"]])) {
206
            // Windows uses UTC in timezone description in opposite to mstzones in TimezoneUtil which uses GMT
207
            $wintz = str_replace("UTC", "GMT", $messageprops[$appointmentprops["timezonedesc"]]);
208
            $tz = TimezoneUtil::GetFullTZFromTZName(TimezoneUtil::GetTZNameFromWinTZ($wintz));
209
            $appTz = true;
210
        }
211
        else {
212
            // set server default timezone (correct timezone should be configured!)
213
            $tz = TimezoneUtil::GetFullTZ();
214
        }
215
216
        if(isset($messageprops[$appointmentprops["isrecurring"]]) && $messageprops[$appointmentprops["isrecurring"]]) {
217
            // Process recurrence
218
            $message->recurrence = new SyncRecurrence();
219
            $this->getRecurrence($mapimessage, $messageprops, $message, $message->recurrence, $tz);
220
221
            if (empty($message->alldayevent)) {
222
                $message->timezone = base64_encode(TimezoneUtil::GetSyncBlobFromTZ($tz));
223
            }
224
        }
225
226
        // Do attendees
227
        $reciptable = mapi_message_getrecipienttable($mapimessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_getrecipienttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

227
        $reciptable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($mapimessage);
Loading history...
228
        // Only get first 256 recipients, to prevent possible load issues.
229
        $rows = mapi_table_queryrows($reciptable, array(PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_ADDRTYPE, PR_RECIPIENT_TRACKSTATUS, PR_RECIPIENT_TYPE, PR_SEARCH_KEY), 0, 256);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

229
        $rows = /** @scrutinizer ignore-call */ mapi_table_queryrows($reciptable, array(PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_ADDRTYPE, PR_RECIPIENT_TRACKSTATUS, PR_RECIPIENT_TYPE, PR_SEARCH_KEY), 0, 256);
Loading history...
230
231
        // Exception: we do not synchronize appointments with more than 250 attendees
232
        if (count($rows) > 250) {
233
            $message->id = bin2hex($messageprops[$appointmentprops["sourcekey"]]);
234
            $mbe = new SyncObjectBrokenException("Appointment has too many attendees");
235
            $mbe->SetSyncObject($message);
236
            throw $mbe;
237
        }
238
239
        if(count($rows) > 0)
240
            $message->attendees = array();
241
242
        foreach($rows as $row) {
243
            $attendee = new SyncAttendee();
244
245
            $attendee->name = w2u($row[PR_DISPLAY_NAME]);
246
            //smtp address is always a proper email address
247
            if(isset($row[PR_SMTP_ADDRESS]))
248
                $attendee->email = w2u($row[PR_SMTP_ADDRESS]);
249
            elseif (isset($row[PR_ADDRTYPE]) && isset($row[PR_EMAIL_ADDRESS])) {
250
                //if address type is SMTP, it's also a proper email address
251
                if ($row[PR_ADDRTYPE] == "SMTP")
252
                    $attendee->email = w2u($row[PR_EMAIL_ADDRESS]);
253
                //if address type is ZARAFA, the PR_EMAIL_ADDRESS contains username
254
                elseif ($row[PR_ADDRTYPE] == "ZARAFA") {
255
                    $userinfo = @nsp_getuserinfo($row[PR_EMAIL_ADDRESS]);
1 ignored issue
show
Bug introduced by
The function nsp_getuserinfo was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

255
                    $userinfo = @/** @scrutinizer ignore-call */ nsp_getuserinfo($row[PR_EMAIL_ADDRESS]);
Loading history...
256
                    if (is_array($userinfo) && isset($userinfo["primary_email"])) {
257
                        $attendee->email = w2u($userinfo["primary_email"]);
258
                    }
259
                    // if the user was not found, do a fallback to PR_SEARCH_KEY
260
                    // @see https://jira.z-hub.io/browse/ZP-1178
261
                    elseif (isset($row[PR_SEARCH_KEY])) {
262
                        $attendee->email = w2u($this->getEmailAddressFromSearchKey($row[PR_SEARCH_KEY]));
263
                    }
264
                    else {
265
                        ZLog::Write(LOGLEVEL_WARN, sprintf("MAPIProvider->getAppointment: The attendee '%s' of type ZARAFA can not be resolved. Code: 0x%X", $row[PR_EMAIL_ADDRESS], mapi_last_hresult()));
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

265
                        ZLog::Write(LOGLEVEL_WARN, sprintf("MAPIProvider->getAppointment: The attendee '%s' of type ZARAFA can not be resolved. Code: 0x%X", $row[PR_EMAIL_ADDRESS], /** @scrutinizer ignore-call */ mapi_last_hresult()));
Loading history...
266
                    }
267
                }
268
            }
269
270
            //set attendee's status and type if they're available and if we are the organizer
271
            $storeprops = $this->GetStoreProps();
272
            if (isset($row[PR_RECIPIENT_TRACKSTATUS], $messageprops[$appointmentprops["representingentryid"]], $storeprops[PR_MAILBOX_OWNER_ENTRYID]) &&
273
                    $messageprops[$appointmentprops["representingentryid"]] == $storeprops[PR_MAILBOX_OWNER_ENTRYID]) {
274
275
                $attendee->attendeestatus = $row[PR_RECIPIENT_TRACKSTATUS];
276
            }
277
            if (isset($row[PR_RECIPIENT_TYPE])) {
278
                $attendee->attendeetype = $row[PR_RECIPIENT_TYPE];
279
            }
280
            // Some attendees have no email or name (eg resources), and if you
281
            // don't send one of those fields, the phone will give an error ... so
282
            // we don't send it in that case.
283
            // also ignore the "attendee" if the email is equal to the organizers' email
284
            if(isset($attendee->name) && isset($attendee->email) && $attendee->email != "" && (!isset($message->organizeremail) || (isset($message->organizeremail) && $attendee->email != $message->organizeremail)))
285
                array_push($message->attendees, $attendee);
286
        }
287
288
        // Status 0 = no meeting, status 1 = organizer, status 2/3/4/5 = tentative/accepted/declined/notresponded
289
        if(isset($messageprops[$appointmentprops["meetingstatus"]]) && $messageprops[$appointmentprops["meetingstatus"]] > 1) {
290
            if (!isset($message->attendees) || !is_array($message->attendees))
291
                $message->attendees = array();
292
            // Work around iOS6 cancellation issue when there are no attendees for this meeting. Just add ourselves as the sole attendee.
293
            if(count($message->attendees) == 0) {
294
295
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->getAppointment: adding ourself as an attendee for iOS6 workaround"));
296
                $attendee = new SyncAttendee();
297
298
                $meinfo = nsp_getuserinfo(Request::GetUser());
299
300
                if (is_array($meinfo)) {
301
                    $attendee->email = w2u($meinfo["primary_email"]);
302
                    $attendee->name = w2u($meinfo["fullname"]);
303
                    $attendee->attendeetype = MAPI_TO;
304
305
                    array_push($message->attendees, $attendee);
306
                }
307
            }
308
            $message->responsetype = $messageprops[$appointmentprops["responsestatus"]];
309
        }
310
311
        // If it's an appointment which doesn't have any attendees, we have to make sure that
312
        // the user is the owner or it will not work properly with android devices
313
        // @see https://jira.z-hub.io/browse/ZP-1020
314
        if(isset($messageprops[$appointmentprops["meetingstatus"]]) && $messageprops[$appointmentprops["meetingstatus"]] == olNonMeeting && empty($message->attendees)) {
315
            $meinfo = nsp_getuserinfo(Request::GetUser());
316
317
            if (is_array($meinfo)) {
318
                $message->organizeremail = w2u($meinfo["primary_email"]);
319
                $message->organizername = w2u($meinfo["fullname"]);
320
                ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->getAppointment(): setting ourself as the organizer for an appointment without attendees.");
321
            }
322
323
        }
324
325
        if (!isset($message->nativebodytype)) {
326
            $message->nativebodytype = MAPIUtils::GetNativeBodyType($messageprops);
327
        }
328
        elseif ($message->nativebodytype == SYNC_BODYPREFERENCE_UNDEFINED) {
329
            $nbt = MAPIUtils::GetNativeBodyType($messageprops);
330
            ZLog::Write(LOGLEVEL_INFO, sprintf("MAPIProvider->getAppointment(): native body type is undefined. Set it to %d.", $nbt));
331
            $message->nativebodytype = $nbt;
332
        }
333
334
        // If the user is working from a location other than the office the busystatus should be interpreted as free.
335
        if (isset($message->busystatus) && $message->busystatus == fbWorkingElsewhere) {
336
            $message->busystatus = fbFree;
337
        }
338
339
        // If the busystatus has the value of -1, we should be interpreted as tentative (1) / ZP-581
340
        if (isset($message->busystatus) && $message->busystatus == -1) {
341
            $message->busystatus = fbTentative;
342
        }
343
344
        // All-day events might appear as 24h (or multiple of it) long when they start not exactly at midnight (+/- bias of the timezone)
345
        if (isset($message->alldayevent) && $message->alldayevent) {
346
            $localStartTime = localtime($message->starttime, 1);
347
348
            // The appointment is all-day but doesn't start at midnight.
349
            // If it was created in another timezone and we have that information,
350
            // set the startime to the midnight of the current timezone.
351
            if ($appTz && ($localStartTime['tm_hour'] || $localStartTime['tm_min'])) {
352
                ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->getAppointment(): all-day event starting not midnight.");
353
                $duration = $message->endtime - $message->starttime;
354
                $serverTz = TimezoneUtil::GetFullTZ();
355
                $message->starttime = $this->getGMTTimeByTZ($this->getLocaltimeByTZ($message->starttime, $tz), $serverTz);
356
                $message->endtime = $message->starttime + $duration;
357
            }
358
        }
359
360
        return $message;
361
    }
362
363
    /**
364
     * Reads recurrence information from MAPI
365
     *
366
     * @param mixed             $mapimessage
367
     * @param array             $recurprops
368
     * @param SyncObject        &$syncMessage       the message
369
     * @param SyncObject        &$syncRecurrence    the  recurrence message
370
     * @param array             $tz                 timezone information
371
     *
372
     * @access private
373
     * @return
374
     */
375
    private function getRecurrence($mapimessage, $recurprops, &$syncMessage, &$syncRecurrence, $tz) {
376
        if ($syncRecurrence instanceof SyncTaskRecurrence)
377
            $recurrence = new TaskRecurrence($this->store, $mapimessage);
378
        else
379
            $recurrence = new Recurrence($this->store, $mapimessage);
380
381
        switch($recurrence->recur["type"]) {
382
            case 10: // daily
383
                switch($recurrence->recur["subtype"]) {
384
                    default:
385
                        $syncRecurrence->type = 0;
386
                        break;
387
                    case 1:
388
                        $syncRecurrence->type = 0;
389
                        $syncRecurrence->dayofweek = 62; // mon-fri
390
                        $syncRecurrence->interval = 1;
391
                        break;
392
                }
393
                break;
394
            case 11: // weekly
395
                    $syncRecurrence->type = 1;
396
                break;
397
            case 12: // monthly
398
                switch($recurrence->recur["subtype"]) {
399
                    default:
400
                        $syncRecurrence->type = 2;
401
                        break;
402
                    case 3:
403
                        $syncRecurrence->type = 3;
404
                        break;
405
                }
406
                break;
407
            case 13: // yearly
408
                switch($recurrence->recur["subtype"]) {
409
                    default:
410
                        $syncRecurrence->type = 4;
411
                        break;
412
                    case 2:
413
                        $syncRecurrence->type = 5;
414
                        break;
415
                    case 3:
416
                        $syncRecurrence->type = 6;
417
                }
418
        }
419
        // Termination
420
        switch($recurrence->recur["term"]) {
421
            case 0x21:
422
                $syncRecurrence->until = $recurrence->recur["end"];
423
                // fixes Mantis #350 : recur-end does not consider timezones - use ClipEnd if available
424
                if (isset($recurprops[$recurrence->proptags["enddate_recurring"]]))
425
                    $syncRecurrence->until = $recurprops[$recurrence->proptags["enddate_recurring"]];
426
                // add one day (minus 1 sec) to the end time to make sure the last occurrence is covered
427
                $syncRecurrence->until += 86399;
428
                break;
429
            case 0x22:
430
                $syncRecurrence->occurrences = $recurrence->recur["numoccur"]; break;
431
            case 0x23:
432
                // never ends
433
                break;
434
        }
435
436
        // Correct 'alldayevent' because outlook fails to set it on recurring items of 24 hours or longer
437
        if(isset($recurrence->recur["endocc"], $recurrence->recur["startocc"]) && ($recurrence->recur["endocc"] - $recurrence->recur["startocc"] >= 1440))
438
            $syncMessage->alldayevent = true;
439
440
        // Interval is different according to the type/subtype
441
        switch($recurrence->recur["type"]) {
442
            case 10:
443
                if($recurrence->recur["subtype"] == 0)
444
                    $syncRecurrence->interval = (int)($recurrence->recur["everyn"] / 1440);  // minutes
445
                break;
446
            case 11:
447
            case 12:
448
                $syncRecurrence->interval = $recurrence->recur["everyn"];
449
                break; // months / weeks
450
            case 13:
451
                $syncRecurrence->interval = (int)($recurrence->recur["everyn"] / 12);
452
                break; // months
453
        }
454
455
        if(isset($recurrence->recur["weekdays"]))
456
            $syncRecurrence->dayofweek = $recurrence->recur["weekdays"]; // bitmask of days (1 == sunday, 128 == saturday
457
        if(isset($recurrence->recur["nday"]))
458
            $syncRecurrence->weekofmonth = $recurrence->recur["nday"]; // N'th {DAY} of {X} (0-5)
459
        if(isset($recurrence->recur["month"]))
460
            $syncRecurrence->monthofyear = (int)($recurrence->recur["month"] / (60 * 24 * 29)) + 1; // works ok due to rounding. see also $monthminutes below (1-12)
461
        if(isset($recurrence->recur["monthday"]))
462
            $syncRecurrence->dayofmonth = $recurrence->recur["monthday"]; // day of month (1-31)
463
464
        // All changed exceptions are appointments within the 'exceptions' array. They contain the same items as a normal appointment
465
        foreach($recurrence->recur["changed_occurences"] as $change) {
466
            $exception = new SyncAppointmentException();
467
468
            // start, end, basedate, subject, remind_before, reminderset, location, busystatus, alldayevent, label
469
            if(isset($change["start"]))
470
                $exception->starttime = $this->getGMTTimeByTZ($change["start"], $tz);
471
            if(isset($change["end"]))
472
                $exception->endtime = $this->getGMTTimeByTZ($change["end"], $tz);
473
            if(isset($change["basedate"])) {
474
                $exception->exceptionstarttime = $this->getGMTTimeByTZ($this->getDayStartOfTimestamp($change["basedate"]) + $recurrence->recur["startocc"] * 60, $tz);
0 ignored issues
show
Bug introduced by
$this->getDayStartOfTime...>recur['startocc'] * 60 of type integer is incompatible with the type long expected by parameter $localtime of MAPIProvider::getGMTTimeByTZ(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

474
                $exception->exceptionstarttime = $this->getGMTTimeByTZ(/** @scrutinizer ignore-type */ $this->getDayStartOfTimestamp($change["basedate"]) + $recurrence->recur["startocc"] * 60, $tz);
Loading history...
475
476
                //open body because getting only property might not work because of memory limit
477
                $exceptionatt = $recurrence->getExceptionAttachment($change["basedate"]);
0 ignored issues
show
Bug introduced by
The method getExceptionAttachment() does not exist on TaskRecurrence. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

477
                /** @scrutinizer ignore-call */ 
478
                $exceptionatt = $recurrence->getExceptionAttachment($change["basedate"]);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
478
                if($exceptionatt) {
479
                    $exceptionobj = mapi_attach_openobj($exceptionatt, 0);
1 ignored issue
show
Bug introduced by
The function mapi_attach_openobj was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

479
                    $exceptionobj = /** @scrutinizer ignore-call */ mapi_attach_openobj($exceptionatt, 0);
Loading history...
480
                    $this->setMessageBodyForType($exceptionobj, SYNC_BODYPREFERENCE_PLAIN, $exception);
481
                }
482
            }
483
            if(isset($change["subject"]))
484
                $exception->subject = w2u($change["subject"]);
485
            if(isset($change["reminder_before"]) && $change["reminder_before"])
486
                $exception->reminder = $change["remind_before"];
487
            if(isset($change["location"]))
488
                $exception->location = w2u($change["location"]);
489
            if(isset($change["busystatus"]))
490
                $exception->busystatus = $change["busystatus"];
491
            if(isset($change["alldayevent"]))
492
                $exception->alldayevent = $change["alldayevent"];
493
494
            // set some data from the original appointment
495
            if (isset($syncMessage->uid))
496
                $exception->uid = $syncMessage->uid;
497
            if (isset($syncMessage->organizername))
498
                $exception->organizername = $syncMessage->organizername;
499
            if (isset($syncMessage->organizeremail))
500
                $exception->organizeremail = $syncMessage->organizeremail;
501
502
            if(!isset($syncMessage->exceptions))
503
                $syncMessage->exceptions = array();
504
505
            // If the user is working from a location other than the office the busystatus should be interpreted as free.
506
            if (isset($exception->busystatus) && $exception->busystatus == fbWorkingElsewhere) {
507
                $exception->busystatus = fbFree;
508
            }
509
510
            // If the busystatus has the value of -1, we should be interpreted as tentative (1) / ZP-581
511
            if (isset($exception->busystatus) && $exception->busystatus == -1) {
512
                $exception->busystatus = fbTentative;
513
            }
514
515
            // if an exception lasts 24 hours and the series are an allday events, set also the exception to allday event,
516
            // otherwise it will be a 24 hour long event on some mobiles.
517
            // @see https://jira.z-hub.io/browse/ZP-980
518
            if (isset($exception->starttime, $exception->endtime) && ($exception->endtime - $exception->starttime == 86400) && $syncMessage->alldayevent) {
519
                $exception->alldayevent = 1;
520
            }
521
            array_push($syncMessage->exceptions, $exception);
522
        }
523
524
        // Deleted appointments contain only the original date (basedate) and a 'deleted' tag
525
        foreach($recurrence->recur["deleted_occurences"] as $deleted) {
526
            $exception = new SyncAppointmentException();
527
528
            $exception->exceptionstarttime = $this->getGMTTimeByTZ($this->getDayStartOfTimestamp($deleted) + $recurrence->recur["startocc"] * 60, $tz);
529
            $exception->deleted = "1";
530
531
            if(!isset($syncMessage->exceptions))
532
                $syncMessage->exceptions = array();
533
534
            array_push($syncMessage->exceptions, $exception);
535
        }
536
537
        if (isset($syncMessage->complete) && $syncMessage->complete) {
538
            $syncRecurrence->complete = $syncMessage->complete;
539
        }
540
    }
541
542
    /**
543
     * Reads an email object from MAPI
544
     *
545
     * @param mixed             $mapimessage
546
     * @param ContentParameters $contentparameters
547
     *
548
     * @access private
549
     * @return SyncEmail
550
     */
551
    private function getEmail($mapimessage, $contentparameters) {
552
        // This workaround fixes ZP-729 and still works with Outlook.
553
        // FIXME: It should be properly fixed when refactoring.
554
        $bpReturnType = Utils::GetBodyPreferenceBestMatch($contentparameters->GetBodyPreference());
0 ignored issues
show
Bug introduced by
It seems like $contentparameters->GetBodyPreference() can also be of type false; however, parameter $bpTypes of Utils::GetBodyPreferenceBestMatch() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

554
        $bpReturnType = Utils::GetBodyPreferenceBestMatch(/** @scrutinizer ignore-type */ $contentparameters->GetBodyPreference());
Loading history...
555
        if (($contentparameters->GetMimeSupport() == SYNC_MIMESUPPORT_NEVER) ||
0 ignored issues
show
Bug introduced by
The method GetMimeSupport() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

555
        if (($contentparameters->/** @scrutinizer ignore-call */ GetMimeSupport() == SYNC_MIMESUPPORT_NEVER) ||
Loading history...
556
                ($key = array_search(SYNC_BODYPREFERENCE_MIME, $contentparameters->GetBodyPreference()) === false) ||
0 ignored issues
show
Bug introduced by
It seems like $contentparameters->GetBodyPreference() can also be of type false; however, parameter $haystack of array_search() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

556
                ($key = array_search(SYNC_BODYPREFERENCE_MIME, /** @scrutinizer ignore-type */ $contentparameters->GetBodyPreference()) === false) ||
Loading history...
Unused Code introduced by
The assignment to $key is dead and can be removed.
Loading history...
557
                $bpReturnType != SYNC_BODYPREFERENCE_MIME) {
558
            MAPIUtils::ParseSmime($this->session, $this->store, $this->getAddressbook(), $mapimessage);
0 ignored issues
show
Bug introduced by
$this->store of type resource is incompatible with the type MAPIStore expected by parameter $store of MAPIUtils::ParseSmime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

558
            MAPIUtils::ParseSmime($this->session, /** @scrutinizer ignore-type */ $this->store, $this->getAddressbook(), $mapimessage);
Loading history...
Bug introduced by
$this->session of type resource is incompatible with the type MAPISession expected by parameter $session of MAPIUtils::ParseSmime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

558
            MAPIUtils::ParseSmime(/** @scrutinizer ignore-type */ $this->session, $this->store, $this->getAddressbook(), $mapimessage);
Loading history...
559
        }
560
561
        $message = new SyncMail();
562
563
        $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetEmailMapping());
564
565
        $emailproperties = MAPIMapping::GetEmailProperties();
566
        $messageprops = $this->getProps($mapimessage, $emailproperties);
567
568
        if(isset($messageprops[PR_SOURCE_KEY]))
569
            $sourcekey = $messageprops[PR_SOURCE_KEY];
0 ignored issues
show
Unused Code introduced by
The assignment to $sourcekey is dead and can be removed.
Loading history...
570
        else {
571
            $mbe = new SyncObjectBrokenException("The message doesn't have a sourcekey");
572
            $mbe->SetSyncObject($message);
573
            throw $mbe;
574
        }
575
576
        //set the body according to contentparameters and supported AS version
577
        $this->setMessageBody($mapimessage, $contentparameters, $message);
578
579
        $fromname = $fromaddr = "";
580
581
        if(isset($messageprops[$emailproperties["representingname"]])) {
582
            // remove encapsulating double quotes from the representingname
583
            $fromname = preg_replace('/^\"(.*)\"$/',"\${1}", $messageprops[$emailproperties["representingname"]]);
584
        }
585
        if(isset($messageprops[$emailproperties["representingentryid"]]))
586
            $fromaddr = $this->getSMTPAddressFromEntryID($messageprops[$emailproperties["representingentryid"]]);
587
588
        // if the email address can't be resolved, fall back to PR_SENT_REPRESENTING_SEARCH_KEY
589
        if ($fromaddr == "" && isset($messageprops[$emailproperties["representingsearchkey"]])) {
590
            $fromaddr = $this->getEmailAddressFromSearchKey($messageprops[$emailproperties["representingsearchkey"]]);
591
        }
592
593
        if($fromname == $fromaddr)
594
            $fromname = "";
595
596
        if($fromname)
597
            $from = "\"" . w2u($fromname) . "\" <" . w2u($fromaddr) . ">";
0 ignored issues
show
Bug introduced by
Are you sure w2u($fromaddr) of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

597
            $from = "\"" . w2u($fromname) . "\" <" . /** @scrutinizer ignore-type */ w2u($fromaddr) . ">";
Loading history...
Bug introduced by
Are you sure w2u($fromname) of type false|mixed|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

597
            $from = "\"" . /** @scrutinizer ignore-type */ w2u($fromname) . "\" <" . w2u($fromaddr) . ">";
Loading history...
598
        else
599
            //START CHANGED dw2412 HTC shows "error" if sender name is unknown
600
            $from = "\"" . w2u($fromaddr) . "\" <" . w2u($fromaddr) . ">";
601
            //END CHANGED dw2412 HTC shows "error" if sender name is unknown
602
603
        $message->from = $from;
604
605
        // process Meeting Requests
606
        if(isset($message->messageclass) && strpos($message->messageclass, "IPM.Schedule.Meeting") === 0) {
607
            $message->meetingrequest = new SyncMeetingRequest();
608
            $this->getPropsFromMAPI($message->meetingrequest, $mapimessage, MAPIMapping::GetMeetingRequestMapping());
609
610
            $meetingrequestproperties = MAPIMapping::GetMeetingRequestProperties();
611
            $props = $this->getProps($mapimessage, $meetingrequestproperties);
612
613
            // Get the GOID
614
            if(isset($props[$meetingrequestproperties["goidtag"]]))
615
                $message->meetingrequest->globalobjid = base64_encode($props[$meetingrequestproperties["goidtag"]]);
616
617
            // Set Timezone
618
            if (isset($props[$meetingrequestproperties["timezonetag"]])) {
619
                $tz = $this->getTZFromMAPIBlob($props[$meetingrequestproperties["timezonetag"]]);
620
            }
621
            else {
622
                $tz = TimezoneUtil::GetFullTZ();
623
            }
624
625
            $message->meetingrequest->timezone = base64_encode(TimezoneUtil::GetSyncBlobFromTZ($tz));
626
627
            // send basedate if exception
628
            if(isset($props[$meetingrequestproperties["recReplTime"]]) ||
629
                (isset($props[$meetingrequestproperties["lidIsException"]]) && $props[$meetingrequestproperties["lidIsException"]] == true)) {
630
                if (isset($props[$meetingrequestproperties["recReplTime"]])){
631
                    $basedate = $props[$meetingrequestproperties["recReplTime"]];
632
                    $message->meetingrequest->recurrenceid = $this->getGMTTimeByTZ($basedate, $this->getGMTTZ());
633
                }
634
                else {
635
                    if (!isset($props[$meetingrequestproperties["goidtag"]]) || !isset($props[$meetingrequestproperties["recurStartTime"]]) || !isset($props[$meetingrequestproperties["timezonetag"]]))
636
                        ZLog::Write(LOGLEVEL_WARN, "Missing property to set correct basedate for exception");
637
                    else {
638
                        $basedate = Utils::ExtractBaseDate($props[$meetingrequestproperties["goidtag"]], $props[$meetingrequestproperties["recurStartTime"]]);
639
                        $message->meetingrequest->recurrenceid = $this->getGMTTimeByTZ($basedate, $tz);
640
                    }
641
                }
642
            }
643
644
            // Organizer is the sender
645
            if (strpos($message->messageclass, "IPM.Schedule.Meeting.Resp") === 0) {
646
                $message->meetingrequest->organizer = $message->to;
647
            }
648
            else {
649
                $message->meetingrequest->organizer = $message->from;
650
            }
651
652
            // Process recurrence
653
            if(isset($props[$meetingrequestproperties["isrecurringtag"]]) && $props[$meetingrequestproperties["isrecurringtag"]]) {
654
                $myrec = new SyncMeetingRequestRecurrence();
655
                // get recurrence -> put $message->meetingrequest as message so the 'alldayevent' is set correctly
656
                $this->getRecurrence($mapimessage, $props, $message->meetingrequest, $myrec, $tz);
657
                $message->meetingrequest->recurrences = array($myrec);
658
            }
659
660
            // Force the 'alldayevent' in the object at all times. (non-existent == 0)
661
            if(!isset($message->meetingrequest->alldayevent) || $message->meetingrequest->alldayevent == "")
662
                $message->meetingrequest->alldayevent = 0;
663
664
            // Instancetype
665
            // 0 = single appointment
666
            // 1 = master recurring appointment
667
            // 2 = single instance of recurring appointment
668
            // 3 = exception of recurring appointment
669
            $message->meetingrequest->instancetype = 0;
670
            if (isset($props[$meetingrequestproperties["isrecurringtag"]]) && $props[$meetingrequestproperties["isrecurringtag"]] == 1)
671
                $message->meetingrequest->instancetype = 1;
672
            else if ((!isset($props[$meetingrequestproperties["isrecurringtag"]]) || $props[$meetingrequestproperties["isrecurringtag"]] == 0 )&& isset($message->meetingrequest->recurrenceid))
673
                if (isset($props[$meetingrequestproperties["appSeqNr"]]) && $props[$meetingrequestproperties["appSeqNr"]] == 0 )
674
                    $message->meetingrequest->instancetype = 2;
675
                else
676
                    $message->meetingrequest->instancetype = 3;
677
678
            // Disable reminder if it is off
679
            if(!isset($props[$meetingrequestproperties["reminderset"]]) || $props[$meetingrequestproperties["reminderset"]] == false)
680
                $message->meetingrequest->reminder = "";
681
            //the property saves reminder in minutes, but we need it in secs
682
            else {
683
                ///set the default reminder time to seconds
684
                if ($props[$meetingrequestproperties["remindertime"]] == 0x5AE980E1)
685
                    $message->meetingrequest->reminder = 900;
686
                else
687
                    $message->meetingrequest->reminder = $props[$meetingrequestproperties["remindertime"]] * 60;
688
            }
689
690
            // Set sensitivity to 0 if missing
691
            if(!isset($message->meetingrequest->sensitivity))
692
                $message->meetingrequest->sensitivity = 0;
693
694
            // If the user is working from a location other than the office the busystatus should be interpreted as free.
695
            if (isset($message->meetingrequest->busystatus) && $message->meetingrequest->busystatus == fbWorkingElsewhere) {
696
                $message->meetingrequest->busystatus = fbFree;
697
            }
698
699
            // If the busystatus has the value of -1, we should be interpreted as tentative (1) / ZP-581
700
            if (isset($message->meetingrequest->busystatus) && $message->meetingrequest->busystatus == -1) {
701
                $message->meetingrequest->busystatus = fbTentative;
702
            }
703
704
            // if a meeting request response hasn't been processed yet,
705
            // do it so that the attendee status is updated on the mobile
706
            if(!isset($messageprops[$emailproperties["processed"]])) {
707
                // check if we are not sending the MR so we can process it - ZP-581
708
                $cuser = ZPush::GetBackend()->GetUserDetails(ZPush::GetBackend()->GetCurrentUsername());
709
                if(isset($cuser["emailaddress"]) && $cuser["emailaddress"] != $fromaddr) {
710
                    if (!isset($req)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $req seems to never exist and therefore isset should always be false.
Loading history...
711
                        $req = new Meetingrequest($this->store, $mapimessage, $this->session);
712
                    }
713
                    if ($req->isMeetingRequestResponse()) {
714
                        $req->processMeetingRequestResponse();
715
                    }
716
                    if ($req->isMeetingCancellation()) {
717
                        $req->processMeetingCancellation();
718
                    }
719
                }
720
            }
721
            $message->contentclass = DEFAULT_CALENDAR_CONTENTCLASS;
722
723
            // MeetingMessageType values
724
            // 0 = A silent update was performed, or the message type is unspecified.
725
            // 1 = Initial meeting request.
726
            // 2 = Full update.
727
            // 3 = Informational update.
728
            // 4 = Outdated. A newer meeting request or meeting update was received after this message.
729
            // 5 = Identifies the delegator's copy of the meeting request.
730
            // 6 = Identifies that the meeting request has been delegated and the meeting request cannot be responded to.
731
            $message->meetingrequest->meetingmessagetype = mtgEmpty;
732
733
            if (isset($props[$meetingrequestproperties["meetingType"]])) {
734
                switch ($props[$meetingrequestproperties["meetingType"]]) {
735
                    case mtgRequest:
736
                        $message->meetingrequest->meetingmessagetype = 1;
737
                        break;
738
                    case mtgFull:
739
                        $message->meetingrequest->meetingmessagetype = 2;
740
                        break;
741
                    case mtgInfo:
742
                        $message->meetingrequest->meetingmessagetype = 3;
743
                        break;
744
                    case mtgOutOfDate:
745
                        $message->meetingrequest->meetingmessagetype = 4;
746
                        break;
747
                    case mtgDelegatorCopy:
748
                        $message->meetingrequest->meetingmessagetype = 5;
749
                        break;
750
                }
751
            }
752
        }
753
754
        // Add attachments
755
        $attachtable = mapi_message_getattachmenttable($mapimessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_getattachmenttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

755
        $attachtable = /** @scrutinizer ignore-call */ mapi_message_getattachmenttable($mapimessage);
Loading history...
756
        $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

756
        $rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM));
Loading history...
757
        $entryid = bin2hex($messageprops[$emailproperties["entryid"]]);
758
        $parentSourcekey = bin2hex($messageprops[$emailproperties["parentsourcekey"]]);
759
760
        foreach($rows as $row) {
761
            if(isset($row[PR_ATTACH_NUM])) {
762
                if (Request::GetProtocolVersion() >= 12.0) {
763
                    $attach = new SyncBaseAttachment();
764
                }
765
                else {
766
                    $attach = new SyncAttachment();
767
                }
768
769
                $mapiattach = mapi_message_openattach($mapimessage, $row[PR_ATTACH_NUM]);
1 ignored issue
show
Bug introduced by
The function mapi_message_openattach was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

769
                $mapiattach = /** @scrutinizer ignore-call */ mapi_message_openattach($mapimessage, $row[PR_ATTACH_NUM]);
Loading history...
770
                $attachprops = mapi_getprops($mapiattach, array(PR_ATTACH_LONG_FILENAME, PR_ATTACH_FILENAME, PR_ATTACHMENT_HIDDEN, PR_ATTACH_CONTENT_ID, PR_ATTACH_CONTENT_ID_W, PR_ATTACH_MIME_TAG, PR_ATTACH_MIME_TAG_W, PR_ATTACH_METHOD, PR_DISPLAY_NAME, PR_DISPLAY_NAME_W, PR_ATTACH_SIZE, PR_ATTACH_FLAGS));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

770
                $attachprops = /** @scrutinizer ignore-call */ mapi_getprops($mapiattach, array(PR_ATTACH_LONG_FILENAME, PR_ATTACH_FILENAME, PR_ATTACHMENT_HIDDEN, PR_ATTACH_CONTENT_ID, PR_ATTACH_CONTENT_ID_W, PR_ATTACH_MIME_TAG, PR_ATTACH_MIME_TAG_W, PR_ATTACH_METHOD, PR_DISPLAY_NAME, PR_DISPLAY_NAME_W, PR_ATTACH_SIZE, PR_ATTACH_FLAGS));
Loading history...
771
                if ((isset($attachprops[PR_ATTACH_MIME_TAG]) && strpos(strtolower($attachprops[PR_ATTACH_MIME_TAG]), 'signed') !== false) ||
772
                    (isset($attachprops[PR_ATTACH_MIME_TAG_W]) && strpos(strtolower($attachprops[PR_ATTACH_MIME_TAG_W]), 'signed') !== false)) {
773
                    continue;
774
                }
775
776
                // the displayname is handled equally for all AS versions
777
                $attach->displayname = w2u((isset($attachprops[PR_ATTACH_LONG_FILENAME])) ? $attachprops[PR_ATTACH_LONG_FILENAME] : ((isset($attachprops[PR_ATTACH_FILENAME])) ? $attachprops[PR_ATTACH_FILENAME] : ((isset($attachprops[PR_DISPLAY_NAME])) ? $attachprops[PR_DISPLAY_NAME] : "attachment.bin")));
778
                // fix attachment name in case of inline images
779
                if (($attach->displayname == "inline.txt" && (isset($attachprops[PR_ATTACH_MIME_TAG]) || $attachprops[PR_ATTACH_MIME_TAG_W])) ||
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($attach->displayname ==...at', -4, 4, true) === 0, Probably Intended Meaning: $attach->displayname == ...t', -4, 4, true) === 0)
Loading history...
780
                (substr_compare($attach->displayname, "attachment", 0, 10, true) === 0 && substr_compare($attach->displayname, ".dat", -4, 4, true) === 0)) {
0 ignored issues
show
Bug introduced by
It seems like $attach->displayname can also be of type false; however, parameter $haystack of substr_compare() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

780
                (substr_compare(/** @scrutinizer ignore-type */ $attach->displayname, "attachment", 0, 10, true) === 0 && substr_compare($attach->displayname, ".dat", -4, 4, true) === 0)) {
Loading history...
781
                    $mimetype = (isset($attachprops[PR_ATTACH_MIME_TAG])) ? $attachprops[PR_ATTACH_MIME_TAG]:$attachprops[PR_ATTACH_MIME_TAG_W];
782
                    $mime = explode("/", $mimetype);
783
784
                    if (count($mime) == 2 && $mime[0] == "image") {
785
                        $attach->displayname = "inline." . $mime[1];
786
                    }
787
                }
788
789
                // set AS version specific parameters
790
                if (Request::GetProtocolVersion() >= 12.0) {
791
                    $attach->filereference = sprintf("%s:%s:%s", $entryid, $row[PR_ATTACH_NUM], $parentSourcekey);
0 ignored issues
show
Bug introduced by
The property filereference does not seem to exist on SyncAttachment.
Loading history...
792
                    $attach->method = (isset($attachprops[PR_ATTACH_METHOD])) ? $attachprops[PR_ATTACH_METHOD] : ATTACH_BY_VALUE;
0 ignored issues
show
Bug introduced by
The property method does not exist on SyncAttachment. Did you mean attmethod?
Loading history...
793
794
                    // if displayname does not have the eml extension for embedde messages, android and WP devices won't open it
795
                    if ($attach->method == ATTACH_EMBEDDED_MSG) {
796
                        if (strtolower(substr($attach->displayname, -4)) != '.eml')
797
                            $attach->displayname .= '.eml';
798
                    }
799
                    // android devices require attachment size in order to display an attachment properly
800
                    if (!isset($attachprops[PR_ATTACH_SIZE])) {
801
                        $stream = mapi_openproperty($mapiattach, PR_ATTACH_DATA_BIN, IID_IStream, 0, 0);
1 ignored issue
show
Bug introduced by
The function mapi_openproperty was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

801
                        $stream = /** @scrutinizer ignore-call */ mapi_openproperty($mapiattach, PR_ATTACH_DATA_BIN, IID_IStream, 0, 0);
Loading history...
802
                        // It's not possible to open some (embedded only?) messages, so we need to open the attachment object itself to get the data
803
                        if (mapi_last_hresult()) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

803
                        if (/** @scrutinizer ignore-call */ mapi_last_hresult()) {
Loading history...
804
                            $embMessage = mapi_attach_openobj($mapiattach);
1 ignored issue
show
Bug introduced by
The function mapi_attach_openobj was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

804
                            $embMessage = /** @scrutinizer ignore-call */ mapi_attach_openobj($mapiattach);
Loading history...
805
                            $addrbook = $this->getAddressbook();
806
                            $stream = mapi_inetmapi_imtoinet($this->session, $addrbook, $embMessage, array('use_tnef' => -1));
1 ignored issue
show
Bug introduced by
The function mapi_inetmapi_imtoinet was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

806
                            $stream = /** @scrutinizer ignore-call */ mapi_inetmapi_imtoinet($this->session, $addrbook, $embMessage, array('use_tnef' => -1));
Loading history...
807
                        }
808
                        $stat = mapi_stream_stat($stream);
1 ignored issue
show
Bug introduced by
The function mapi_stream_stat was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

808
                        $stat = /** @scrutinizer ignore-call */ mapi_stream_stat($stream);
Loading history...
809
                        $attach->estimatedDataSize = $stat['cb'];
0 ignored issues
show
Bug introduced by
The property estimatedDataSize does not seem to exist on SyncAttachment.
Loading history...
810
                    }
811
                    else {
812
                        $attach->estimatedDataSize = $attachprops[PR_ATTACH_SIZE];
813
                    }
814
815
                    if (isset($attachprops[PR_ATTACH_CONTENT_ID]) && $attachprops[PR_ATTACH_CONTENT_ID])
816
                        $attach->contentid = $attachprops[PR_ATTACH_CONTENT_ID];
0 ignored issues
show
Bug introduced by
The property contentid does not exist on SyncAttachment. Did you mean content?
Loading history...
817
818
                    if (!isset($attach->contentid) && isset($attachprops[PR_ATTACH_CONTENT_ID_W]) && $attachprops[PR_ATTACH_CONTENT_ID_W])
819
                        $attach->contentid = $attachprops[PR_ATTACH_CONTENT_ID_W];
820
821
                    if (isset($attachprops[PR_ATTACHMENT_HIDDEN]) && $attachprops[PR_ATTACHMENT_HIDDEN]) $attach->isinline = 1;
0 ignored issues
show
Bug introduced by
The property isinline does not seem to exist on SyncAttachment.
Loading history...
822
823
                    if (isset($attach->contentid, $attachprops[PR_ATTACH_FLAGS]) && $attachprops[PR_ATTACH_FLAGS] & 4) {
824
                        $attach->isinline = 1;
825
                    }
826
827
                    if(!isset($message->asattachments))
828
                        $message->asattachments = array();
829
830
                    array_push($message->asattachments, $attach);
831
                }
832
                else {
833
                    $attach->attsize = $attachprops[PR_ATTACH_SIZE];
0 ignored issues
show
Bug introduced by
The property attsize does not seem to exist on SyncBaseAttachment.
Loading history...
834
                    $attach->attname = sprintf("%s:%s:%s", $entryid, $row[PR_ATTACH_NUM], $parentSourcekey);
0 ignored issues
show
Bug introduced by
The property attname does not seem to exist on SyncBaseAttachment.
Loading history...
835
                    if(!isset($message->attachments))
836
                        $message->attachments = array();
837
838
                    array_push($message->attachments, $attach);
839
                }
840
            }
841
        }
842
843
        // Get To/Cc as SMTP addresses (this is different from displayto and displaycc because we are putting
844
        // in the SMTP addresses as well, while displayto and displaycc could just contain the display names
845
        $message->to = array();
846
        $message->cc = array();
847
848
        $reciptable = mapi_message_getrecipienttable($mapimessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_getrecipienttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

848
        $reciptable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($mapimessage);
Loading history...
849
        $rows = mapi_table_queryallrows($reciptable, array(PR_RECIPIENT_TYPE, PR_DISPLAY_NAME, PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_ENTRYID, PR_SEARCH_KEY));
850
851
        foreach ($rows as $row) {
852
            $address = "";
853
            $fulladdr = "";
854
855
            $addrtype = isset($row[PR_ADDRTYPE]) ? $row[PR_ADDRTYPE] : "";
856
857
            if (isset($row[PR_SMTP_ADDRESS]))
858
                $address = $row[PR_SMTP_ADDRESS];
859
            elseif ($addrtype == "SMTP" && isset($row[PR_EMAIL_ADDRESS]))
860
                $address = $row[PR_EMAIL_ADDRESS];
861
            elseif ($addrtype == "ZARAFA" && isset($row[PR_ENTRYID]))
862
                $address = $this->getSMTPAddressFromEntryID($row[PR_ENTRYID]);
863
864
            // if the user was not found, do a fallback to PR_SEARCH_KEY
865
            // @see https://jira.z-hub.io/browse/ZP-1178
866
            if (empty($address) && isset($row[PR_SEARCH_KEY])) {
867
                $address = $this->getEmailAddressFromSearchKey($row[PR_SEARCH_KEY]);
868
            }
869
870
            $name = isset($row[PR_DISPLAY_NAME]) ? $row[PR_DISPLAY_NAME] : "";
871
872
            if($name == "" || $name == $address)
873
                $fulladdr = w2u($address);
874
            else {
875
                if (substr($name, 0, 1) != '"' && substr($name, -1) != '"') {
876
                    $fulladdr = "\"" . w2u($name) ."\" <" . w2u($address) . ">";
0 ignored issues
show
Bug introduced by
Are you sure w2u($address) of type false|mixed|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

876
                    $fulladdr = "\"" . w2u($name) ."\" <" . /** @scrutinizer ignore-type */ w2u($address) . ">";
Loading history...
Bug introduced by
Are you sure w2u($name) of type false|mixed|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

876
                    $fulladdr = "\"" . /** @scrutinizer ignore-type */ w2u($name) ."\" <" . w2u($address) . ">";
Loading history...
877
                }
878
                else {
879
                    $fulladdr = w2u($name) ."<" . w2u($address) . ">";
880
                }
881
            }
882
883
            if($row[PR_RECIPIENT_TYPE] == MAPI_TO) {
884
                array_push($message->to, $fulladdr);
885
            } else if($row[PR_RECIPIENT_TYPE] == MAPI_CC) {
886
                array_push($message->cc, $fulladdr);
887
            }
888
        }
889
890
        if (is_array($message->to) && !empty($message->to)) $message->to = implode(", ", $message->to);
891
        if (is_array($message->cc) && !empty($message->cc)) $message->cc = implode(", ", $message->cc);
892
893
        // without importance some mobiles assume "0" (low) - Mantis #439
894
        if (!isset($message->importance))
895
            $message->importance = IMPORTANCE_NORMAL;
896
897
        if (!isset($message->internetcpid)) $message->internetcpid = (defined('STORE_INTERNET_CPID')) ? constant('STORE_INTERNET_CPID') : INTERNET_CPID_WINDOWS1252;
898
        $this->setFlag($mapimessage, $message);
899
        //TODO checkcontentclass
900
        if (!isset($message->contentclass)) $message->contentclass = DEFAULT_EMAIL_CONTENTCLASS;
901
902
        if (!isset($message->nativebodytype)) {
903
            $message->nativebodytype = MAPIUtils::GetNativeBodyType($messageprops);
904
        }
905
        elseif ($message->nativebodytype == SYNC_BODYPREFERENCE_UNDEFINED) {
906
            $nbt = MAPIUtils::GetNativeBodyType($messageprops);
907
            ZLog::Write(LOGLEVEL_INFO, sprintf("MAPIProvider->getEmail(): native body type is undefined. Set it to %d.", $nbt));
908
            $message->nativebodytype = $nbt;
909
        }
910
911
        // reply, reply to all, forward flags
912
        if (isset($message->lastverbexecuted) && $message->lastverbexecuted) {
913
            $message->lastverbexecuted = Utils::GetLastVerbExecuted($message->lastverbexecuted);
914
        }
915
916
        return $message;
917
    }
918
919
    /**
920
    * Reads a note object from MAPI
921
    *
922
    * @param mixed             $mapimessage
923
    * @param ContentParameters $contentparameters
924
    *
925
    * @access private
926
    * @return SyncNote
927
    */
928
    private function getNote($mapimessage, $contentparameters) {
929
        $message = new SyncNote();
930
931
        // Standard one-to-one mappings first
932
        $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetNoteMapping());
933
934
        //set the body according to contentparameters and supported AS version
935
        $this->setMessageBody($mapimessage, $contentparameters, $message);
936
937
        return $message;
938
    }
939
940
    /**
941
     * Creates a SyncFolder from MAPI properties.
942
     *
943
     * @param mixed             $folderprops
944
     *
945
     * @access public
946
     * @return SyncFolder
947
     */
948
    public function GetFolder($folderprops) {
949
        $folder = new SyncFolder();
950
951
        $storeprops = $this->GetStoreProps();
952
953
        // For ZCP 7.0.x we need to retrieve more properties explicitly, see ZP-780
954
        if (isset($folderprops[PR_SOURCE_KEY]) && !isset($folderprops[PR_ENTRYID]) && !isset($folderprops[PR_CONTAINER_CLASS])) {
955
            $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $folderprops[PR_SOURCE_KEY]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

955
            $entryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, $folderprops[PR_SOURCE_KEY]);
Loading history...
956
            $mapifolder = mapi_msgstore_openentry($this->store, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

956
            $mapifolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $entryid);
Loading history...
957
            $folderprops = mapi_getprops($mapifolder, array(PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_ENTRYID, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_CONTAINER_CLASS, PR_ATTR_HIDDEN, PR_EXTENDED_FOLDER_FLAGS));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

957
            $folderprops = /** @scrutinizer ignore-call */ mapi_getprops($mapifolder, array(PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_ENTRYID, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_CONTAINER_CLASS, PR_ATTR_HIDDEN, PR_EXTENDED_FOLDER_FLAGS));
Loading history...
958
            ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->GetFolder(): received insufficient of data from ICS. Fetching required data.");
959
        }
960
961
        if(!isset($folderprops[PR_DISPLAY_NAME]) ||
962
           !isset($folderprops[PR_PARENT_ENTRYID]) ||
963
           !isset($folderprops[PR_SOURCE_KEY]) ||
964
           !isset($folderprops[PR_ENTRYID]) ||
965
           !isset($folderprops[PR_PARENT_SOURCE_KEY]) ||
966
           !isset($storeprops[PR_IPM_SUBTREE_ENTRYID])) {
967
            ZLog::Write(LOGLEVEL_ERROR, "MAPIProvider->GetFolder(): invalid folder. Missing properties");
968
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type SyncFolder.
Loading history...
969
        }
970
971
        // ignore hidden folders
972
        if (isset($folderprops[PR_ATTR_HIDDEN]) && $folderprops[PR_ATTR_HIDDEN] != false) {
973
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->GetFolder(): invalid folder '%s' as it is a hidden folder (PR_ATTR_HIDDEN)", $folderprops[PR_DISPLAY_NAME]));
974
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type SyncFolder.
Loading history...
975
        }
976
977
        // ignore certain undesired folders, like "RSS Feeds" and "Suggested contacts"
978
        if ((isset($folderprops[PR_CONTAINER_CLASS]) && $folderprops[PR_CONTAINER_CLASS] == "IPF.Note.OutlookHomepage")
979
                || in_array($folderprops[PR_ENTRYID], $this->getSpecialFoldersData())) {
980
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->GetFolder(): folder '%s' should not be synchronized", $folderprops[PR_DISPLAY_NAME]));
981
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type SyncFolder.
Loading history...
982
        }
983
984
        $folder->BackendId = bin2hex($folderprops[PR_SOURCE_KEY]);
985
        $folderOrigin = DeviceManager::FLD_ORIGIN_USER;
986
        if (ZPush::GetBackend()->GetImpersonatedUser()) {
987
            $folderOrigin = DeviceManager::FLD_ORIGIN_IMPERSONATED;
988
        }
989
        $folder->serverid = ZPush::GetDeviceManager()->GetFolderIdForBackendId($folder->BackendId, true, $folderOrigin, $folderprops[PR_DISPLAY_NAME]);
990
        if($folderprops[PR_PARENT_ENTRYID] == $storeprops[PR_IPM_SUBTREE_ENTRYID] || $folderprops[PR_PARENT_ENTRYID] == $storeprops[PR_IPM_PUBLIC_FOLDERS_ENTRYID]) {
991
            $folder->parentid = "0";
992
        }
993
        else {
994
            $folder->parentid = ZPush::GetDeviceManager()->GetFolderIdForBackendId(bin2hex($folderprops[PR_PARENT_SOURCE_KEY]));
995
        }
996
        $folder->displayname = w2u($folderprops[PR_DISPLAY_NAME]);
997
        $folder->type = $this->GetFolderType($folderprops[PR_ENTRYID], isset($folderprops[PR_CONTAINER_CLASS])?$folderprops[PR_CONTAINER_CLASS]:false);
0 ignored issues
show
Bug introduced by
It seems like IssetNode ? $folderprops...ONTAINER_CLASS] : false can also be of type false; however, parameter $class of MAPIProvider::GetFolderType() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

997
        $folder->type = $this->GetFolderType($folderprops[PR_ENTRYID], /** @scrutinizer ignore-type */ isset($folderprops[PR_CONTAINER_CLASS])?$folderprops[PR_CONTAINER_CLASS]:false);
Loading history...
998
999
        return $folder;
1000
    }
1001
1002
    /**
1003
     * Returns the foldertype for an entryid
1004
     * Gets the folder type by checking the default folders in MAPI
1005
     *
1006
     * @param string            $entryid
1007
     * @param string            $class      (opt)
1008
     *
1009
     * @access public
1010
     * @return long
0 ignored issues
show
Bug introduced by
The type long was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1011
     */
1012
    public function GetFolderType($entryid, $class = false) {
1013
        $storeprops = $this->GetStoreProps();
1014
        $inboxprops = $this->GetInboxProps();
1015
1016
        if($entryid == $storeprops[PR_IPM_WASTEBASKET_ENTRYID])
1017
            return SYNC_FOLDER_TYPE_WASTEBASKET;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_WASTEBASKET returns the type integer which is incompatible with the documented return type long.
Loading history...
1018
        if($entryid == $storeprops[PR_IPM_SENTMAIL_ENTRYID])
1019
            return SYNC_FOLDER_TYPE_SENTMAIL;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_SENTMAIL returns the type integer which is incompatible with the documented return type long.
Loading history...
1020
        if($entryid == $storeprops[PR_IPM_OUTBOX_ENTRYID])
1021
            return SYNC_FOLDER_TYPE_OUTBOX;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_OUTBOX returns the type integer which is incompatible with the documented return type long.
Loading history...
1022
1023
        // Public folders do not have inboxprops
1024
        // @see https://jira.z-hub.io/browse/ZP-995
1025
        if (!empty($inboxprops)) {
1026
            if($entryid == $inboxprops[PR_ENTRYID])
1027
                return SYNC_FOLDER_TYPE_INBOX;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_INBOX returns the type integer which is incompatible with the documented return type long.
Loading history...
1028
            if($entryid == $inboxprops[PR_IPM_DRAFTS_ENTRYID])
1029
                return SYNC_FOLDER_TYPE_DRAFTS;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_DRAFTS returns the type integer which is incompatible with the documented return type long.
Loading history...
1030
            if($entryid == $inboxprops[PR_IPM_TASK_ENTRYID])
1031
                return SYNC_FOLDER_TYPE_TASK;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_TASK returns the type integer which is incompatible with the documented return type long.
Loading history...
1032
            if($entryid == $inboxprops[PR_IPM_APPOINTMENT_ENTRYID])
1033
                return SYNC_FOLDER_TYPE_APPOINTMENT;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_APPOINTMENT returns the type integer which is incompatible with the documented return type long.
Loading history...
1034
            if($entryid == $inboxprops[PR_IPM_CONTACT_ENTRYID])
1035
                return SYNC_FOLDER_TYPE_CONTACT;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_CONTACT returns the type integer which is incompatible with the documented return type long.
Loading history...
1036
            if($entryid == $inboxprops[PR_IPM_NOTE_ENTRYID])
1037
                return SYNC_FOLDER_TYPE_NOTE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_NOTE returns the type integer which is incompatible with the documented return type long.
Loading history...
1038
            if($entryid == $inboxprops[PR_IPM_JOURNAL_ENTRYID])
1039
                return SYNC_FOLDER_TYPE_JOURNAL;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_JOURNAL returns the type integer which is incompatible with the documented return type long.
Loading history...
1040
        }
1041
1042
        // user created folders
1043
        if ($class == "IPF.Note")
1044
            return SYNC_FOLDER_TYPE_USER_MAIL;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_USER_MAIL returns the type integer which is incompatible with the documented return type long.
Loading history...
1045
        if ($class == "IPF.Task")
1046
            return SYNC_FOLDER_TYPE_USER_TASK;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_USER_TASK returns the type integer which is incompatible with the documented return type long.
Loading history...
1047
        if ($class == "IPF.Appointment")
1048
            return SYNC_FOLDER_TYPE_USER_APPOINTMENT;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_USER_APPOINTMENT returns the type integer which is incompatible with the documented return type long.
Loading history...
1049
        if ($class == "IPF.Contact")
1050
            return SYNC_FOLDER_TYPE_USER_CONTACT;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_USER_CONTACT returns the type integer which is incompatible with the documented return type long.
Loading history...
1051
        if ($class == "IPF.StickyNote")
1052
            return SYNC_FOLDER_TYPE_USER_NOTE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_USER_NOTE returns the type integer which is incompatible with the documented return type long.
Loading history...
1053
        if ($class == "IPF.Journal")
1054
            return  SYNC_FOLDER_TYPE_USER_JOURNAL;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_USER_JOURNAL returns the type integer which is incompatible with the documented return type long.
Loading history...
1055
1056
        return SYNC_FOLDER_TYPE_OTHER;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_FOLDER_TYPE_OTHER returns the type integer which is incompatible with the documented return type long.
Loading history...
1057
    }
1058
1059
    /**
1060
     * Indicates if the entry id is a default MAPI folder
1061
     *
1062
     * @param string            $entryid
1063
     *
1064
     * @access public
1065
     * @return boolean
1066
     */
1067
    public function IsMAPIDefaultFolder($entryid) {
1068
        $msgstore_props = mapi_getprops($this->store, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_IPM_SUBTREE_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_MDB_PROVIDER, PR_IPM_PUBLIC_FOLDERS_ENTRYID, PR_IPM_FAVORITES_ENTRYID, PR_MAILBOX_OWNER_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1068
        $msgstore_props = /** @scrutinizer ignore-call */ mapi_getprops($this->store, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_IPM_SUBTREE_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_MDB_PROVIDER, PR_IPM_PUBLIC_FOLDERS_ENTRYID, PR_IPM_FAVORITES_ENTRYID, PR_MAILBOX_OWNER_ENTRYID));
Loading history...
1069
1070
        $inboxProps = array();
1071
        $inbox = mapi_msgstore_getreceivefolder($this->store);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_getreceivefolder was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1071
        $inbox = /** @scrutinizer ignore-call */ mapi_msgstore_getreceivefolder($this->store);
Loading history...
1072
        if(!mapi_last_hresult())
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1072
        if(!/** @scrutinizer ignore-call */ mapi_last_hresult())
Loading history...
1073
            $inboxProps = mapi_getprops($inbox, array(PR_ENTRYID));
1074
1075
        $root = mapi_msgstore_openentry($this->store, null); // TODO use getRootProps()
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1075
        $root = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, null); // TODO use getRootProps()
Loading history...
1076
        $rootProps = mapi_getprops($root, array(PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_JOURNAL_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_TASK_ENTRYID, PR_ADDITIONAL_REN_ENTRYIDS));
1077
1078
        $additional_ren_entryids = array();
1079
        if(isset($rootProps[PR_ADDITIONAL_REN_ENTRYIDS]))
1080
            $additional_ren_entryids = $rootProps[PR_ADDITIONAL_REN_ENTRYIDS];
1081
1082
        $defaultfolders = array(
1083
                        "inbox"                 =>      array("inbox"=>PR_ENTRYID),
1084
                        "outbox"                =>      array("store"=>PR_IPM_OUTBOX_ENTRYID),
1085
                        "sent"                  =>      array("store"=>PR_IPM_SENTMAIL_ENTRYID),
1086
                        "wastebasket"           =>      array("store"=>PR_IPM_WASTEBASKET_ENTRYID),
1087
                        "favorites"             =>      array("store"=>PR_IPM_FAVORITES_ENTRYID),
1088
                        "publicfolders"         =>      array("store"=>PR_IPM_PUBLIC_FOLDERS_ENTRYID),
1089
                        "calendar"              =>      array("root" =>PR_IPM_APPOINTMENT_ENTRYID),
1090
                        "contact"               =>      array("root" =>PR_IPM_CONTACT_ENTRYID),
1091
                        "drafts"                =>      array("root" =>PR_IPM_DRAFTS_ENTRYID),
1092
                        "journal"               =>      array("root" =>PR_IPM_JOURNAL_ENTRYID),
1093
                        "note"                  =>      array("root" =>PR_IPM_NOTE_ENTRYID),
1094
                        "task"                  =>      array("root" =>PR_IPM_TASK_ENTRYID),
1095
                        "junk"                  =>      array("additional" =>4),
1096
                        "syncissues"            =>      array("additional" =>1),
1097
                        "conflicts"             =>      array("additional" =>0),
1098
                        "localfailures"         =>      array("additional" =>2),
1099
                        "serverfailures"        =>      array("additional" =>3),
1100
        );
1101
1102
        foreach($defaultfolders as $key=>$prop){
1103
            $tag = reset($prop);
1104
            $from = key($prop);
1105
            switch($from){
1106
                case "inbox":
1107
                    if(isset($inboxProps[$tag]) && $entryid == $inboxProps[$tag]) {
1108
                        ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->IsMAPIFolder(): Inbox found, key '%s'", $key));
1109
                        return true;
1110
                    }
1111
                    break;
1112
                case "store":
1113
                    if(isset($msgstore_props[$tag]) && $entryid == $msgstore_props[$tag]) {
1114
                        ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->IsMAPIFolder(): Store folder found, key '%s'", $key));
1115
                        return true;
1116
                    }
1117
                    break;
1118
                case "root":
1119
                    if(isset($rootProps[$tag]) && $entryid == $rootProps[$tag]) {
1120
                        ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->IsMAPIFolder(): Root folder found, key '%s'", $key));
1121
                        return true;
1122
                    }
1123
                    break;
1124
                case "additional":
1125
                    if(isset($additional_ren_entryids[$tag]) && $entryid == $additional_ren_entryids[$tag]) {
1126
                        ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->IsMAPIFolder(): Additional folder found, key '%s'", $key));
1127
                        return true;
1128
                    }
1129
            }
1130
        }
1131
        return false;
1132
    }
1133
1134
    /**----------------------------------------------------------------------------------------------------------
1135
     * SETTER
1136
     */
1137
1138
    /**
1139
     * Writes a SyncObject to MAPI
1140
     * Depending on the message class, a contact, appointment, task or email is written
1141
     *
1142
     * @param mixed             $mapimessage
1143
     * @param SyncObject        $message
1144
     *
1145
     * @access public
1146
     * @return boolean
1147
     */
1148
    public function SetMessage($mapimessage, $message) {
1149
        // TODO check with instanceof
1150
        switch(strtolower(get_class($message))) {
1151
            case "synccontact":
1152
                return $this->setContact($mapimessage, $message);
1153
            case "syncappointment":
1154
                return $this->setAppointment($mapimessage, $message);
1155
            case "synctask":
1156
                return $this->setTask($mapimessage, $message);
1157
            case "syncnote":
1158
                return $this->setNote($mapimessage, $message);
1159
            default:
1160
                //for emails only flag (read and todo) changes are possible
1161
                return $this->setEmail($mapimessage, $message);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setEmail($mapimessage, $message) targeting MAPIProvider::setEmail() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1162
        }
1163
    }
1164
1165
    /**
1166
     * Writes SyncMail to MAPI (actually flags only)
1167
     *
1168
     * @param mixed             $mapimessage
1169
     * @param SyncMail          $message
1170
     */
1171
    private function setEmail($mapimessage, $message) {
1172
        // update categories
1173
        if (!isset($message->categories)) $message->categories = array();
1174
        $emailmap = MAPIMapping::GetEmailMapping();
1175
        $this->setPropsInMAPI($mapimessage, $message, array("categories" => $emailmap["categories"]));
1176
1177
        $flagmapping = MAPIMapping::GetMailFlagsMapping();
1178
        $flagprops = MAPIMapping::GetMailFlagsProperties();
1179
        $flagprops = array_merge($this->getPropIdsFromStrings($flagmapping), $this->getPropIdsFromStrings($flagprops));
1180
        // flag specific properties to be set
1181
        $props = $delprops = array();
1182
        // unset message flags if:
1183
        // flag is not set
1184
        if (empty($message->flag) ||
1185
            // flag status is not set
1186
            !isset($message->flag->flagstatus) ||
1187
            // flag status is 0 or empty
1188
            (isset($message->flag->flagstatus) && ($message->flag->flagstatus == 0 || $message->flag->flagstatus == "")) ) {
1189
            // if message flag is empty, some properties need to be deleted
1190
            // and some set to 0 or false
1191
1192
            $props[$flagprops["todoitemsflags"]] = 0;
1193
            $props[$flagprops["status"]] = 0;
1194
            $props[$flagprops["completion"]] = 0.0;
1195
            $props[$flagprops["flagtype"]] = "";
1196
            $props[$flagprops["ordinaldate"]] = 0x7fffffff; // ordinal date is 12am 1.1.4501, set it to max possible value
1197
            $props[$flagprops["subordinaldate"]] = "";
1198
            $props[$flagprops["replyrequested"]] = false;
1199
            $props[$flagprops["responserequested"]] = false;
1200
            $props[$flagprops["reminderset"]] = false;
1201
            $props[$flagprops["complete"]] = false;
1202
1203
            $delprops[] = $flagprops["todotitle"];
1204
            $delprops[] = $flagprops["duedate"];
1205
            $delprops[] = $flagprops["startdate"];
1206
            $delprops[] = $flagprops["datecompleted"];
1207
            $delprops[] = $flagprops["utcstartdate"];
1208
            $delprops[] = $flagprops["utcduedate"];
1209
            $delprops[] = $flagprops["completetime"];
1210
            $delprops[] = $flagprops["flagstatus"];
1211
            $delprops[] = $flagprops["flagicon"];
1212
        }
1213
        else {
1214
            $this->setPropsInMAPI($mapimessage, $message->flag, $flagmapping);
1215
            $props[$flagprops["todoitemsflags"]] = 1;
1216
            if (isset($message->subject) && strlen($message->subject) > 0)
1217
                $props[$flagprops["todotitle"]] = $message->subject;
1218
            // ordinal date is utc current time
1219
            if (!isset($message->flag->ordinaldate) || empty($message->flag->ordinaldate)) {
1220
                $props[$flagprops["ordinaldate"]] = time();
1221
            }
1222
            // the default value
1223
            if (!isset($message->flag->subordinaldate) || empty($message->flag->subordinaldate)) {
1224
                $props[$flagprops["subordinaldate"]] = "5555555";
1225
            }
1226
            $props[$flagprops["flagicon"]] = 6; //red flag icon
1227
            $props[$flagprops["replyrequested"]] = true;
1228
            $props[$flagprops["responserequested"]] = true;
1229
1230
            if ($message->flag->flagstatus == SYNC_FLAGSTATUS_COMPLETE) {
1231
                $props[$flagprops["status"]] = olTaskComplete;
1232
                $props[$flagprops["completion"]] = 1.0;
1233
                $props[$flagprops["complete"]] = true;
1234
                $props[$flagprops["replyrequested"]] = false;
1235
                $props[$flagprops["responserequested"]] = false;
1236
                unset($props[$flagprops["flagicon"]]);
1237
                $delprops[] = $flagprops["flagicon"];
1238
            }
1239
        }
1240
1241
        if (!empty($props)) {
1242
            mapi_setprops($mapimessage, $props);
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1242
            /** @scrutinizer ignore-call */ 
1243
            mapi_setprops($mapimessage, $props);
Loading history...
1243
        }
1244
        if (!empty($delprops)) {
1245
            mapi_deleteprops($mapimessage, $delprops);
1 ignored issue
show
Bug introduced by
The function mapi_deleteprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1245
            /** @scrutinizer ignore-call */ 
1246
            mapi_deleteprops($mapimessage, $delprops);
Loading history...
1246
        }
1247
    }
1248
1249
    /**
1250
     * Writes a SyncAppointment to MAPI
1251
     *
1252
     * @param mixed             $mapimessage
1253
     * @param SyncAppointment   $message
1254
     *
1255
     * @access private
1256
     * @return boolean
1257
     */
1258
    private function setAppointment($mapimessage, $appointment) {
1259
        // Get timezone info
1260
        if(isset($appointment->timezone))
1261
            $tz = $this->getTZFromSyncBlob(base64_decode($appointment->timezone));
1262
        else
1263
            $tz = false;
1264
1265
        // start and end time may not be set - try to get them from the existing appointment for further calculation - see https://jira.z-hub.io/browse/ZP-983
1266
        if (!isset($appointment->starttime) || !isset($appointment->endtime)) {
1267
            $amapping = MAPIMapping::GetAppointmentMapping();
1268
            $amapping = $this->getPropIdsFromStrings($amapping);
1269
            $existingstartendpropsmap = array($amapping["starttime"], $amapping["endtime"]);
1270
            $existingstartendprops = $this->getProps($mapimessage, $existingstartendpropsmap);
1271
1272
            if (isset($existingstartendprops[$amapping["starttime"]]) && !isset($appointment->starttime)) {
1273
                $appointment->starttime = $existingstartendprops[$amapping["starttime"]];
1274
                ZLog::Write(LOGLEVEL_WBXML, sprintf("MAPIProvider->setAppointment(): Parameter 'starttime' was not set, using value from MAPI %d (%s).", $appointment->starttime, gmstrftime("%Y%m%dT%H%M%SZ", $appointment->starttime)));
1275
            }
1276
            if (isset($existingstartendprops[$amapping["endtime"]]) && !isset($appointment->endtime)) {
1277
                $appointment->endtime = $existingstartendprops[$amapping["endtime"]];
1278
                ZLog::Write(LOGLEVEL_WBXML, sprintf("MAPIProvider->setAppointment(): Parameter 'endtime' was not set, using value from MAPI %d (%s).", $appointment->endtime, gmstrftime("%Y%m%dT%H%M%SZ", $appointment->endtime)));
1279
            }
1280
        }
1281
        if (!isset($appointment->starttime) || !isset($appointment->endtime)) {
1282
            throw new StatusException("MAPIProvider->setAppointment(): Error, start and/or end time not set and can not be retrieved from MAPI.", SYNC_STATUS_SYNCCANNOTBECOMPLETED);
1283
        }
1284
1285
        //calculate duration because without it some webaccess views are broken. duration is in min
1286
        $localstart = $this->getLocaltimeByTZ($appointment->starttime, $tz);
0 ignored issues
show
Bug introduced by
It seems like $tz can also be of type false; however, parameter $tz of MAPIProvider::getLocaltimeByTZ() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1286
        $localstart = $this->getLocaltimeByTZ($appointment->starttime, /** @scrutinizer ignore-type */ $tz);
Loading history...
1287
        $localend = $this->getLocaltimeByTZ($appointment->endtime, $tz);
1288
        $duration = ($localend - $localstart)/60;
1289
1290
        //nokia sends an yearly event with 0 mins duration but as all day event,
1291
        //so make it end next day
1292
        if ($appointment->starttime == $appointment->endtime && isset($appointment->alldayevent) && $appointment->alldayevent) {
1293
            $duration = 1440;
1294
            $appointment->endtime = $appointment->starttime + 24 * 60 * 60;
1295
            $localend = $localstart + 24 * 60 * 60;
1296
        }
1297
1298
        // is the transmitted UID OL compatible?
1299
        // if not, encapsulate the transmitted uid
1300
        $appointment->uid = Utils::GetOLUidFromICalUid($appointment->uid);
1301
1302
        mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Appointment"));
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1302
        /** @scrutinizer ignore-call */ 
1303
        mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Appointment"));
Loading history...
1303
1304
        $appointmentmapping = MAPIMapping::GetAppointmentMapping();
1305
        $this->setPropsInMAPI($mapimessage, $appointment, $appointmentmapping);
1306
        $appointmentprops = MAPIMapping::GetAppointmentProperties();
1307
        $appointmentprops = array_merge($this->getPropIdsFromStrings($appointmentmapping), $this->getPropIdsFromStrings($appointmentprops));
1308
        //appointment specific properties to be set
1309
        $props = array();
1310
1311
        //sensitivity is not enough to mark an appointment as private, so we use another mapi tag
1312
        $private = (isset($appointment->sensitivity) && $appointment->sensitivity >= SENSITIVITY_PRIVATE) ? true : false;
1313
1314
        // Set commonstart/commonend to start/end and remindertime to start, duration, private and cleanGlobalObjectId
1315
        $props[$appointmentprops["commonstart"]] = $appointment->starttime;
1316
        $props[$appointmentprops["commonend"]] = $appointment->endtime;
1317
        $props[$appointmentprops["reminderstart"]] = $appointment->starttime;
1318
        // Set reminder boolean to 'true' if reminder is set
1319
        $props[$appointmentprops["reminderset"]] = isset($appointment->reminder) ? true : false;
1320
        $props[$appointmentprops["duration"]] = $duration;
1321
        $props[$appointmentprops["private"]] = $private;
1322
        $props[$appointmentprops["uid"]] = $appointment->uid;
1323
        // Set named prop 8510, unknown property, but enables deleting a single occurrence of a recurring
1324
        // type in OLK2003.
1325
        $props[$appointmentprops["sideeffects"]] = 369;
1326
1327
1328
        if(isset($appointment->reminder) && $appointment->reminder >= 0) {
1329
            // Set 'flagdueby' to correct value (start - reminderminutes)
1330
            $props[$appointmentprops["flagdueby"]] = $appointment->starttime - $appointment->reminder * 60;
1331
            $props[$appointmentprops["remindertime"]] = $appointment->reminder;
1332
        }
1333
        // unset the reminder
1334
        else {
1335
            $props[$appointmentprops["reminderset"]] = false;
1336
        }
1337
1338
        if (isset($appointment->asbody)) {
1339
            $this->setASbody($appointment->asbody, $props, $appointmentprops);
1340
        }
1341
1342
        if ($tz !== false) {
1343
            $props[$appointmentprops["timezonetag"]] = $this->getMAPIBlobFromTZ($tz);
1344
        }
1345
1346
        if(isset($appointment->recurrence)) {
1347
            // Set PR_ICON_INDEX to 1025 to show correct icon in category view
1348
            $props[$appointmentprops["icon"]] = 1025;
1349
1350
            //if there aren't any exceptions, use the 'old style' set recurrence
1351
            $noexceptions = true;
1352
1353
            $recurrence = new Recurrence($this->store, $mapimessage);
1354
            $recur = array();
1355
            $this->setRecurrence($appointment, $recur);
1356
1357
            // set the recurrence type to that of the MAPI
1358
            $props[$appointmentprops["recurrencetype"]] = $recur["recurrencetype"];
1359
1360
            $starttime = $this->gmtime($localstart);
1361
            $endtime = $this->gmtime($localend);
0 ignored issues
show
Bug introduced by
It seems like $localend can also be of type integer; however, parameter $time of MAPIProvider::gmtime() does only seem to accept long, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1361
            $endtime = $this->gmtime(/** @scrutinizer ignore-type */ $localend);
Loading history...
Unused Code introduced by
The assignment to $endtime is dead and can be removed.
Loading history...
1362
1363
            //set recurrence start here because it's calculated differently for tasks and appointments
1364
            $recur["start"] = $this->getDayStartOfTimestamp($this->getGMTTimeByTZ($localstart, $tz));
0 ignored issues
show
Bug introduced by
It seems like $tz can also be of type false; however, parameter $tz of MAPIProvider::getGMTTimeByTZ() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1364
            $recur["start"] = $this->getDayStartOfTimestamp($this->getGMTTimeByTZ($localstart, /** @scrutinizer ignore-type */ $tz));
Loading history...
1365
1366
            $recur["startocc"] = $starttime["tm_hour"] * 60 + $starttime["tm_min"];
1367
            $recur["endocc"] = $recur["startocc"] + $duration; // Note that this may be > 24*60 if multi-day
1368
1369
            //only tasks can regenerate
1370
            $recur["regen"] = false;
1371
1372
            // Process exceptions. The PDA will send all exceptions for this recurring item.
1373
            if(isset($appointment->exceptions)) {
1374
                foreach($appointment->exceptions as $exception) {
1375
                    // we always need the base date
1376
                    if(!isset($exception->exceptionstarttime)) {
1377
                        continue;
1378
                    }
1379
1380
                    $basedate = $this->getDayStartOfTimestamp($exception->exceptionstarttime);
1381
                    if(isset($exception->deleted) && $exception->deleted) {
1382
                        $noexceptions = false;
1383
                        // Delete exception
1384
                        $recurrence->createException(array(), $basedate, true);
1385
                    }
1386
                    else {
1387
                        // Change exception
1388
                        $mapiexception = array("basedate" => $basedate);
1389
                        //other exception properties which are not handled in recurrence
1390
                        $exceptionprops = array();
1391
1392
                        if(isset($exception->starttime)) {
1393
                            $mapiexception["start"] = $this->getLocaltimeByTZ($exception->starttime, $tz);
1394
                            $exceptionprops[$appointmentprops["starttime"]] = $exception->starttime;
1395
                        }
1396
                        if(isset($exception->endtime)) {
1397
                            $mapiexception["end"] = $this->getLocaltimeByTZ($exception->endtime, $tz);
1398
                            $exceptionprops[$appointmentprops["endtime"]] = $exception->endtime;
1399
                        }
1400
                        if(isset($exception->subject))
1401
                            $exceptionprops[$appointmentprops["subject"]] = $mapiexception["subject"] = u2w($exception->subject);
1402
                        if(isset($exception->location))
1403
                            $exceptionprops[$appointmentprops["location"]] = $mapiexception["location"] = u2w($exception->location);
1404
                        if(isset($exception->busystatus))
1405
                            $exceptionprops[$appointmentprops["busystatus"]] = $mapiexception["busystatus"] = $exception->busystatus;
1406
                        if(isset($exception->reminder)) {
1407
                            $exceptionprops[$appointmentprops["reminderset"]] = $mapiexception["reminder_set"] = 1;
1408
                            $exceptionprops[$appointmentprops["remindertime"]] = $mapiexception["remind_before"] = $exception->reminder;
1409
                        }
1410
                        if(isset($exception->alldayevent))
1411
                            $exceptionprops[$appointmentprops["alldayevent"]] = $mapiexception["alldayevent"] = $exception->alldayevent;
1412
1413
1414
                        if(!isset($recur["changed_occurences"]))
1415
                            $recur["changed_occurences"] = array();
1416
1417
                        if (isset($exception->body))
1418
                            $exceptionprops[$appointmentprops["body"]] = u2w($exception->body);
1419
1420
                        if (isset($exception->asbody)) {
1421
                            $this->setASbody($exception->asbody, $exceptionprops, $appointmentprops);
1422
                            $mapiexception["body"] = $exceptionprops[$appointmentprops["body"]] =
1423
                                (isset($exceptionprops[$appointmentprops["body"]])) ? $exceptionprops[$appointmentprops["body"]] :
1424
                                ((isset($exceptionprops[$appointmentprops["html"]])) ? $exceptionprops[$appointmentprops["html"]] : "");
1425
                        }
1426
1427
                        array_push($recur["changed_occurences"], $mapiexception);
1428
1429
                        if (!empty($exceptionprops)) {
1430
                            $noexceptions = false;
1431
                            if($recurrence->isException($basedate)){
1432
                                $recurrence->modifyException($exceptionprops, $basedate);
1433
                            }
1434
                            else {
1435
                                $recurrence->createException($exceptionprops, $basedate);
1436
                            }
1437
                        }
1438
1439
                    }
1440
                }
1441
            }
1442
1443
            //setRecurrence deletes the attachments from an appointment
1444
            if ($noexceptions) {
1445
                $recurrence->setRecurrence($tz, $recur);
1446
            }
1447
        }
1448
        else {
1449
            $props[$appointmentprops["isrecurring"]] = false;
1450
        }
1451
1452
        //always set the PR_SENT_REPRESENTING_* props so that the attendee status update also works with the webaccess
1453
        $p = array( $appointmentprops["representingentryid"], $appointmentprops["representingname"], $appointmentprops["sentrepresentingaddt"],
1454
                    $appointmentprops["sentrepresentingemail"], $appointmentprops["sentrepresentinsrchk"], $appointmentprops["responsestatus"]);
1455
        $representingprops = $this->getProps($mapimessage, $p);
1456
1457
        if (!isset($representingprops[$appointmentprops["representingentryid"]])) {
1458
            // TODO use GetStoreProps
1459
            $storeProps = mapi_getprops($this->store, array(PR_MAILBOX_OWNER_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1459
            $storeProps = /** @scrutinizer ignore-call */ mapi_getprops($this->store, array(PR_MAILBOX_OWNER_ENTRYID));
Loading history...
1460
            $props[$appointmentprops["representingentryid"]] = $storeProps[PR_MAILBOX_OWNER_ENTRYID];
1461
            $displayname = $this->getFullnameFromEntryID($storeProps[PR_MAILBOX_OWNER_ENTRYID]);
1462
1463
            $props[$appointmentprops["representingname"]] = ($displayname !== false) ? $displayname : Request::GetUser();
0 ignored issues
show
introduced by
The condition $displayname !== false is always true.
Loading history...
1464
            $props[$appointmentprops["sentrepresentingemail"]] = Request::GetUser();
1465
            $props[$appointmentprops["sentrepresentingaddt"]] = "ZARAFA";
1466
            $props[$appointmentprops["sentrepresentinsrchk"]] = $props[$appointmentprops["sentrepresentingaddt"]].":".$props[$appointmentprops["sentrepresentingemail"]];
1467
1468
            if(isset($appointment->attendees) && is_array($appointment->attendees) && !empty($appointment->attendees)) {
1469
                $props[$appointmentprops["icon"]] = 1026;
1470
                // the user is the organizer
1471
                // set these properties to show tracking tab in webapp
1472
1473
                $props[$appointmentprops["mrwassent"]] = true;
1474
                $props[$appointmentprops["responsestatus"]] = olResponseOrganized;
1475
                $props[$appointmentprops["meetingstatus"]] = olMeeting;
1476
            }
1477
        }
1478
        //we also have to set the responsestatus and not only meetingstatus, so we use another mapi tag
1479
        if (!isset($props[$appointmentprops["responsestatus"]])) {
1480
            if (isset($appointment->responsetype)) {
1481
                $props[$appointmentprops["responsestatus"]] = $appointment->responsetype;
1482
            }
1483
            // only set responsestatus to none if it is not set on the server
1484
            elseif (!isset($representingprops[$appointmentprops["responsestatus"]])) {
1485
                $props[$appointmentprops["responsestatus"]] = olResponseNone;
1486
            }
1487
        }
1488
1489
        // Do attendees
1490
        if(isset($appointment->attendees) && is_array($appointment->attendees)) {
1491
            $recips = array();
1492
1493
            // Outlook XP requires organizer in the attendee list as well
1494
            $org = array();
1495
            $org[PR_ENTRYID] = isset($representingprops[$appointmentprops["representingentryid"]]) ? $representingprops[$appointmentprops["representingentryid"]] : $props[$appointmentprops["representingentryid"]];
1496
            $org[PR_DISPLAY_NAME] = isset($representingprops[$appointmentprops["representingname"]]) ? $representingprops[$appointmentprops["representingname"]] : $props[$appointmentprops["representingname"]];
1497
            $org[PR_ADDRTYPE] = isset($representingprops[$appointmentprops["sentrepresentingaddt"]]) ? $representingprops[$appointmentprops["sentrepresentingaddt"]] : $props[$appointmentprops["sentrepresentingaddt"]];
1498
            $org[PR_SMTP_ADDRESS] = $org[PR_EMAIL_ADDRESS] = isset($representingprops[$appointmentprops["sentrepresentingemail"]]) ? $representingprops[$appointmentprops["sentrepresentingemail"]] : $props[$appointmentprops["sentrepresentingemail"]];
1499
            $org[PR_SEARCH_KEY] = isset($representingprops[$appointmentprops["sentrepresentinsrchk"]]) ? $representingprops[$appointmentprops["sentrepresentinsrchk"]] : $props[$appointmentprops["sentrepresentinsrchk"]];
1500
            $org[PR_RECIPIENT_FLAGS] = recipOrganizer | recipSendable;
1501
            $org[PR_RECIPIENT_TYPE] = MAPI_ORIG;
1502
1503
            array_push($recips, $org);
1504
1505
            // Open address book for user resolve
1506
            $addrbook = $this->getAddressbook();
1507
            foreach($appointment->attendees as $attendee) {
1508
                $recip = array();
1509
                $recip[PR_EMAIL_ADDRESS] = u2w($attendee->email);
1510
                $recip[PR_SMTP_ADDRESS] = u2w($attendee->email);
1511
1512
                // lookup information in GAB if possible so we have up-to-date name for given address
1513
                $userinfo = array( array( PR_DISPLAY_NAME => $recip[PR_EMAIL_ADDRESS] ) );
1514
                $userinfo = mapi_ab_resolvename($addrbook, $userinfo, EMS_AB_ADDRESS_LOOKUP);
1 ignored issue
show
Bug introduced by
The function mapi_ab_resolvename was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1514
                $userinfo = /** @scrutinizer ignore-call */ mapi_ab_resolvename($addrbook, $userinfo, EMS_AB_ADDRESS_LOOKUP);
Loading history...
1515
                if(mapi_last_hresult() == NOERROR) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1515
                if(/** @scrutinizer ignore-call */ mapi_last_hresult() == NOERROR) {
Loading history...
1516
                    $recip[PR_DISPLAY_NAME] = $userinfo[0][PR_DISPLAY_NAME];
1517
                    $recip[PR_EMAIL_ADDRESS] = $userinfo[0][PR_EMAIL_ADDRESS];
1518
                    $recip[PR_SEARCH_KEY] = $userinfo[0][PR_SEARCH_KEY];
1519
                    $recip[PR_ADDRTYPE] = $userinfo[0][PR_ADDRTYPE];
1520
                    $recip[PR_ENTRYID] = $userinfo[0][PR_ENTRYID];
1521
                    $recip[PR_RECIPIENT_TYPE] = isset($attendee->attendeetype) ? $attendee->attendeetype : MAPI_TO;
1522
                    $recip[PR_RECIPIENT_FLAGS] = recipSendable;
1523
                    $recip[PR_RECIPIENT_TRACKSTATUS] = isset($attendee->attendeestatus) ? $attendee->attendeestatus : olResponseNone;
1524
                }
1525
                else {
1526
                    $recip[PR_DISPLAY_NAME] = u2w($attendee->name);
1527
                    $recip[PR_SEARCH_KEY] = "SMTP:".$recip[PR_EMAIL_ADDRESS]."\0";
1528
                    $recip[PR_ADDRTYPE] = "SMTP";
1529
                    $recip[PR_RECIPIENT_TYPE] = isset($attendee->attendeetype) ? $attendee->attendeetype : MAPI_TO;
1530
                    $recip[PR_ENTRYID] = mapi_createoneoff($recip[PR_DISPLAY_NAME], $recip[PR_ADDRTYPE], $recip[PR_EMAIL_ADDRESS]);
1 ignored issue
show
Bug introduced by
The function mapi_createoneoff was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1530
                    $recip[PR_ENTRYID] = /** @scrutinizer ignore-call */ mapi_createoneoff($recip[PR_DISPLAY_NAME], $recip[PR_ADDRTYPE], $recip[PR_EMAIL_ADDRESS]);
Loading history...
1531
                }
1532
1533
                array_push($recips, $recip);
1534
            }
1535
1536
            mapi_message_modifyrecipients($mapimessage, 0, $recips);
1 ignored issue
show
Bug introduced by
The function mapi_message_modifyrecipients was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1536
            /** @scrutinizer ignore-call */ 
1537
            mapi_message_modifyrecipients($mapimessage, 0, $recips);
Loading history...
1537
        }
1538
        mapi_setprops($mapimessage, $props);
1539
        return true;
1540
    }
1541
1542
    /**
1543
     * Writes a SyncContact to MAPI
1544
     *
1545
     * @param mixed             $mapimessage
1546
     * @param SyncContact       $contact
1547
     *
1548
     * @access private
1549
     * @return boolean
1550
     */
1551
    private function setContact($mapimessage, $contact) {
1552
        mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Contact"));
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1552
        /** @scrutinizer ignore-call */ 
1553
        mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Contact"));
Loading history...
1553
1554
        // normalize email addresses
1555
        if (isset($contact->email1address) && (($contact->email1address = $this->extractEmailAddress($contact->email1address)) === false))
0 ignored issues
show
introduced by
The condition $contact->email1address ...mail1address) === false is always false.
Loading history...
1556
            unset($contact->email1address);
1557
1558
        if (isset($contact->email2address) && (($contact->email2address = $this->extractEmailAddress($contact->email2address)) === false))
0 ignored issues
show
introduced by
The condition $contact->email2address ...mail2address) === false is always false.
Loading history...
1559
            unset($contact->email2address);
1560
1561
        if (isset($contact->email3address) && (($contact->email3address = $this->extractEmailAddress($contact->email3address)) === false))
0 ignored issues
show
introduced by
The condition $contact->email3address ...mail3address) === false is always false.
Loading history...
1562
            unset($contact->email3address);
1563
1564
        $contactmapping = MAPIMapping::GetContactMapping();
1565
        $contactprops = MAPIMapping::GetContactProperties();
1566
        $this->setPropsInMAPI($mapimessage, $contact, $contactmapping);
1567
1568
        ///set display name from contact's properties
1569
        $cname = $this->composeDisplayName($contact);
1570
1571
        //get contact specific mapi properties and merge them with the AS properties
1572
        $contactprops = array_merge($this->getPropIdsFromStrings($contactmapping), $this->getPropIdsFromStrings($contactprops));
1573
1574
        //contact specific properties to be set
1575
        $props = array();
1576
1577
        //need to be set in order to show contacts properly in outlook and wa
1578
        $nremails = array();
1579
        $abprovidertype = 0;
1580
1581
        if (isset($contact->email1address))
1582
            $this->setEmailAddress($contact->email1address, $cname, 1, $props, $contactprops, $nremails, $abprovidertype);
1583
        if (isset($contact->email2address))
1584
            $this->setEmailAddress($contact->email2address, $cname, 2, $props, $contactprops, $nremails, $abprovidertype);
1585
        if (isset($contact->email3address))
1586
            $this->setEmailAddress($contact->email3address, $cname, 3, $props, $contactprops, $nremails, $abprovidertype);
1587
1588
        $props[$contactprops["addressbooklong"]] = $abprovidertype;
1589
        $props[$contactprops["displayname"]] = $props[$contactprops["subject"]] = $cname;
1590
1591
        //pda multiple e-mail addresses bug fix for the contact
1592
        if (!empty($nremails)) $props[$contactprops["addressbookmv"]] = $nremails;
1593
1594
1595
        //set addresses
1596
        $this->setAddress("home", $contact->homecity, $contact->homecountry, $contact->homepostalcode, $contact->homestate, $contact->homestreet, $props, $contactprops);
1597
        $this->setAddress("business", $contact->businesscity, $contact->businesscountry, $contact->businesspostalcode, $contact->businessstate, $contact->businessstreet, $props, $contactprops);
1598
        $this->setAddress("other", $contact->othercity, $contact->othercountry, $contact->otherpostalcode, $contact->otherstate, $contact->otherstreet, $props, $contactprops);
1599
1600
        //set the mailing address and its type
1601
        if (isset($props[$contactprops["businessaddress"]])) {
1602
            $props[$contactprops["mailingaddress"]] = 2;
1603
            $this->setMailingAddress($contact->businesscity, $contact->businesscountry, $contact->businesspostalcode, $contact->businessstate, $contact->businessstreet, $props[$contactprops["businessaddress"]], $props, $contactprops);
1604
        }
1605
        elseif (isset($props[$contactprops["homeaddress"]])) {
1606
            $props[$contactprops["mailingaddress"]] = 1;
1607
            $this->setMailingAddress($contact->homecity, $contact->homecountry, $contact->homepostalcode, $contact->homestate, $contact->homestreet, $props[$contactprops["homeaddress"]], $props, $contactprops);
1608
        }
1609
        elseif (isset($props[$contactprops["otheraddress"]])) {
1610
            $props[$contactprops["mailingaddress"]] = 3;
1611
            $this->setMailingAddress($contact->othercity, $contact->othercountry, $contact->otherpostalcode, $contact->otherstate, $contact->otherstreet, $props[$contactprops["otheraddress"]], $props, $contactprops);
1612
        }
1613
1614
        if (isset($contact->picture)) {
1615
            $picbinary = base64_decode($contact->picture);
1616
            $picsize = strlen($picbinary);
1617
            $props[$contactprops["haspic"]] = false;
1618
1619
            // TODO contact picture handling
1620
            // check if contact has already got a picture. delete it first in that case
1621
            // delete it also if it was removed on a mobile
1622
            $picprops = mapi_getprops($mapimessage, array($contactprops["haspic"]));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1622
            $picprops = /** @scrutinizer ignore-call */ mapi_getprops($mapimessage, array($contactprops["haspic"]));
Loading history...
1623
            if (isset($picprops[$contactprops["haspic"]]) && $picprops[$contactprops["haspic"]]) {
1624
                ZLog::Write(LOGLEVEL_DEBUG, "Contact already has a picture. Delete it");
1625
1626
                $attachtable = mapi_message_getattachmenttable($mapimessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_getattachmenttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1626
                $attachtable = /** @scrutinizer ignore-call */ mapi_message_getattachmenttable($mapimessage);
Loading history...
1627
                mapi_table_restrict($attachtable, MAPIUtils::GetContactPicRestriction());
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1627
                /** @scrutinizer ignore-call */ 
1628
                mapi_table_restrict($attachtable, MAPIUtils::GetContactPicRestriction());
Loading history...
1628
                $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1628
                $rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM));
Loading history...
1629
                if (isset($rows) && is_array($rows)) {
1630
                    foreach ($rows as $row) {
1631
                        mapi_message_deleteattach($mapimessage, $row[PR_ATTACH_NUM]);
1 ignored issue
show
Bug introduced by
The function mapi_message_deleteattach was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1631
                        /** @scrutinizer ignore-call */ 
1632
                        mapi_message_deleteattach($mapimessage, $row[PR_ATTACH_NUM]);
Loading history...
1632
                    }
1633
                }
1634
            }
1635
1636
            // only set picture if there's data in the request
1637
            if ($picbinary !== false && $picsize > 0) {
1638
                $props[$contactprops["haspic"]] = true;
1639
                $pic = mapi_message_createattach($mapimessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_createattach was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1639
                $pic = /** @scrutinizer ignore-call */ mapi_message_createattach($mapimessage);
Loading history...
1640
                // Set properties of the attachment
1641
                $picprops = array(
1642
                    PR_ATTACH_LONG_FILENAME => "ContactPicture.jpg",
1643
                    PR_DISPLAY_NAME => "ContactPicture.jpg",
1644
                    0x7FFF000B => true,
1645
                    PR_ATTACHMENT_HIDDEN => false,
1646
                    PR_ATTACHMENT_FLAGS => 1,
1647
                    PR_ATTACH_METHOD => ATTACH_BY_VALUE,
1648
                    PR_ATTACH_EXTENSION => ".jpg",
1649
                    PR_ATTACH_NUM => 1,
1650
                    PR_ATTACH_SIZE => $picsize,
1651
                    PR_ATTACH_DATA_BIN => $picbinary,
1652
                );
1653
1654
                mapi_setprops($pic, $picprops);
1655
                mapi_savechanges($pic);
1 ignored issue
show
Bug introduced by
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1655
                /** @scrutinizer ignore-call */ 
1656
                mapi_savechanges($pic);
Loading history...
1656
            }
1657
        }
1658
1659
        if (isset($contact->asbody)) {
1660
            $this->setASbody($contact->asbody, $props, $contactprops);
1661
        }
1662
1663
        //set fileas
1664
        if (defined('FILEAS_ORDER')) {
1665
            $lastname = (isset($contact->lastname)) ? $contact->lastname : "";
1666
            $firstname = (isset($contact->firstname)) ? $contact->firstname : "";
1667
            $middlename = (isset($contact->middlename)) ? $contact->middlename : "";
1668
            $company = (isset($contact->companyname)) ? $contact->companyname : "";
1669
            $props[$contactprops["fileas"]] = Utils::BuildFileAs($lastname, $firstname, $middlename, $company);
1670
        }
1671
        else ZLog::Write(LOGLEVEL_DEBUG, "FILEAS_ORDER not defined");
1672
1673
        mapi_setprops($mapimessage, $props);
1674
        return true;
1675
    }
1676
1677
    /**
1678
     * Writes a SyncTask to MAPI
1679
     *
1680
     * @param mixed             $mapimessage
1681
     * @param SyncTask          $task
1682
     *
1683
     * @access private
1684
     * @return boolean
1685
     */
1686
    private function setTask($mapimessage, $task) {
1687
        mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Task"));
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1687
        /** @scrutinizer ignore-call */ 
1688
        mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Task"));
Loading history...
1688
1689
        $taskmapping = MAPIMapping::GetTaskMapping();
1690
        $taskprops = MAPIMapping::GetTaskProperties();
1691
        $this->setPropsInMAPI($mapimessage, $task, $taskmapping);
1692
        $taskprops = array_merge($this->getPropIdsFromStrings($taskmapping), $this->getPropIdsFromStrings($taskprops));
1693
1694
        // task specific properties to be set
1695
        $props = array();
1696
1697
        if (isset($task->asbody)) {
1698
            $this->setASbody($task->asbody, $props, $taskprops);
1699
        }
1700
1701
        if(isset($task->complete)) {
1702
            if($task->complete) {
1703
                // Set completion to 100%
1704
                // Set status to 'complete'
1705
                $props[$taskprops["completion"]] = 1.0;
1706
                $props[$taskprops["status"]] = 2;
1707
                $props[$taskprops["reminderset"]] = false;
1708
            } else {
1709
                // Set completion to 0%
1710
                // Set status to 'not started'
1711
                $props[$taskprops["completion"]] = 0.0;
1712
                $props[$taskprops["status"]] = 0;
1713
            }
1714
        }
1715
        if (isset($task->recurrence) && class_exists('TaskRecurrence')) {
1716
            $deadoccur = false;
1717
            if ((isset($task->recurrence->occurrences) && $task->recurrence->occurrences == 1) ||
1718
                (isset($task->recurrence->deadoccur) && $task->recurrence->deadoccur == 1)) //ios5 sends deadoccur inside the recurrence
1719
                $deadoccur = true;
1720
1721
            // Set PR_ICON_INDEX to 1281 to show correct icon in category view
1722
            $props[$taskprops["icon"]] = 1281;
1723
            // dead occur - false if new occurrences should be generated from the task
1724
            // true - if it is the last occurrence of the task
1725
            $props[$taskprops["deadoccur"]] = $deadoccur;
1726
            $props[$taskprops["isrecurringtag"]] = true;
1727
1728
            $recurrence = new TaskRecurrence($this->store, $mapimessage);
1729
            $recur = array();
1730
            $this->setRecurrence($task, $recur);
1731
1732
            // task specific recurrence properties which we need to set here
1733
            // "start" and "end" are in GMT when passing to class.recurrence
1734
            // set recurrence start here because it's calculated differently for tasks and appointments
1735
            $recur["start"] = $task->recurrence->start;
1736
            $recur["regen"] = (isset($task->recurrence->regenerate) && $task->recurrence->regenerate) ? 1 : 0;
1737
            // OL regenerates recurring task itself, but setting deleteOccurrence is required so that PHP-MAPI doesn't regenerate
1738
            // completed occurrence of a task.
1739
            if ($recur["regen"] == 0) {
1740
                $recur["deleteOccurrence"] = 0;
1741
            }
1742
            //Also add dates to $recur
1743
            $recur["duedate"] = $task->duedate;
1744
            $recur["complete"] = (isset($task->complete) && $task->complete) ? 1 : 0;
1745
            if (isset($task->datecompleted)) {
1746
                $recur["datecompleted"] = $task->datecompleted;
1747
            }
1748
            $recurrence->setRecurrence($recur);
1749
        }
1750
1751
        $props[$taskprops["private"]] = (isset($task->sensitivity) && $task->sensitivity >= SENSITIVITY_PRIVATE) ? true : false;
1752
1753
        // Open address book for user resolve to set the owner
1754
        $addrbook = $this->getAddressbook();
0 ignored issues
show
Unused Code introduced by
The assignment to $addrbook is dead and can be removed.
Loading history...
1755
1756
        // check if there is already an owner for the task, set current user if not
1757
        $p = array( $taskprops["owner"]);
1758
        $owner = $this->getProps($mapimessage, $p);
1759
        if (!isset($owner[$taskprops["owner"]])) {
1760
            $userinfo = nsp_getuserinfo(Request::GetUser());
1 ignored issue
show
Bug introduced by
The function nsp_getuserinfo was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1760
            $userinfo = /** @scrutinizer ignore-call */ nsp_getuserinfo(Request::GetUser());
Loading history...
1761
            if(mapi_last_hresult() == NOERROR && isset($userinfo["fullname"])) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1761
            if(/** @scrutinizer ignore-call */ mapi_last_hresult() == NOERROR && isset($userinfo["fullname"])) {
Loading history...
1762
                $props[$taskprops["owner"]] = $userinfo["fullname"];
1763
            }
1764
        }
1765
        mapi_setprops($mapimessage, $props);
1766
        return true;
1767
    }
1768
1769
    /**
1770
    * Writes a SyncNote to MAPI
1771
    *
1772
    * @param mixed             $mapimessage
1773
    * @param SyncNote          $note
1774
    *
1775
    * @access private
1776
    * @return boolean
1777
    */
1778
    private function setNote($mapimessage, $note) {
1779
        // Touchdown does not send categories if all are unset or there is none.
1780
        // Setting it to an empty array will unset the property in KC as well
1781
        if (!isset($note->categories)) $note->categories = array();
1782
1783
        // update icon index to correspond to the color
1784
        if (isset($note->Color) && $note->Color > -1 && $note->Color < 5) {
1785
            $note->Iconindex = 768 + $note->Color;
0 ignored issues
show
Bug introduced by
The property Iconindex does not seem to exist on SyncNote.
Loading history...
1786
        }
1787
1788
        $this->setPropsInMAPI($mapimessage, $note, MAPIMapping::GetNoteMapping());
1789
1790
        $noteprops = MAPIMapping::GetNoteProperties();
1791
        $noteprops = $this->getPropIdsFromStrings($noteprops);
1792
1793
        // note specific properties to be set
1794
        $props = array();
1795
        $props[$noteprops["messageclass"]] = "IPM.StickyNote";
1796
        // set body otherwise the note will be "broken" when editing it in outlook
1797
        if (isset($note->asbody)) {
1798
            $this->setASbody($note->asbody, $props, $noteprops);
1799
        }
1800
1801
        $props[$noteprops["internetcpid"]] = INTERNET_CPID_UTF8;
1802
        mapi_setprops($mapimessage, $props);
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1802
        /** @scrutinizer ignore-call */ 
1803
        mapi_setprops($mapimessage, $props);
Loading history...
1803
        return true;
1804
    }
1805
1806
    /**----------------------------------------------------------------------------------------------------------
1807
     * HELPER
1808
     */
1809
1810
    /**
1811
     * Returns the timestamp offset.
1812
     *
1813
     * @param string            $ts
1814
     *
1815
     * @access private
1816
     * @return long
1817
     */
1818
    private function GetTZOffset($ts) {
1819
        $Offset = date("O", $ts);
0 ignored issues
show
Bug introduced by
$ts of type string is incompatible with the type integer|null expected by parameter $timestamp of date(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1819
        $Offset = date("O", /** @scrutinizer ignore-type */ $ts);
Loading history...
1820
1821
        $Parity = $Offset < 0 ? -1 : 1;
1822
        $Offset = $Parity * $Offset;
1823
        $Offset = ($Offset - ($Offset % 100)) / 100 * 60 + $Offset % 100;
1824
1825
        return $Parity * $Offset;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $Parity * $Offset returns the type integer which is incompatible with the documented return type long.
Loading history...
1826
    }
1827
1828
    /**
1829
     * Localtime of the timestamp.
1830
     *
1831
     * @param long              $time
1832
     *
1833
     * @access private
1834
     * @return array
1835
     */
1836
    private function gmtime($time) {
1837
        $TZOffset = $this->GetTZOffset($time);
1838
1839
        $t_time = $time - $TZOffset * 60; #Counter adjust for localtime()
1840
        $t_arr = localtime($t_time, 1);
1841
1842
        return $t_arr;
1843
    }
1844
1845
    /**
1846
     * Sets the properties in a MAPI object according to an Sync object and a property mapping.
1847
     *
1848
     * @param mixed             $mapimessage
1849
     * @param SyncObject        $message
1850
     * @param array             $mapping
1851
     *
1852
     * @access private
1853
     * @return
1854
     */
1855
    private function setPropsInMAPI($mapimessage, $message, $mapping) {
1856
        $mapiprops = $this->getPropIdsFromStrings($mapping);
1857
        $unsetVars = $message->getUnsetVars();
1858
        $propsToDelete = array();
1859
        $propsToSet = array();
1860
1861
        foreach ($mapiprops as $asprop => $mapiprop) {
1862
            if(isset($message->$asprop)) {
1863
1864
                // UTF8->windows1252.. this is ok for all numerical values
1865
                if(mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY) {
1 ignored issue
show
Bug introduced by
The function mapi_prop_type was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1865
                if(/** @scrutinizer ignore-call */ mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY) {
Loading history...
1866
                    if(is_array($message->$asprop))
1867
                        $value = array_map("u2wi", $message->$asprop);
1868
                    else
1869
                        $value = u2wi($message->$asprop);
1870
                } else {
1871
                    $value = $message->$asprop;
1872
                }
1873
1874
                // Make sure the php values are the correct type
1875
                switch(mapi_prop_type($mapiprop)) {
1876
                    case PT_BINARY:
1877
                    case PT_STRING8:
1878
                        settype($value, "string");
1879
                        break;
1880
                    case PT_BOOLEAN:
1881
                        settype($value, "boolean");
1882
                        break;
1883
                    case PT_SYSTIME:
1884
                    case PT_LONG:
1885
                        settype($value, "integer");
1886
                        break;
1887
                }
1888
1889
                // decode base64 value
1890
                if($mapiprop == PR_RTF_COMPRESSED) {
1891
                    $value = base64_decode($value);
1892
                    if(strlen($value) == 0)
1893
                        continue; // PDA will sometimes give us an empty RTF, which we'll ignore.
1894
1895
                    // Note that you can still remove notes because when you remove notes it gives
1896
                    // a valid compressed RTF with nothing in it.
1897
1898
                }
1899
                // if an "empty array" is to be saved, it the mvprop should be deleted - fixes Mantis #468
1900
                if (is_array($value) && empty($value)) {
1901
                    $propsToDelete[] = $mapiprop;
1902
                    ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->setPropsInMAPI(): Property '%s' to be deleted as it is an empty array", $asprop));
1903
                }
1904
                else {
1905
                    // all properties will be set at once
1906
                    $propsToSet[$mapiprop] = $value;
1907
                }
1908
            }
1909
            elseif (in_array($asprop, $unsetVars)) {
1910
                $propsToDelete[] = $mapiprop;
1911
            }
1912
        }
1913
1914
        mapi_setprops($mapimessage, $propsToSet);
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1914
        /** @scrutinizer ignore-call */ 
1915
        mapi_setprops($mapimessage, $propsToSet);
Loading history...
1915
        if (mapi_last_hresult()) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1915
        if (/** @scrutinizer ignore-call */ mapi_last_hresult()) {
Loading history...
1916
            ZLog::Write(LOGLEVEL_WARN, sprintf("Failed to set properties, trying to set them separately. Error code was:%x", mapi_last_hresult()));
1917
            $this->setPropsIndividually($mapimessage, $propsToSet, $mapiprops);
1918
        }
1919
1920
        mapi_deleteprops($mapimessage, $propsToDelete);
1 ignored issue
show
Bug introduced by
The function mapi_deleteprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1920
        /** @scrutinizer ignore-call */ 
1921
        mapi_deleteprops($mapimessage, $propsToDelete);
Loading history...
1921
1922
        //clean up
1923
        unset($unsetVars, $propsToDelete);
1924
    }
1925
1926
    /**
1927
     * Sets the properties one by one in a MAPI object.
1928
     *
1929
     * @param mixed             &$mapimessage
1930
     * @param array             &$propsToSet
1931
     * @param array             &$mapiprops
1932
     *
1933
     * @access private
1934
     * @return
1935
     */
1936
    private function setPropsIndividually(&$mapimessage, &$propsToSet, &$mapiprops) {
1937
        foreach ($propsToSet as $prop => $value) {
1938
            mapi_setprops($mapimessage, array($prop => $value));
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1938
            /** @scrutinizer ignore-call */ 
1939
            mapi_setprops($mapimessage, array($prop => $value));
Loading history...
1939
            if (mapi_last_hresult()) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1939
            if (/** @scrutinizer ignore-call */ mapi_last_hresult()) {
Loading history...
1940
                ZLog::Write(LOGLEVEL_ERROR, sprintf("Failed setting property [%s] with value [%s], error code was:%x", array_search($prop, $mapiprops), $value, mapi_last_hresult()));
1941
            }
1942
        }
1943
1944
    }
1945
1946
    /**
1947
     * Gets the properties from a MAPI object and sets them in the Sync object according to mapping.
1948
     *
1949
     * @param SyncObject        &$message
1950
     * @param mixed             $mapimessage
1951
     * @param array             $mapping
1952
     *
1953
     * @access private
1954
     * @return
1955
     */
1956
    private function getPropsFromMAPI(&$message, $mapimessage, $mapping) {
1957
        $messageprops = $this->getProps($mapimessage, $mapping);
1958
        foreach ($mapping as $asprop => $mapiprop) {
1959
             // Get long strings via openproperty
1960
            if (isset($messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))])) {
2 ignored issues
show
Bug introduced by
The function mapi_prop_id was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1960
            if (isset($messageprops[mapi_prop_tag(PT_ERROR, /** @scrutinizer ignore-call */ mapi_prop_id($mapiprop))])) {
Loading history...
Bug introduced by
The function mapi_prop_tag was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1960
            if (isset($messageprops[/** @scrutinizer ignore-call */ mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))])) {
Loading history...
1961
                if ($messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))] == MAPI_E_NOT_ENOUGH_MEMORY_32BIT ||
1962
                    $messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))] == MAPI_E_NOT_ENOUGH_MEMORY_64BIT) {
1963
                    $messageprops[$mapiprop] = MAPIUtils::readPropStream($mapimessage, $mapiprop);
1964
                }
1965
            }
1966
1967
            if(isset($messageprops[$mapiprop])) {
1968
                if(mapi_prop_type($mapiprop) == PT_BOOLEAN) {
1 ignored issue
show
Bug introduced by
The function mapi_prop_type was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1968
                if(/** @scrutinizer ignore-call */ mapi_prop_type($mapiprop) == PT_BOOLEAN) {
Loading history...
1969
                    // Force to actual '0' or '1'
1970
                    if($messageprops[$mapiprop])
1971
                        $message->$asprop = 1;
1972
                    else
1973
                        $message->$asprop = 0;
1974
                } else {
1975
                    // Special handling for PR_MESSAGE_FLAGS
1976
                    if($mapiprop == PR_MESSAGE_FLAGS)
1977
                        $message->$asprop = $messageprops[$mapiprop] & 1; // only look at 'read' flag
1978
                    else if($mapiprop == PR_RTF_COMPRESSED)
1979
                        //do not send rtf to the mobile
1980
                        continue;
1981
                    else if(is_array($messageprops[$mapiprop]))
1982
                        $message->$asprop = array_map("w2u", $messageprops[$mapiprop]);
1983
                    else {
1984
                        if(mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY)
1985
                            $message->$asprop = w2u($messageprops[$mapiprop]);
1986
                        else
1987
                            $message->$asprop = $messageprops[$mapiprop];
1988
                    }
1989
                }
1990
            }
1991
        }
1992
    }
1993
1994
    /**
1995
     * Wraps getPropIdsFromStrings() calls.
1996
     *
1997
     * @param mixed             &$mapiprops
1998
     *
1999
     * @access private
2000
     * @return
2001
     */
2002
    private function getPropIdsFromStrings(&$mapiprops) {
2003
        return getPropIdsFromStrings($this->store, $mapiprops);
2004
    }
2005
2006
    /**
2007
     * Wraps mapi_getprops() calls.
2008
     *
2009
     * @param mixed             &$mapiprops
2010
     *
2011
     * @access private
2012
     * @return
2013
     */
2014
    protected function getProps($mapimessage, &$mapiproperties) {
2015
        $mapiproperties = $this->getPropIdsFromStrings($mapiproperties);
2016
        return mapi_getprops($mapimessage, $mapiproperties);
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2016
        return /** @scrutinizer ignore-call */ mapi_getprops($mapimessage, $mapiproperties);
Loading history...
2017
    }
2018
2019
    /**
2020
     * Returns an GMT timezone array.
2021
     *
2022
     * @access private
2023
     * @return array
2024
     */
2025
    private function getGMTTZ() {
2026
        $tz = array(
2027
            "bias" => 0,
2028
            "tzname" => "",
2029
            "dstendyear" => 0,
2030
            "dstendmonth" => 10,
2031
            "dstendday" => 0,
2032
            "dstendweek" => 5,
2033
            "dstendhour" => 2,
2034
            "dstendminute" => 0,
2035
            "dstendsecond" => 0,
2036
            "dstendmillis" => 0,
2037
            "stdbias" => 0,
2038
            "tznamedst" => "",
2039
            "dststartyear" => 0,
2040
            "dststartmonth" => 3,
2041
            "dststartday" => 0,
2042
            "dststartweek" => 5,
2043
            "dststarthour" => 1,
2044
            "dststartminute" => 0,
2045
            "dststartsecond" => 0,
2046
            "dststartmillis" => 0,
2047
            "dstbias" => -60
2048
    );
2049
2050
        return $tz;
2051
    }
2052
2053
    /**
2054
     * Unpack timezone info from MAPI.
2055
     *
2056
     * @param string    $data
2057
     *
2058
     * @access private
2059
     * @return array
2060
     */
2061
    private function getTZFromMAPIBlob($data) {
2062
        $unpacked = unpack("lbias/lstdbias/ldstbias/" .
2063
                           "vconst1/vdstendyear/vdstendmonth/vdstendday/vdstendweek/vdstendhour/vdstendminute/vdstendsecond/vdstendmillis/" .
2064
                           "vconst2/vdststartyear/vdststartmonth/vdststartday/vdststartweek/vdststarthour/vdststartminute/vdststartsecond/vdststartmillis", $data);
2065
        return $unpacked;
2066
    }
2067
2068
    /**
2069
     * Unpack timezone info from Sync.
2070
     *
2071
     * @param string    $data
2072
     *
2073
     * @access private
2074
     * @return array
2075
     */
2076
    private function getTZFromSyncBlob($data) {
2077
        $tz = unpack(   "lbias/a64tzname/vdstendyear/vdstendmonth/vdstendday/vdstendweek/vdstendhour/vdstendminute/vdstendsecond/vdstendmillis/" .
2078
                        "lstdbias/a64tznamedst/vdststartyear/vdststartmonth/vdststartday/vdststartweek/vdststarthour/vdststartminute/vdststartsecond/vdststartmillis/" .
2079
                        "ldstbias", $data);
2080
2081
        // Make the structure compatible with class.recurrence.php
2082
        $tz["timezone"] = $tz["bias"];
2083
        $tz["timezonedst"] = $tz["dstbias"];
2084
2085
        return $tz;
2086
    }
2087
2088
    /**
2089
     * Pack timezone info for MAPI.
2090
     *
2091
     * @param array     $tz
2092
     *
2093
     * @access private
2094
     * @return string
2095
     */
2096
    private function getMAPIBlobFromTZ($tz) {
2097
        $packed = pack("lll" . "vvvvvvvvv" . "vvvvvvvvv",
2098
                      $tz["bias"], $tz["stdbias"], $tz["dstbias"],
2099
                      0, 0, $tz["dstendmonth"], $tz["dstendday"], $tz["dstendweek"], $tz["dstendhour"], $tz["dstendminute"], $tz["dstendsecond"], $tz["dstendmillis"],
2100
                      0, 0, $tz["dststartmonth"], $tz["dststartday"], $tz["dststartweek"], $tz["dststarthour"], $tz["dststartminute"], $tz["dststartsecond"], $tz["dststartmillis"]);
2101
2102
        return $packed;
2103
    }
2104
2105
    /**
2106
     * Checks the date to see if it is in DST, and returns correct GMT date accordingly.
2107
     *
2108
     * @param long      $localtime
2109
     * @param array     $tz
2110
     *
2111
     * @access private
2112
     * @return long
2113
     */
2114
    private function getGMTTimeByTZ($localtime, $tz) {
2115
        if(!isset($tz) || !is_array($tz))
2116
            return $localtime;
2117
2118
        if($this->isDST($localtime, $tz))
2119
            return $localtime + $tz["bias"]*60 + $tz["dstbias"]*60;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $localtime + $tz[...0 + $tz['dstbias'] * 60 returns the type integer which is incompatible with the documented return type long.
Loading history...
2120
        else
2121
            return $localtime + $tz["bias"]*60;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $localtime + $tz['bias'] * 60 returns the type integer which is incompatible with the documented return type long.
Loading history...
2122
    }
2123
2124
    /**
2125
     * Returns the local time for the given GMT time, taking account of the given timezone.
2126
     *
2127
     * @param long      $gmttime
2128
     * @param array     $tz
2129
     *
2130
     * @access private
2131
     * @return long
2132
     */
2133
    private function getLocaltimeByTZ($gmttime, $tz) {
2134
        if(!isset($tz) || !is_array($tz))
2135
            return $gmttime;
2136
2137
        if($this->isDST($gmttime - $tz["bias"]*60, $tz)) // may bug around the switch time because it may have to be 'gmttime - bias - dstbias'
0 ignored issues
show
Bug introduced by
$gmttime - $tz['bias'] * 60 of type integer is incompatible with the type long expected by parameter $localtime of MAPIProvider::isDST(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2137
        if($this->isDST(/** @scrutinizer ignore-type */ $gmttime - $tz["bias"]*60, $tz)) // may bug around the switch time because it may have to be 'gmttime - bias - dstbias'
Loading history...
2138
            return $gmttime - $tz["bias"]*60 - $tz["dstbias"]*60;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $gmttime - $tz['b...0 - $tz['dstbias'] * 60 returns the type integer which is incompatible with the documented return type long.
Loading history...
2139
        else
2140
            return $gmttime - $tz["bias"]*60;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $gmttime - $tz['bias'] * 60 returns the type integer which is incompatible with the documented return type long.
Loading history...
2141
    }
2142
2143
    /**
2144
     * Returns TRUE if it is the summer and therefore DST is in effect.
2145
     *
2146
     * @param long      $localtime
2147
     * @param array     $tz
2148
     *
2149
     * @access private
2150
     * @return boolean
2151
     */
2152
    private function isDST($localtime, $tz) {
2153
        if( !isset($tz) || !is_array($tz) ||
2154
            !isset($tz["dstbias"]) || $tz["dstbias"] == 0 ||
2155
            !isset($tz["dststartmonth"]) || $tz["dststartmonth"] == 0 ||
2156
            !isset($tz["dstendmonth"]) || $tz["dstendmonth"] == 0)
2157
            return false;
2158
2159
        $year = gmdate("Y", $localtime);
0 ignored issues
show
Bug introduced by
$localtime of type long is incompatible with the type integer|null expected by parameter $timestamp of gmdate(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2159
        $year = gmdate("Y", /** @scrutinizer ignore-type */ $localtime);
Loading history...
2160
        $start = $this->getTimestampOfWeek($year, $tz["dststartmonth"], $tz["dststartweek"], $tz["dststartday"], $tz["dststarthour"], $tz["dststartminute"], $tz["dststartsecond"]);
0 ignored issues
show
Bug introduced by
$year of type string is incompatible with the type integer expected by parameter $year of MAPIProvider::getTimestampOfWeek(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2160
        $start = $this->getTimestampOfWeek(/** @scrutinizer ignore-type */ $year, $tz["dststartmonth"], $tz["dststartweek"], $tz["dststartday"], $tz["dststarthour"], $tz["dststartminute"], $tz["dststartsecond"]);
Loading history...
2161
        $end = $this->getTimestampOfWeek($year, $tz["dstendmonth"], $tz["dstendweek"], $tz["dstendday"], $tz["dstendhour"], $tz["dstendminute"], $tz["dstendsecond"]);
2162
2163
        if($start < $end) {
2164
            // northern hemisphere (july = dst)
2165
          if($localtime >= $start && $localtime < $end)
2166
              $dst = true;
2167
          else
2168
              $dst = false;
2169
        } else {
2170
            // southern hemisphere (january = dst)
2171
          if($localtime >= $end && $localtime < $start)
2172
              $dst = false;
2173
          else
2174
              $dst = true;
2175
        }
2176
2177
        return $dst;
2178
    }
2179
2180
    /**
2181
     * Returns the local timestamp for the $week'th $wday of $month in $year at $hour:$minute:$second.
2182
     *
2183
     * @param int       $year
2184
     * @param int       $month
2185
     * @param int       $week
2186
     * @param int       $wday
2187
     * @param int       $hour
2188
     * @param int       $minute
2189
     * @param int       $second
2190
     *
2191
     * @access private
2192
     * @return long
2193
     */
2194
    private function getTimestampOfWeek($year, $month, $week, $wday, $hour, $minute, $second) {
2195
        if ($month == 0)
2196
            return;
2197
2198
        $date = gmmktime($hour, $minute, $second, $month, 1, $year);
2199
2200
        // Find first day in month which matches day of the week
2201
        while(1) {
2202
            $wdaynow = gmdate("w", $date);
2203
            if($wdaynow == $wday)
2204
                break;
2205
            $date += 24 * 60 * 60;
2206
        }
2207
2208
        // Forward $week weeks (may 'overflow' into the next month)
2209
        $date = $date + $week * (24 * 60 * 60 * 7);
2210
2211
        // Reverse 'overflow'. Eg week '10' will always be the last week of the month in which the
2212
        // specified weekday exists
2213
        while(1) {
2214
            $monthnow = gmdate("n", $date); // gmdate returns 1-12
2215
            if($monthnow > $month)
2216
                $date = $date - (24 * 7 * 60 * 60);
2217
            else
2218
                break;
2219
        }
2220
2221
        return $date;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $date returns the type integer which is incompatible with the documented return type long.
Loading history...
2222
    }
2223
2224
    /**
2225
     * Normalize the given timestamp to the start of the day.
2226
     *
2227
     * @param long      $timestamp
2228
     *
2229
     * @access private
2230
     * @return long
2231
     */
2232
    private function getDayStartOfTimestamp($timestamp) {
2233
        return $timestamp - ($timestamp % (60 * 60 * 24));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $timestamp - $timestamp % 60 * 60 * 24 returns the type integer which is incompatible with the documented return type long.
Loading history...
2234
    }
2235
2236
    /**
2237
     * Returns an SMTP address from an entry id.
2238
     *
2239
     * @param string    $entryid
2240
     *
2241
     * @access private
2242
     * @return string
2243
     */
2244
    private function getSMTPAddressFromEntryID($entryid) {
2245
        $addrbook = $this->getAddressbook();
2246
2247
        $mailuser = mapi_ab_openentry($addrbook, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_ab_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2247
        $mailuser = /** @scrutinizer ignore-call */ mapi_ab_openentry($addrbook, $entryid);
Loading history...
2248
        if(!$mailuser)
2249
            return "";
2250
2251
        $props = mapi_getprops($mailuser, array(PR_ADDRTYPE, PR_SMTP_ADDRESS, PR_EMAIL_ADDRESS));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2251
        $props = /** @scrutinizer ignore-call */ mapi_getprops($mailuser, array(PR_ADDRTYPE, PR_SMTP_ADDRESS, PR_EMAIL_ADDRESS));
Loading history...
2252
2253
        $addrtype = isset($props[PR_ADDRTYPE]) ? $props[PR_ADDRTYPE] : "";
2254
2255
        if(isset($props[PR_SMTP_ADDRESS]))
2256
            return $props[PR_SMTP_ADDRESS];
2257
2258
        if($addrtype == "SMTP" && isset($props[PR_EMAIL_ADDRESS]))
2259
            return $props[PR_EMAIL_ADDRESS];
2260
        elseif ($addrtype == "ZARAFA" && isset($props[PR_EMAIL_ADDRESS])) {
2261
            $userinfo = nsp_getuserinfo($props[PR_EMAIL_ADDRESS]);
1 ignored issue
show
Bug introduced by
The function nsp_getuserinfo was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2261
            $userinfo = /** @scrutinizer ignore-call */ nsp_getuserinfo($props[PR_EMAIL_ADDRESS]);
Loading history...
2262
            if (is_array($userinfo) && isset($userinfo["primary_email"]))
2263
                return $userinfo["primary_email"];
2264
        }
2265
2266
        return "";
2267
    }
2268
2269
    /**
2270
     * Returns fullname from an entryid.
2271
     *
2272
     * @param binary $entryid
0 ignored issues
show
Bug introduced by
The type binary was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2273
     * @return string fullname or false on error
2274
     */
2275
    private function getFullnameFromEntryID($entryid) {
2276
        $addrbook = $this->getAddressbook();
2277
        $mailuser = mapi_ab_openentry($addrbook, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_ab_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2277
        $mailuser = /** @scrutinizer ignore-call */ mapi_ab_openentry($addrbook, $entryid);
Loading history...
2278
        if(!$mailuser) {
2279
            ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to get mailuser for getFullnameFromEntryID (0x%X)", mapi_last_hresult()));
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2279
            ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to get mailuser for getFullnameFromEntryID (0x%X)", /** @scrutinizer ignore-call */ mapi_last_hresult()));
Loading history...
2280
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
2281
        }
2282
2283
        $props = mapi_getprops($mailuser, array(PR_DISPLAY_NAME));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2283
        $props = /** @scrutinizer ignore-call */ mapi_getprops($mailuser, array(PR_DISPLAY_NAME));
Loading history...
2284
        if (isset($props[PR_DISPLAY_NAME])) {
2285
            return $props[PR_DISPLAY_NAME];
2286
        }
2287
        ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to get fullname for getFullnameFromEntryID (0x%X)", mapi_last_hresult()));
2288
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
2289
    }
2290
2291
    /**
2292
     * Builds a displayname from several separated values.
2293
     *
2294
     * @param SyncContact       $contact
2295
     *
2296
     * @access private
2297
     * @return string
2298
     */
2299
    private function composeDisplayName(&$contact) {
2300
        // Set display name and subject to a combined value of firstname and lastname
2301
        $cname = (isset($contact->prefix))?u2w($contact->prefix)." ":"";
0 ignored issues
show
Bug introduced by
Are you sure u2w($contact->prefix) of type false|mixed|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2301
        $cname = (isset($contact->prefix))?/** @scrutinizer ignore-type */ u2w($contact->prefix)." ":"";
Loading history...
2302
        $cname .= u2w($contact->firstname);
2303
        $cname .= (isset($contact->middlename))?" ". u2w($contact->middlename):"";
0 ignored issues
show
Bug introduced by
Are you sure u2w($contact->middlename) of type false|mixed|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2303
        $cname .= (isset($contact->middlename))?" ". /** @scrutinizer ignore-type */ u2w($contact->middlename):"";
Loading history...
2304
        $cname .= " ". u2w($contact->lastname);
0 ignored issues
show
Bug introduced by
Are you sure u2w($contact->lastname) of type false|mixed|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2304
        $cname .= " ". /** @scrutinizer ignore-type */ u2w($contact->lastname);
Loading history...
2305
        $cname .= (isset($contact->suffix))?" ". u2w($contact->suffix):"";
0 ignored issues
show
Bug introduced by
Are you sure u2w($contact->suffix) of type false|mixed|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2305
        $cname .= (isset($contact->suffix))?" ". /** @scrutinizer ignore-type */ u2w($contact->suffix):"";
Loading history...
2306
        return trim($cname);
2307
    }
2308
2309
    /**
2310
     * Sets all dependent properties for an email address.
2311
     *
2312
     * @param string            $emailAddress
2313
     * @param string            $displayName
2314
     * @param int               $cnt
2315
     * @param array             &$props
2316
     * @param array             &$properties
2317
     * @param array             &$nremails
2318
     * @param int               &$abprovidertype
2319
     *
2320
     * @access private
2321
     * @return
2322
     */
2323
    private function setEmailAddress($emailAddress, $displayName, $cnt, &$props, &$properties, &$nremails, &$abprovidertype){
2324
        if (isset($emailAddress)) {
2325
            $name = (isset($displayName)) ? $displayName : $emailAddress;
2326
2327
            $props[$properties["emailaddress$cnt"]] = $emailAddress;
2328
            $props[$properties["emailaddressdemail$cnt"]] = $emailAddress;
2329
            $props[$properties["emailaddressdname$cnt"]] = $name;
2330
            $props[$properties["emailaddresstype$cnt"]] = "SMTP";
2331
            $props[$properties["emailaddressentryid$cnt"]] = mapi_createoneoff($name, "SMTP", $emailAddress);
1 ignored issue
show
Bug introduced by
The function mapi_createoneoff was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2331
            $props[$properties["emailaddressentryid$cnt"]] = /** @scrutinizer ignore-call */ mapi_createoneoff($name, "SMTP", $emailAddress);
Loading history...
2332
            $nremails[] = $cnt - 1;
2333
            $abprovidertype |= 2 ^ ($cnt - 1);
2334
        }
2335
    }
2336
2337
    /**
2338
     * Sets the properties for an address string.
2339
     *
2340
     * @param string            $type               which address is being set
2341
     * @param string            $city
2342
     * @param string            $country
2343
     * @param string            $postalcode
2344
     * @param string            $state
2345
     * @param string            $street
2346
     * @param array             &$props
2347
     * @param array             &$properties
2348
     *
2349
     * @access private
2350
     * @return
2351
     */
2352
     private function setAddress($type, &$city, &$country, &$postalcode, &$state, &$street, &$props, &$properties) {
2353
        if (isset($city)) $props[$properties[$type."city"]] = $city = u2w($city);
2354
2355
        if (isset($country)) $props[$properties[$type."country"]] = $country = u2w($country);
2356
2357
        if (isset($postalcode)) $props[$properties[$type."postalcode"]] = $postalcode = u2w($postalcode);
2358
2359
        if (isset($state)) $props[$properties[$type."state"]] = $state = u2w($state);
2360
2361
        if (isset($street)) $props[$properties[$type."street"]] = $street = u2w($street);
2362
2363
        //set composed address
2364
        $address = Utils::BuildAddressString($street, $postalcode, $city, $state, $country);
2365
        if ($address) $props[$properties[$type."address"]] = $address;
2366
    }
2367
2368
    /**
2369
     * Sets the properties for a mailing address.
2370
     *
2371
     * @param string            $city
2372
     * @param string            $country
2373
     * @param string            $postalcode
2374
     * @param string            $state
2375
     * @param string            $street
2376
     * @param string            $address
2377
     * @param array             &$props
2378
     * @param array             &$properties
2379
     *
2380
     * @access private
2381
     * @return
2382
     */
2383
    private function setMailingAddress($city, $country, $postalcode,  $state, $street, $address, &$props, &$properties) {
2384
        if (isset($city)) $props[$properties["city"]] = $city;
2385
        if (isset($country)) $props[$properties["country"]] = $country;
2386
        if (isset($postalcode)) $props[$properties["postalcode"]] = $postalcode;
2387
        if (isset($state)) $props[$properties["state"]] = $state;
2388
        if (isset($street)) $props[$properties["street"]] = $street;
2389
        if (isset($address)) $props[$properties["postaladdress"]] = $address;
2390
    }
2391
2392
    /**
2393
     * Sets data in a recurrence array.
2394
     *
2395
     * @param SyncObject        $message
2396
     * @param array             &$recur
2397
     *
2398
     * @access private
2399
     * @return
2400
     */
2401
    private function setRecurrence($message, &$recur) {
2402
        if (isset($message->complete)) {
2403
            $recur["complete"] = $message->complete;
2404
        }
2405
2406
        if(!isset($message->recurrence->interval))
2407
            $message->recurrence->interval = 1;
2408
2409
        //set the default value of numoccur
2410
        $recur["numoccur"] = 0;
2411
        //a place holder for recurrencetype property
2412
        $recur["recurrencetype"] = 0;
2413
2414
        switch($message->recurrence->type) {
2415
            case 0:
2416
                $recur["type"] = 10;
2417
                if(isset($message->recurrence->dayofweek))
2418
                    $recur["subtype"] = 1;
2419
                else
2420
                    $recur["subtype"] = 0;
2421
2422
                $recur["everyn"] = $message->recurrence->interval * (60 * 24);
2423
                $recur["recurrencetype"] = 1;
2424
                break;
2425
            case 1:
2426
                $recur["type"] = 11;
2427
                $recur["subtype"] = 1;
2428
                $recur["everyn"] = $message->recurrence->interval;
2429
                $recur["recurrencetype"] = 2;
2430
                break;
2431
            case 2:
2432
                $recur["type"] = 12;
2433
                $recur["subtype"] = 2;
2434
                $recur["everyn"] = $message->recurrence->interval;
2435
                $recur["recurrencetype"] = 3;
2436
                break;
2437
            case 3:
2438
                $recur["type"] = 12;
2439
                $recur["subtype"] = 3;
2440
                $recur["everyn"] = $message->recurrence->interval;
2441
                $recur["recurrencetype"] = 3;
2442
                break;
2443
            case 4:
2444
                $recur["type"] = 13;
2445
                $recur["subtype"] = 1;
2446
                $recur["everyn"] = $message->recurrence->interval * 12;
2447
                $recur["recurrencetype"] = 4;
2448
                break;
2449
            case 5:
2450
                $recur["type"] = 13;
2451
                $recur["subtype"] = 2;
2452
                $recur["everyn"] = $message->recurrence->interval * 12;
2453
                $recur["recurrencetype"] = 4;
2454
                break;
2455
            case 6:
2456
                $recur["type"] = 13;
2457
                $recur["subtype"] = 3;
2458
                $recur["everyn"] = $message->recurrence->interval * 12;
2459
                $recur["recurrencetype"] = 4;
2460
                break;
2461
        }
2462
2463
        // "start" and "end" are in GMT when passing to class.recurrence
2464
        $recur["end"] = $this->getDayStartOfTimestamp(0x7fffffff); // Maximum GMT value for end by default
0 ignored issues
show
Bug introduced by
2147483647 of type integer is incompatible with the type long expected by parameter $timestamp of MAPIProvider::getDayStartOfTimestamp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2464
        $recur["end"] = $this->getDayStartOfTimestamp(/** @scrutinizer ignore-type */ 0x7fffffff); // Maximum GMT value for end by default
Loading history...
2465
2466
        if(isset($message->recurrence->until)) {
2467
            $recur["term"] = 0x21;
2468
            $recur["end"] = $message->recurrence->until;
2469
        } else if(isset($message->recurrence->occurrences)) {
2470
            $recur["term"] = 0x22;
2471
            $recur["numoccur"] = $message->recurrence->occurrences;
2472
        } else {
2473
            $recur["term"] = 0x23;
2474
        }
2475
2476
        if(isset($message->recurrence->dayofweek))
2477
            $recur["weekdays"] = $message->recurrence->dayofweek;
2478
        if(isset($message->recurrence->weekofmonth))
2479
            $recur["nday"] = $message->recurrence->weekofmonth;
2480
        if(isset($message->recurrence->monthofyear)) {
2481
            // MAPI stores months as the amount of minutes until the beginning of the month in a
2482
            // non-leapyear. Why this is, is totally unclear.
2483
            $monthminutes = array(0,44640,84960,129600,172800,217440,260640,305280,348480,393120,437760,480960);
2484
            $recur["month"] = $monthminutes[$message->recurrence->monthofyear-1];
2485
        }
2486
        if(isset($message->recurrence->dayofmonth))
2487
            $recur["monthday"] = $message->recurrence->dayofmonth;
2488
    }
2489
2490
    /**
2491
     * Extracts the email address (mailbox@host) from an email address because
2492
     * some devices send email address as "Firstname Lastname" <[email protected]>.
2493
     *
2494
     *  @link http://developer.berlios.de/mantis/view.php?id=486
2495
     *
2496
     *  @param string           $email
2497
     *
2498
     *  @access private
2499
     *  @return string or false on error
2500
     */
2501
    private function extractEmailAddress($email) {
2502
        if (!isset($this->zRFC822)) $this->zRFC822 = new Mail_RFC822();
2503
        $parsedAddress = $this->zRFC822->parseAddressList($email);
2504
        if (!isset($parsedAddress[0]->mailbox) || !isset($parsedAddress[0]->host)) return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
2505
2506
        return $parsedAddress[0]->mailbox.'@'.$parsedAddress[0]->host;
2507
    }
2508
2509
    /**
2510
     * Returns the message body for a required format.
2511
     *
2512
     * @param MAPIMessage       $mapimessage
0 ignored issues
show
Bug introduced by
The type MAPIMessage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2513
     * @param int               $bpReturnType
2514
     * @param SyncObject        $message
2515
     *
2516
     * @access private
2517
     * @return boolean
2518
     */
2519
    private function setMessageBodyForType($mapimessage, $bpReturnType, &$message) {
2520
        $truncateHtmlSafe = false;
2521
        //default value is PR_BODY
2522
        $property = PR_BODY;
2523
        switch ($bpReturnType) {
2524
            case SYNC_BODYPREFERENCE_HTML:
2525
                $property = PR_HTML;
2526
                $truncateHtmlSafe = true;
2527
                break;
2528
            case SYNC_BODYPREFERENCE_RTF:
2529
                $property = PR_RTF_COMPRESSED;
2530
                break;
2531
            case SYNC_BODYPREFERENCE_MIME:
2532
                $stat = $this->imtoinet($mapimessage, $message);
2533
                if (isset($message->asbody))
2534
                    $message->asbody->type = $bpReturnType;
2535
                return $stat;
2536
        }
2537
2538
        $stream = mapi_openproperty($mapimessage, $property, IID_IStream, 0, 0);
1 ignored issue
show
Bug introduced by
The function mapi_openproperty was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2538
        $stream = /** @scrutinizer ignore-call */ mapi_openproperty($mapimessage, $property, IID_IStream, 0, 0);
Loading history...
2539
        if ($stream) {
2540
            $stat = mapi_stream_stat($stream);
1 ignored issue
show
Bug introduced by
The function mapi_stream_stat was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2540
            $stat = /** @scrutinizer ignore-call */ mapi_stream_stat($stream);
Loading history...
2541
            $streamsize = $stat['cb'];
2542
        }
2543
        else {
2544
            $streamsize = 0;
2545
        }
2546
2547
        //set the properties according to supported AS version
2548
        if (Request::GetProtocolVersion() >= 12.0) {
2549
            $message->asbody = new SyncBaseBody();
2550
            $message->asbody->type = $bpReturnType;
2551
            if ($bpReturnType == SYNC_BODYPREFERENCE_RTF) {
2552
                $body = $this->mapiReadStream($stream, $streamsize);
2553
                $message->asbody->data = StringStreamWrapper::Open(base64_encode($body));
2554
            }
2555
            elseif (isset($message->internetcpid) && $bpReturnType == SYNC_BODYPREFERENCE_HTML) {
2556
                // if PR_HTML is UTF-8 we can stream it directly, else we have to convert to UTF-8 & wrap it
2557
                if ($message->internetcpid == INTERNET_CPID_UTF8) {
2558
                    $message->asbody->data = MAPIStreamWrapper::Open($stream, $truncateHtmlSafe);
2559
                }
2560
                else {
2561
                    $body = $this->mapiReadStream($stream, $streamsize);
2562
                    $message->asbody->data = StringStreamWrapper::Open(Utils::ConvertCodepageStringToUtf8($message->internetcpid, $body), $truncateHtmlSafe);
2563
                    $message->internetcpid = INTERNET_CPID_UTF8;
2564
                }
2565
            }
2566
            else {
2567
                $message->asbody->data = MAPIStreamWrapper::Open($stream);
2568
            }
2569
            $message->asbody->estimatedDataSize = $streamsize;
2570
        }
2571
        else {
2572
            $body = $this->mapiReadStream($stream, $streamsize);
2573
            $message->body = str_replace("\n","\r\n", w2u(str_replace("\r", "", $body)));
2574
            $message->bodysize = $streamsize;
2575
            $message->bodytruncated = 0;
2576
        }
2577
2578
        return true;
2579
    }
2580
2581
    /**
2582
     * Reads from a mapi stream, if it's set. If not, returns an empty string.
2583
     *
2584
     * @param resource $stream
2585
     * @param int $size
2586
     *
2587
     * @access private
2588
     * @return string
2589
     */
2590
    private function mapiReadStream($stream, $size) {
2591
        if (!$stream || $size == 0) {
0 ignored issues
show
introduced by
$stream is of type resource, thus it always evaluated to false.
Loading history...
2592
            return "";
2593
        }
2594
        return mapi_stream_read($stream, $size);
1 ignored issue
show
Bug introduced by
The function mapi_stream_read was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2594
        return /** @scrutinizer ignore-call */ mapi_stream_read($stream, $size);
Loading history...
2595
    }
2596
2597
    /**
2598
     * A wrapper for mapi_inetmapi_imtoinet function.
2599
     *
2600
     * @param MAPIMessage       $mapimessage
2601
     * @param SyncObject        $message
2602
     *
2603
     * @access private
2604
     * @return boolean
2605
     */
2606
    private function imtoinet($mapimessage, &$message) {
2607
        $mapiEmail = mapi_getprops($mapimessage, array(PR_EC_IMAP_EMAIL));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2607
        $mapiEmail = /** @scrutinizer ignore-call */ mapi_getprops($mapimessage, array(PR_EC_IMAP_EMAIL));
Loading history...
2608
        $stream = false;
2609
        if (isset($mapiEmail[PR_EC_IMAP_EMAIL]) || MAPIUtils::GetError(PR_EC_IMAP_EMAIL, $mapiEmail) == MAPI_E_NOT_ENOUGH_MEMORY) {
2610
            $stream = mapi_openproperty($mapimessage, PR_EC_IMAP_EMAIL, IID_IStream, 0, 0);
1 ignored issue
show
Bug introduced by
The function mapi_openproperty was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2610
            $stream = /** @scrutinizer ignore-call */ mapi_openproperty($mapimessage, PR_EC_IMAP_EMAIL, IID_IStream, 0, 0);
Loading history...
2611
            ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->imtoinet(): using PR_EC_IMAP_EMAIL as full RFC822 message");
2612
        }
2613
        else {
2614
            $addrbook = $this->getAddressbook();
2615
            $stream = mapi_inetmapi_imtoinet($this->session, $addrbook, $mapimessage, array('use_tnef' => -1, 'ignore_missing_attachments' => 1));
1 ignored issue
show
Bug introduced by
The function mapi_inetmapi_imtoinet was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2615
            $stream = /** @scrutinizer ignore-call */ mapi_inetmapi_imtoinet($this->session, $addrbook, $mapimessage, array('use_tnef' => -1, 'ignore_missing_attachments' => 1));
Loading history...
2616
        }
2617
        if (is_resource($stream)) {
2618
            $mstreamstat = mapi_stream_stat($stream);
1 ignored issue
show
Bug introduced by
The function mapi_stream_stat was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2618
            $mstreamstat = /** @scrutinizer ignore-call */ mapi_stream_stat($stream);
Loading history...
2619
            $streamsize = $mstreamstat["cb"];
2620
            if (isset($streamsize)) {
2621
                if (Request::GetProtocolVersion() >= 12.0) {
2622
                    if (!isset($message->asbody))
2623
                        $message->asbody = new SyncBaseBody();
2624
                    $message->asbody->data = MAPIStreamWrapper::Open($stream);
0 ignored issues
show
Bug introduced by
$stream of type resource is incompatible with the type mapistream expected by parameter $mapistream of MAPIStreamWrapper::Open(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2624
                    $message->asbody->data = MAPIStreamWrapper::Open(/** @scrutinizer ignore-type */ $stream);
Loading history...
2625
                    $message->asbody->estimatedDataSize = $streamsize;
2626
                    $message->asbody->truncated = 0;
2627
                }
2628
                else {
2629
                    $message->mimedata = MAPIStreamWrapper::Open($stream);
2630
                    $message->mimesize = $streamsize;
2631
                    $message->mimetruncated = 0;
2632
                }
2633
                unset($message->body, $message->bodytruncated);
2634
                return true;
2635
            }
2636
        }
2637
        ZLog::Write(LOGLEVEL_ERROR, "MAPIProvider->imtoinet(): got no stream or content from mapi_inetmapi_imtoinet()");
2638
2639
        return false;
2640
    }
2641
2642
    /**
2643
     * Sets the message body.
2644
     *
2645
     * @param MAPIMessage       $mapimessage
2646
     * @param ContentParameters $contentparameters
2647
     * @param SyncObject        $message
2648
     */
2649
    private function setMessageBody($mapimessage, $contentparameters, &$message) {
2650
        //get the available body preference types
2651
        $bpTypes = $contentparameters->GetBodyPreference();
2652
        if ($bpTypes !== false) {
2653
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("BodyPreference types: %s", implode(', ', $bpTypes)));
2654
            //do not send mime data if the client requests it
2655
            if (($contentparameters->GetMimeSupport() == SYNC_MIMESUPPORT_NEVER) && ($key = array_search(SYNC_BODYPREFERENCE_MIME, $bpTypes)!== false)) {
2656
                unset($bpTypes[$key]);
2657
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Remove mime body preference type because the device required no mime support. BodyPreference types: %s", implode(', ', $bpTypes)));
2658
            }
2659
            //get the best fitting preference type
2660
            $bpReturnType = Utils::GetBodyPreferenceBestMatch($bpTypes);
2661
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("GetBodyPreferenceBestMatch: %d", $bpReturnType));
2662
            $bpo = $contentparameters->BodyPreference($bpReturnType);
2663
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("bpo: truncation size:'%d', allornone:'%d', preview:'%d'", $bpo->GetTruncationSize(), $bpo->GetAllOrNone(), $bpo->GetPreview()));
0 ignored issues
show
Bug introduced by
The method GetAllOrNone() does not exist on BodyPreference. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2663
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("bpo: truncation size:'%d', allornone:'%d', preview:'%d'", $bpo->GetTruncationSize(), $bpo->/** @scrutinizer ignore-call */ GetAllOrNone(), $bpo->GetPreview()));
Loading history...
Bug introduced by
The method GetPreview() does not exist on BodyPreference. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2663
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("bpo: truncation size:'%d', allornone:'%d', preview:'%d'", $bpo->GetTruncationSize(), $bpo->GetAllOrNone(), $bpo->/** @scrutinizer ignore-call */ GetPreview()));
Loading history...
Bug introduced by
The method GetTruncationSize() does not exist on BodyPreference. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2663
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("bpo: truncation size:'%d', allornone:'%d', preview:'%d'", $bpo->/** @scrutinizer ignore-call */ GetTruncationSize(), $bpo->GetAllOrNone(), $bpo->GetPreview()));
Loading history...
2664
2665
            // Android Blackberry expects a full mime message for signed emails
2666
            // @see https://jira.z-hub.io/projects/ZP/issues/ZP-1154
2667
            // @TODO change this when refactoring
2668
            $props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2668
            $props = /** @scrutinizer ignore-call */ mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS));
Loading history...
2669
            if (isset($props[PR_MESSAGE_CLASS]) &&
2670
                    stripos($props[PR_MESSAGE_CLASS], 'IPM.Note.SMIME.MultipartSigned') !== false &&
2671
                    ($key = array_search(SYNC_BODYPREFERENCE_MIME, $bpTypes) !== false)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $key is dead and can be removed.
Loading history...
2672
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->setMessageBody(): enforcing SYNC_BODYPREFERENCE_MIME type for a signed message"));
2673
                $bpReturnType = SYNC_BODYPREFERENCE_MIME;
2674
            }
2675
2676
            $this->setMessageBodyForType($mapimessage, $bpReturnType, $message);
2677
            //only set the truncation size data if device set it in request
2678
            if (    $bpo->GetTruncationSize() != false &&
2679
                    $bpReturnType != SYNC_BODYPREFERENCE_MIME &&
2680
                    $message->asbody->estimatedDataSize > $bpo->GetTruncationSize()
2681
                ) {
2682
2683
                // Truncated plaintext requests are used on iOS for the preview in the email list. All images and links should be removed - see https://jira.z-hub.io/browse/ZP-1025
2684
                if ($bpReturnType == SYNC_BODYPREFERENCE_PLAIN) {
2685
                    ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->setMessageBody(): truncated plain-text body requested, stripping all links and images");
2686
                    // Get more data because of the filtering it's most probably going down in size. It's going to be truncated to the correct size below.
2687
                    $plainbody = stream_get_contents($message->asbody->data, $bpo->GetTruncationSize() * 5);
2688
                    $message->asbody->data = StringStreamWrapper::Open(preg_replace('/<http(s){0,1}:\/\/.*?>/i', '', $plainbody));
2689
                }
2690
2691
                // truncate data stream
2692
                ftruncate($message->asbody->data, $bpo->GetTruncationSize());
0 ignored issues
show
Bug introduced by
It seems like $bpo->GetTruncationSize() can also be of type true; however, parameter $size of ftruncate() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2692
                ftruncate($message->asbody->data, /** @scrutinizer ignore-type */ $bpo->GetTruncationSize());
Loading history...
2693
                $message->asbody->truncated = 1;
2694
            }
2695
            // set the preview or windows phones won't show the preview of an email
2696
            if (Request::GetProtocolVersion() >= 14.0 && $bpo->GetPreview()) {
2697
                $message->asbody->preview = Utils::Utf8_truncate(MAPIUtils::readPropStream($mapimessage, PR_BODY), $bpo->GetPreview());
0 ignored issues
show
Bug introduced by
It seems like $bpo->GetPreview() can also be of type true; however, parameter $length of Utils::Utf8_truncate() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2697
                $message->asbody->preview = Utils::Utf8_truncate(MAPIUtils::readPropStream($mapimessage, PR_BODY), /** @scrutinizer ignore-type */ $bpo->GetPreview());
Loading history...
2698
            }
2699
        }
2700
        else {
2701
            // Override 'body' for truncation
2702
            $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation());
0 ignored issues
show
Bug introduced by
The method GetTruncation() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2702
            $truncsize = Utils::GetTruncSize($contentparameters->/** @scrutinizer ignore-call */ GetTruncation());
Loading history...
2703
            $this->setMessageBodyForType($mapimessage, SYNC_BODYPREFERENCE_PLAIN, $message);
2704
2705
            if($message->bodysize > $truncsize) {
2706
                $message->body = Utils::Utf8_truncate($message->body, $truncsize);
2707
                $message->bodytruncated = 1;
2708
            }
2709
2710
            if (!isset($message->body) || strlen($message->body) == 0)
2711
                $message->body = " ";
2712
2713
            if ($contentparameters->GetMimeSupport() == SYNC_MIMESUPPORT_ALWAYS) {
2714
                //set the html body for iphone in AS 2.5 version
2715
                $this->imtoinet($mapimessage, $message);
2716
            }
2717
        }
2718
    }
2719
2720
    /**
2721
    * Sets properties for an email message.
2722
    *
2723
    * @param mixed             $mapimessage
2724
    * @param SyncMail          $message
2725
    *
2726
    * @access private
2727
    * @return void
2728
    */
2729
    private function setFlag($mapimessage, &$message){
2730
        // do nothing if protocol version is lower than 12.0 as flags haven't been defined before
2731
        if (Request::GetProtocolVersion() < 12.0 ) return;
2732
2733
        $message->flag = new SyncMailFlags();
2734
2735
        $this->getPropsFromMAPI($message->flag, $mapimessage, MAPIMapping::GetMailFlagsMapping());
2736
    }
2737
2738
    /**
2739
     * Sets information from SyncBaseBody type for a MAPI message.
2740
     *
2741
     * @param SyncBaseBody $asbody
2742
     * @param array $props
2743
     * @param array $appointmentprops
2744
     *
2745
     * @access private
2746
     * @return void
2747
     */
2748
    private function setASbody($asbody, &$props, $appointmentprops) {
2749
        // TODO: fix checking for the length
2750
        if (isset($asbody->type) && isset($asbody->data) /*&& strlen($asbody->data) > 0*/) {
2751
            switch ($asbody->type) {
2752
                case SYNC_BODYPREFERENCE_PLAIN:
2753
                default:
2754
                //set plain body if the type is not in valid range
2755
                    $props[$appointmentprops["body"]] = stream_get_contents($asbody->data);
2756
                    break;
2757
                case SYNC_BODYPREFERENCE_HTML:
2758
                    $props[$appointmentprops["html"]] = stream_get_contents($asbody->data);
2759
                    break;
2760
                case SYNC_BODYPREFERENCE_RTF:
2761
                    break;
2762
                case SYNC_BODYPREFERENCE_MIME:
2763
                    break;
2764
            }
2765
        }
2766
        else {
2767
            ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->setASbody either type or data are not set. Setting to empty body");
2768
            $props[$appointmentprops["body"]] = "";
2769
        }
2770
    }
2771
2772
    /**
2773
     * Get MAPI addressbook object.
2774
     *
2775
     * @access private
2776
     * @return MAPIAddressbook object to be used with mapi_ab_* or false on failure
0 ignored issues
show
Bug introduced by
The type MAPIAddressbook was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2777
     */
2778
    private function getAddressbook() {
2779
        if (isset($this->addressbook) && $this->addressbook) {
2780
            return $this->addressbook;
2781
        }
2782
        $this->addressbook = mapi_openaddressbook($this->session);
1 ignored issue
show
Bug introduced by
The function mapi_openaddressbook was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2782
        $this->addressbook = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
2783
        $result = mapi_last_hresult();
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2783
        $result = /** @scrutinizer ignore-call */ mapi_last_hresult();
Loading history...
2784
        if ($result && $this->addressbook === false) {
2785
            ZLog::Write(LOGLEVEL_ERROR, sprintf("MAPIProvider->getAddressbook error opening addressbook 0x%X", $result));
2786
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type MAPIAddressbook.
Loading history...
2787
        }
2788
        return $this->addressbook;
2789
    }
2790
2791
    /**
2792
     * Gets the required store properties.
2793
     *
2794
     * @access private
2795
     * @return array
2796
     */
2797
    public function GetStoreProps() {
2798
        if (!isset($this->storeProps) || empty($this->storeProps)) {
2799
            ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->GetStoreProps(): Getting store properties.");
2800
            $this->storeProps = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_ENTRYID, PR_IPM_PUBLIC_FOLDERS_ENTRYID, PR_IPM_FAVORITES_ENTRYID, PR_MAILBOX_OWNER_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2800
            $this->storeProps = /** @scrutinizer ignore-call */ mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_ENTRYID, PR_IPM_PUBLIC_FOLDERS_ENTRYID, PR_IPM_FAVORITES_ENTRYID, PR_MAILBOX_OWNER_ENTRYID));
Loading history...
2801
            // make sure all properties are set
2802
            if(!isset($this->storeProps[PR_IPM_WASTEBASKET_ENTRYID])) {
2803
                $this->storeProps[PR_IPM_WASTEBASKET_ENTRYID] = false;
2804
            }
2805
            if(!isset($this->storeProps[PR_IPM_SENTMAIL_ENTRYID])) {
2806
                $this->storeProps[PR_IPM_SENTMAIL_ENTRYID] = false;
2807
            }
2808
            if(!isset($this->storeProps[PR_IPM_OUTBOX_ENTRYID])) {
2809
                $this->storeProps[PR_IPM_OUTBOX_ENTRYID] = false;
2810
            }
2811
            if(!isset($this->storeProps[PR_IPM_PUBLIC_FOLDERS_ENTRYID])) {
2812
                $this->storeProps[PR_IPM_PUBLIC_FOLDERS_ENTRYID] = false;
2813
            }
2814
        }
2815
        return $this->storeProps;
2816
    }
2817
2818
    /**
2819
     * Gets the required inbox properties.
2820
     *
2821
     * @access public
2822
     * @return array
2823
     */
2824
    public function GetInboxProps() {
2825
        if (!isset($this->inboxProps) || empty($this->inboxProps)) {
2826
            ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->GetInboxProps(): Getting inbox properties.");
2827
            $this->inboxProps = array();
2828
            $inbox = mapi_msgstore_getreceivefolder($this->store);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_getreceivefolder was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2828
            $inbox = /** @scrutinizer ignore-call */ mapi_msgstore_getreceivefolder($this->store);
Loading history...
2829
            if ($inbox) {
2830
                $this->inboxProps = mapi_getprops($inbox, array(PR_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_TASK_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_JOURNAL_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2830
                $this->inboxProps = /** @scrutinizer ignore-call */ mapi_getprops($inbox, array(PR_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_TASK_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_JOURNAL_ENTRYID));
Loading history...
2831
                // make sure all properties are set
2832
                if(!isset($this->inboxProps[PR_ENTRYID])) {
2833
                    $this->inboxProps[PR_ENTRYID] = false;
2834
                }
2835
                if(!isset($this->inboxProps[PR_IPM_DRAFTS_ENTRYID])) {
2836
                    $this->inboxProps[PR_IPM_DRAFTS_ENTRYID] = false;
2837
                }
2838
                if(!isset($this->inboxProps[PR_IPM_TASK_ENTRYID])) {
2839
                    $this->inboxProps[PR_IPM_TASK_ENTRYID] = false;
2840
                }
2841
                if(!isset($this->inboxProps[PR_IPM_APPOINTMENT_ENTRYID])) {
2842
                    $this->inboxProps[PR_IPM_APPOINTMENT_ENTRYID] = false;
2843
                }
2844
                if(!isset($this->inboxProps[PR_IPM_CONTACT_ENTRYID])) {
2845
                    $this->inboxProps[PR_IPM_CONTACT_ENTRYID] = false;
2846
                }
2847
                if(!isset($this->inboxProps[PR_IPM_NOTE_ENTRYID])) {
2848
                    $this->inboxProps[PR_IPM_NOTE_ENTRYID] = false;
2849
                }
2850
                if(!isset($this->inboxProps[PR_IPM_JOURNAL_ENTRYID])) {
2851
                    $this->inboxProps[PR_IPM_JOURNAL_ENTRYID] = false;
2852
                }
2853
            }
2854
        }
2855
        return $this->inboxProps;
2856
    }
2857
2858
    /**
2859
     * Gets the required store root properties.
2860
     *
2861
     * @access private
2862
     * @return array
2863
     */
2864
    private function getRootProps() {
2865
        if (!isset($this->rootProps)) {
2866
            $root = mapi_msgstore_openentry($this->store, null);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2866
            $root = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, null);
Loading history...
2867
            $this->rootProps = mapi_getprops($root, array(PR_IPM_OL2007_ENTRYIDS));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2867
            $this->rootProps = /** @scrutinizer ignore-call */ mapi_getprops($root, array(PR_IPM_OL2007_ENTRYIDS));
Loading history...
2868
        }
2869
        return $this->rootProps;
2870
    }
2871
2872
    /**
2873
     * Returns an array with entryids of some special folders.
2874
     *
2875
     * @access private
2876
     * @return array
2877
     */
2878
    private function getSpecialFoldersData() {
2879
        // The persist data of an entry in PR_IPM_OL2007_ENTRYIDS consists of:
2880
        //      PersistId - e.g. RSF_PID_SUGGESTED_CONTACTS (2 bytes)
2881
        //      DataElementsSize - size of DataElements field (2 bytes)
2882
        //      DataElements - array of PersistElement structures (variable size)
2883
        //          PersistElement Structure consists of
2884
        //              ElementID - e.g. RSF_ELID_ENTRYID (2 bytes)
2885
        //              ElementDataSize - size of ElementData (2 bytes)
2886
        //              ElementData - The data for the special folder identified by the PersistID (variable size)
2887
        if (empty($this->specialFoldersData)) {
2888
            $this->specialFoldersData = array();
2889
            $rootProps = $this->getRootProps();
2890
            if (isset($rootProps[PR_IPM_OL2007_ENTRYIDS])) {
2891
                $persistData = $rootProps[PR_IPM_OL2007_ENTRYIDS];
2892
                while (strlen($persistData) > 0) {
2893
                    // PERSIST_SENTINEL marks the end of the persist data
2894
                    if (strlen($persistData) == 4 && $persistData == PERSIST_SENTINEL) {
2895
                        break;
2896
                    }
2897
                    $unpackedData = unpack("vdataSize/velementID/velDataSize", substr($persistData, 2, 6));
2898
                    if (isset($unpackedData['dataSize']) && isset($unpackedData['elementID']) && $unpackedData['elementID'] == RSF_ELID_ENTRYID && isset($unpackedData['elDataSize'])) {
2899
                        $this->specialFoldersData[] = substr($persistData, 8, $unpackedData['elDataSize']);
2900
                        // Add PersistId and DataElementsSize lengths to the data size as they're not part of it
2901
                        $persistData = substr($persistData, $unpackedData['dataSize'] + 4);
2902
                    }
2903
                    else {
2904
                        ZLog::Write(LOGLEVEL_INFO, "MAPIProvider->getSpecialFoldersData(): persistent data is not valid");
2905
                        break;
2906
                    }
2907
                }
2908
            }
2909
        }
2910
        return $this->specialFoldersData;
2911
    }
2912
2913
    /**
2914
     * Extracts email address from PR_SEARCH_KEY property if possible.
2915
     *
2916
     * @param string $searchKey
2917
     * @access private
2918
     * @see https://jira.z-hub.io/browse/ZP-1178
2919
     *
2920
     * @return string
2921
     */
2922
    private function getEmailAddressFromSearchKey($searchKey) {
2923
        if (strpos($searchKey, ':') !== false && strpos($searchKey, '@') !== false) {
2924
            ZLog::Write(LOGLEVEL_INFO, "MAPIProvider->getEmailAddressFromSearchKey(): fall back to PR_SEARCH_KEY or PR_SENT_REPRESENTING_SEARCH_KEY to resolve user and get email address");
2925
            return trim(strtolower(explode(':', $searchKey)[1]));
2926
        }
2927
        return "";
2928
    }
2929
2930
    /**
2931
     * Returns categories for a message.
2932
     *
2933
     * @param binary $parentsourcekey
2934
     * @param binary $sourcekey
2935
     *
2936
     * @access public
2937
     * @return array or false on failure
2938
     */
2939
    public function GetMessageCategories($parentsourcekey, $sourcekey) {
2940
        $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $parentsourcekey, $sourcekey);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2940
        $entryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, $parentsourcekey, $sourcekey);
Loading history...
2941
        if (!$entryid) {
2942
            ZLog::Write(LOGLEVEL_INFO, sprintf("MAPIProvider->GetMessageCategories(): Couldn't retrieve message, sourcekey: '%s', parentsourcekey: '%s'", bin2hex($sourcekey), bin2hex($parentsourcekey)));
2943
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
2944
        }
2945
        $mapimessage = mapi_msgstore_openentry($this->store, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2945
        $mapimessage = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $entryid);
Loading history...
2946
        $emailMapping = MAPIMapping::GetEmailMapping();
2947
        $emailMapping = array("categories" => $emailMapping["categories"]);
2948
        $messageCategories = $this->getProps($mapimessage, $emailMapping);
2949
        if (isset($messageCategories[$emailMapping["categories"]]) && is_array($messageCategories[$emailMapping["categories"]])) {
2950
            return $messageCategories[$emailMapping["categories"]];
2951
        }
2952
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
2953
    }
2954
}
2955