source: trunk/zpush/backend/zarafa/mapiutils.php @ 7589

Revision 7589, 19.0 KB checked in by douglas, 11 years ago (diff)

Ticket #3209 - Integrar módulo de sincronização Z-push ao Expresso

Line 
1<?php
2/***********************************************
3* File      :   mapiutils.php
4* Project   :   Z-Push
5* Descr     :
6*
7* Created   :   14.02.2011
8*
9* Copyright 2007 - 2012 Zarafa Deutschland GmbH
10*
11* This program is free software: you can redistribute it and/or modify
12* it under the terms of the GNU Affero General Public License, version 3,
13* as published by the Free Software Foundation with the following additional
14* term according to sec. 7:
15*
16* According to sec. 7 of the GNU Affero General Public License, version 3,
17* the terms of the AGPL are supplemented with the following terms:
18*
19* "Zarafa" is a registered trademark of Zarafa B.V.
20* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
21* The licensing of the Program under the AGPL does not imply a trademark license.
22* Therefore any rights, title and interest in our trademarks remain entirely with us.
23*
24* However, if you propagate an unmodified version of the Program you are
25* allowed to use the term "Z-Push" to indicate that you distribute the Program.
26* Furthermore you may use our trademarks where it is necessary to indicate
27* the intended purpose of a product or service provided you use it in accordance
28* with honest practices in industrial or commercial matters.
29* If you want to propagate modified versions of the Program under the name "Z-Push",
30* you may only do so if you have a written permission by Zarafa Deutschland GmbH
31* (to acquire a permission please contact Zarafa at trademark@zarafa.com).
32*
33* This program is distributed in the hope that it will be useful,
34* but WITHOUT ANY WARRANTY; without even the implied warranty of
35* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36* GNU Affero General Public License for more details.
37*
38* You should have received a copy of the GNU Affero General Public License
39* along with this program.  If not, see <http://www.gnu.org/licenses/>.
40*
41* Consult LICENSE file for details
42************************************************/
43
44/**
45 *
46 * MAPI to AS mapping class
47 *
48 *
49 */
50class MAPIUtils {
51
52    /**
53     * Create a MAPI restriction to use within an email folder which will
54     * return all messages since since $timestamp
55     *
56     * @param long       $timestamp     Timestamp since when to include messages
57     *
58     * @access public
59     * @return array
60     */
61    public static function GetEmailRestriction($timestamp) {
62        // ATTENTION: ON CHANGING THIS RESTRICTION, MAPIUtils::IsInEmailSyncInterval() also needs to be changed
63        $restriction = array ( RES_PROPERTY,
64                          array (   RELOP => RELOP_GE,
65                                    ULPROPTAG => PR_MESSAGE_DELIVERY_TIME,
66                                    VALUE => $timestamp
67                          )
68                      );
69
70        return $restriction;
71    }
72
73
74    /**
75     * Create a MAPI restriction to use in the calendar which will
76     * return all future calendar items, plus those since $timestamp
77     *
78     * @param MAPIStore  $store         the MAPI store
79     * @param long       $timestamp     Timestamp since when to include messages
80     *
81     * @access public
82     * @return array
83     */
84    //TODO getting named properties
85    public static function GetCalendarRestriction($store, $timestamp) {
86        // This is our viewing window
87        $start = $timestamp;
88        $end = 0x7fffffff; // infinite end
89
90        $props = MAPIMapping::GetAppointmentProperties();
91        $props = getPropIdsFromStrings($store, $props);
92
93        // ATTENTION: ON CHANGING THIS RESTRICTION, MAPIUtils::IsInCalendarSyncInterval() also needs to be changed
94        $restriction = Array(RES_OR,
95             Array(
96                   // OR
97                   // item.end > window.start && item.start < window.end
98                   Array(RES_AND,
99                         Array(
100                               Array(RES_PROPERTY,
101                                     Array(RELOP => RELOP_LE,
102                                           ULPROPTAG => $props["starttime"],
103                                           VALUE => $end
104                                           )
105                                     ),
106                               Array(RES_PROPERTY,
107                                     Array(RELOP => RELOP_GE,
108                                           ULPROPTAG => $props["endtime"],
109                                           VALUE => $start
110                                           )
111                                     )
112                               )
113                         ),
114                   // OR
115                   Array(RES_OR,
116                         Array(
117                               // OR
118                               // (EXIST(recurrence_enddate_property) && item[isRecurring] == true && recurrence_enddate_property >= start)
119                               Array(RES_AND,
120                                     Array(
121                                           Array(RES_EXIST,
122                                                 Array(ULPROPTAG => $props["recurrenceend"],
123                                                       )
124                                                 ),
125                                           Array(RES_PROPERTY,
126                                                 Array(RELOP => RELOP_EQ,
127                                                       ULPROPTAG => $props["isrecurring"],
128                                                       VALUE => true
129                                                       )
130                                                 ),
131                                           Array(RES_PROPERTY,
132                                                 Array(RELOP => RELOP_GE,
133                                                       ULPROPTAG => $props["recurrenceend"],
134                                                       VALUE => $start
135                                                       )
136                                                 )
137                                           )
138                                     ),
139                               // OR
140                               // (!EXIST(recurrence_enddate_property) && item[isRecurring] == true && item[start] <= end)
141                               Array(RES_AND,
142                                     Array(
143                                           Array(RES_NOT,
144                                                 Array(
145                                                       Array(RES_EXIST,
146                                                             Array(ULPROPTAG => $props["recurrenceend"]
147                                                                   )
148                                                             )
149                                                       )
150                                                 ),
151                                           Array(RES_PROPERTY,
152                                                 Array(RELOP => RELOP_LE,
153                                                       ULPROPTAG => $props["starttime"],
154                                                       VALUE => $end
155                                                       )
156                                                 ),
157                                           Array(RES_PROPERTY,
158                                                 Array(RELOP => RELOP_EQ,
159                                                       ULPROPTAG => $props["isrecurring"],
160                                                       VALUE => true
161                                                       )
162                                                 )
163                                           )
164                                     )
165                               )
166                         ) // EXISTS OR
167                   )
168             );        // global OR
169
170        return $restriction;
171    }
172
173
174    /**
175     * Create a MAPI restriction in order to check if a contact has a picture
176     *
177     * @access public
178     * @return array
179     */
180    public static function GetContactPicRestriction() {
181        return array ( RES_PROPERTY,
182                        array (
183                            RELOP => RELOP_EQ,
184                            ULPROPTAG => mapi_prop_tag(PT_BOOLEAN, 0x7FFF),
185                            VALUE => true
186                        )
187        );
188    }
189
190
191    /**
192     * Create a MAPI restriction for search
193     *
194     * @access public
195     *
196     * @param string $query
197     * @return array
198     */
199    public static function GetSearchRestriction($query) {
200        return array(RES_AND,
201                    array(
202                        array(RES_OR,
203                            array(
204                                array(RES_CONTENT, array(FUZZYLEVEL => FL_SUBSTRING | FL_IGNORECASE, ULPROPTAG => PR_DISPLAY_NAME, VALUE => $query)),
205                                array(RES_CONTENT, array(FUZZYLEVEL => FL_SUBSTRING | FL_IGNORECASE, ULPROPTAG => PR_ACCOUNT, VALUE => $query)),
206                            ), // RES_OR
207                        ),
208                        array(
209                            RES_PROPERTY,
210                            array(RELOP => RELOP_EQ, ULPROPTAG => PR_OBJECT_TYPE, VALUE => MAPI_MAILUSER)
211                        )
212                    ) // RES_AND
213        );
214    }
215
216    /**
217     * Checks if mapimessage is inside the synchronization interval
218     * also defined by MAPIUtils::GetEmailRestriction()
219     *
220     * @param MAPIStore       $store           mapi store
221     * @param MAPIMessage     $mapimessage     the mapi message to be checked
222     * @param long            $timestamp       the lower time limit
223     *
224     * @access public
225     * @return boolean
226     */
227    public static function IsInEmailSyncInterval($store, $mapimessage, $timestamp) {
228        $p = mapi_getprops($mapimessage, array(PR_MESSAGE_DELIVERY_TIME));
229
230        if (isset($p[PR_MESSAGE_DELIVERY_TIME]) && $p[PR_MESSAGE_DELIVERY_TIME] >= $timestamp) {
231            ZLog::Write(LOGLEVEL_DEBUG, "MAPIUtils->IsInEmailSyncInterval: Message is in the synchronization interval");
232            return true;
233        }
234
235        ZLog::Write(LOGLEVEL_WARN, "MAPIUtils->IsInEmailSyncInterval: Message is OUTSIDE the synchronization interval");
236        return false;
237    }
238
239    /**
240     * Checks if mapimessage is inside the synchronization interval
241     * also defined by MAPIUtils::GetCalendarRestriction()
242     *
243     * @param MAPIStore       $store           mapi store
244     * @param MAPIMessage     $mapimessage     the mapi message to be checked
245     * @param long            $timestamp       the lower time limit
246     *
247     * @access public
248     * @return boolean
249     */
250    public static function IsInCalendarSyncInterval($store, $mapimessage, $timestamp) {
251        // This is our viewing window
252        $start = $timestamp;
253        $end = 0x7fffffff; // infinite end
254
255        $props = MAPIMapping::GetAppointmentProperties();
256        $props = getPropIdsFromStrings($store, $props);
257
258        $p = mapi_getprops($mapimessage, array($props["starttime"], $props["endtime"], $props["recurrenceend"], $props["isrecurring"], $props["recurrenceend"]));
259
260        if (
261                (
262                    isset($p[$props["endtime"]]) && isset($p[$props["starttime"]]) &&
263
264                    //item.end > window.start && item.start < window.end
265                    $p[$props["endtime"]] > $start && $p[$props["starttime"]] < $end
266                )
267            ||
268                (
269                    isset($p[$props["isrecurring"]]) &&
270
271                    //(EXIST(recurrence_enddate_property) && item[isRecurring] == true && recurrence_enddate_property >= start)
272                    isset($p[$props["recurrenceend"]]) && $p[$props["isrecurring"]] == true && $p[$props["recurrenceend"]] >= $start
273                )
274            ||
275                (
276                    isset($p[$props["isrecurring"]]) && isset($p[$props["starttime"]]) &&
277
278                    //(!EXIST(recurrence_enddate_property) && item[isRecurring] == true && item[start] <= end)
279                    !isset($p[$props["recurrenceend"]]) && $p[$props["isrecurring"]] == true && $p[$props["starttime"]] <= $end
280                )
281           ) {
282            ZLog::Write(LOGLEVEL_DEBUG, "MAPIUtils->IsInCalendarSyncInterval: Message is in the synchronization interval");
283            return true;
284        }
285
286
287        ZLog::Write(LOGLEVEL_WARN, "MAPIUtils->IsInCalendarSyncInterval: Message is OUTSIDE the synchronization interval");
288        return false;
289    }
290
291
292    /**
293     * Handles recurring item for meeting request coming from tnef
294     *
295     * @param array $mapiprops
296     * @param array $props
297     *
298     * @access public
299     * @return
300     */
301    public static function handleRecurringItem(&$mapiprops, &$props) {
302        $mapiprops[$props["isrecurringtag"]] = true;
303        $mapiprops[$props["sideeffects"]] = 369;
304        //both goids have the same value
305        $mapiprops[$props["goid2tag"]] = $mapiprops[$props["goidtag"]];
306        $mapiprops[$props["type"]] = "IPM.Appointment";
307        $mapiprops[$props["busystatus"]] = 1; //tentative
308        $mapiprops[PR_RESPONSE_REQUESTED] = true;
309        $mapiprops[PR_ICON_INDEX] = 1027;
310        $mapiprops[$props["meetingstatus"]] = olMeetingReceived; // The recipient is receiving the request
311        $mapiprops[$props["responsestatus"]] = olResponseNotResponded;
312        $mapiprops[$props["usetnef"]] = true;
313    }
314
315
316    /**
317     * Reads data of large properties from a stream
318     *
319     * @param MAPIMessage $message
320     * @param long $prop
321     *
322     * @access public
323     * @return string
324     */
325    public static function readPropStream($message, $prop) {
326        $stream = mapi_openproperty($message, $prop, IID_IStream, 0, 0);
327        $ret = mapi_last_hresult();
328        if ($ret == MAPI_E_NOT_FOUND) {
329            ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIUtils->readPropStream: property 0x%s not found. It is either empty or not set. It will be ignored.", str_pad(dechex($prop), 8, 0, STR_PAD_LEFT)));
330            return "";
331        }
332        elseif ($ret) {
333            ZLog::Write(LOGLEVEL_ERROR, "MAPIUtils->readPropStream error opening stream: 0X%X", $ret);
334            return "";
335        }
336        $data = "";
337        $string = "";
338        while(1) {
339            $data = mapi_stream_read($stream, 1024);
340            if(strlen($data) == 0)
341                break;
342            $string .= $data;
343        }
344
345        return $string;
346    }
347
348
349    /**
350     * Checks if a store supports properties containing unicode characters
351     *
352     * @param MAPIStore $store
353     *
354     * @access public
355     * @return
356     */
357    public static function IsUnicodeStore($store) {
358        $supportmask = mapi_getprops($store, array(PR_STORE_SUPPORT_MASK));
359        if (isset($supportmask[PR_STORE_SUPPORT_MASK]) && ($supportmask[PR_STORE_SUPPORT_MASK] & STORE_UNICODE_OK)) {
360            ZLog::Write(LOGLEVEL_DEBUG, "Store supports properties containing Unicode characters.");
361            define('STORE_SUPPORTS_UNICODE', true);
362            //setlocale to UTF-8 in order to support properties containing Unicode characters
363            setlocale(LC_CTYPE, "en_US.UTF-8");
364            define('STORE_INTERNET_CPID', INTERNET_CPID_UTF8);
365        }
366    }
367
368    /**
369     * Gets attachment from a Mail_mimeDecode parsed email and stores it into MAPI
370     *
371     * @param mixed     $mapimessage        target message
372     * @param object    $part               Mail_mimeDecode part to be stored
373     *
374     * @access public
375     * @return boolean
376     */
377    public static function StoreAttachment($mapimessage, $part) {
378        // attachment
379        $attach = mapi_message_createattach($mapimessage);
380
381        $filename = "";
382        // Filename is present in both Content-Type: name=.. and in Content-Disposition: filename=
383        if(isset($part->ctype_parameters["name"]))
384            $filename = $part->ctype_parameters["name"];
385        else if(isset($part->d_parameters["name"]))
386            $filename = $part->d_parameters["filename"];
387        else if (isset($part->d_parameters["filename"])) // sending appointment with nokia & android only filename is set
388            $filename = $part->d_parameters["filename"];
389        // filenames with more than 63 chars as splitted several strings
390        else if (isset($part->d_parameters["filename*0"])) {
391            for ($i=0; $i< count($part->d_parameters); $i++)
392               if (isset($part->d_parameters["filename*".$i]))
393                   $filename .= $part->d_parameters["filename*".$i];
394        }
395        else
396            $filename = "untitled";
397
398        // Android just doesn't send content-type, so mimeDecode doesn't performs base64 decoding
399        // on meeting requests text/calendar somewhere inside content-transfer-encoding
400        if (isset($part->headers['content-transfer-encoding']) && strpos($part->headers['content-transfer-encoding'], 'base64')) {
401            if (strpos($part->headers['content-transfer-encoding'], 'text/calendar') !== false) {
402                $part->ctype_primary = 'text';
403                $part->ctype_secondary = 'calendar';
404            }
405            if (!isset($part->headers['content-type']))
406                $part->body = base64_decode($part->body);
407        }
408
409        mapi_setprops($attach, array(
410            // Set filename and attachment type
411            PR_ATTACH_LONG_FILENAME => u2wi($filename),
412            PR_ATTACH_METHOD => ATTACH_BY_VALUE,
413            // Set attachment data
414            PR_ATTACH_DATA_BIN => $part->body,
415            // Set MIME type
416            PR_ATTACH_MIME_TAG => $part->ctype_primary . "/" . $part->ctype_secondary));
417
418        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Utils::StoreAttachment: Attachment '%s' with %d bytes saved", $filename, strlen($part->body)));
419        return mapi_savechanges($attach);
420    }
421
422    /**
423     * Returns the MAPI PR_CONTAINER_CLASS string for an ActiveSync Foldertype
424     *
425     * @param int       $foldertype
426     *
427     * @access public
428     * @return string
429     */
430    public static function GetContainerClassFromFolderType($foldertype) {
431        switch ($foldertype) {
432            case SYNC_FOLDER_TYPE_TASK:
433            case SYNC_FOLDER_TYPE_USER_TASK:
434                return "IPF.Task";
435                break;
436
437            case SYNC_FOLDER_TYPE_APPOINTMENT:
438            case SYNC_FOLDER_TYPE_USER_APPOINTMENT:
439                return "IPF.Appointment";
440                break;
441
442            case SYNC_FOLDER_TYPE_CONTACT:
443            case SYNC_FOLDER_TYPE_USER_CONTACT:
444                return "IPF.Contact";
445                break;
446
447            case SYNC_FOLDER_TYPE_NOTE:
448            case SYNC_FOLDER_TYPE_USER_NOTE:
449                return "IPF.StickyNote";
450                break;
451
452            case SYNC_FOLDER_TYPE_JOURNAL:
453            case SYNC_FOLDER_TYPE_USER_JOURNAL:
454                return "IPF.Journal";
455                break;
456
457            case SYNC_FOLDER_TYPE_INBOX:
458            case SYNC_FOLDER_TYPE_DRAFTS:
459            case SYNC_FOLDER_TYPE_WASTEBASKET:
460            case SYNC_FOLDER_TYPE_SENTMAIL:
461            case SYNC_FOLDER_TYPE_OUTBOX:
462            case SYNC_FOLDER_TYPE_USER_MAIL:
463            case SYNC_FOLDER_TYPE_OTHER:
464            case SYNC_FOLDER_TYPE_UNKNOWN:
465            default:
466                return "IPF.Note";
467                break;
468        }
469    }
470
471}
472
473?>
Note: See TracBrowser for help on using the repository browser.