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

MAPIProvider::setAddress()   B

Complexity

Conditions 7
Paths 64

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 7
nc 64
nop 8
dl 0
loc 14
rs 8.8333
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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